Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / jitconfig.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 #include "jitpch.h"
6 #ifdef _MSC_VER
7 #pragma hdrstop
8 #endif
9
10 #include "jitconfig.h"
11
12 JitConfigValues JitConfig;
13
14 void JitConfigValues::MethodSet::initialize(const wchar_t* list, ICorJitHost* host)
15 {
16     assert(m_list == nullptr);
17     assert(m_names == nullptr);
18
19     enum State
20     {
21         NO_NAME,
22         CLS_NAME,
23         FUNC_NAME,
24         ARG_LIST
25     }; // parsing state machine
26
27     const char SEP_CHAR = ' '; // current character use to separate each entry
28
29     wchar_t      lastChar  = '?';     // dummy
30     int          nameStart = -1;      // Index of the start of the current class or method name
31     MethodName   currentName;         // Buffer used while parsing the current entry
32     MethodName** lastName = &m_names; // Last entry inserted into the list
33     bool         isQuoted = false;
34
35     currentName.m_next            = nullptr;
36     currentName.m_methodNameStart = -1;
37     currentName.m_methodNameLen   = -1;
38     currentName.m_classNameStart  = -1;
39     currentName.m_classNameLen    = -1;
40     currentName.m_numArgs         = -1;
41
42     // Convert the input list to UTF-8
43     int utf8ListLen = WszWideCharToMultiByte(CP_UTF8, 0, list, -1, nullptr, 0, nullptr, nullptr);
44     m_list          = (char*)host->allocateMemory(utf8ListLen);
45     if (WszWideCharToMultiByte(CP_UTF8, 0, list, -1, const_cast<LPSTR>(m_list), utf8ListLen, nullptr, nullptr) == 0)
46     {
47         // Failed to convert the list. Free the memory and ignore the list.
48         host->freeMemory(reinterpret_cast<void*>(const_cast<char*>(m_list)));
49         m_list = nullptr;
50         return;
51     }
52
53     State state = NO_NAME;
54     for (int i = 0; lastChar != '\0'; i++)
55     {
56         lastChar = m_list[i];
57
58         switch (state)
59         {
60             case NO_NAME:
61                 if (m_list[i] != SEP_CHAR)
62                 {
63                     nameStart = i;
64                     state     = CLS_NAME; // we have found the start of the next entry
65                 }
66                 break;
67
68             case CLS_NAME:
69                 if (m_list[nameStart] == '"')
70                 {
71                     for (; m_list[i] != '\0' && m_list[i] != '"'; i++)
72                     {
73                         ;
74                     }
75
76                     nameStart++;
77                     isQuoted = true;
78                 }
79
80                 if (m_list[i] == ':')
81                 {
82                     if (m_list[nameStart] == '*' && !isQuoted)
83                     {
84                         // The class name is a wildcard; mark it invalid.
85                         currentName.m_classNameStart = -1;
86                         currentName.m_classNameLen   = -1;
87                     }
88                     else
89                     {
90                         currentName.m_classNameStart = nameStart;
91                         currentName.m_classNameLen   = i - nameStart;
92
93                         // Remove the trailing quote, if any
94                         if (isQuoted)
95                         {
96                             currentName.m_classNameLen--;
97                             isQuoted = false;
98                         }
99                     }
100
101                     // Accept class::name syntax as well
102                     if (m_list[i + 1] == ':')
103                     {
104                         i++;
105                     }
106
107                     nameStart = i + 1;
108                     state     = FUNC_NAME;
109                 }
110                 else if (m_list[i] == '\0' || m_list[i] == SEP_CHAR || m_list[i] == '(')
111                 {
112                     // Treat this as a method name without a class name.
113                     currentName.m_classNameStart = -1;
114                     currentName.m_classNameLen   = -1;
115                     goto DONE_FUNC_NAME;
116                 }
117                 break;
118
119             case FUNC_NAME:
120                 if (m_list[nameStart] == '"')
121                 {
122                     // The first half of the outer contdition handles the case where the
123                     // class name is valid.
124                     for (; nameStart == i || (m_list[i] != '\0' && m_list[i] != '"'); i++)
125                     {
126                         ;
127                     }
128
129                     nameStart++;
130                     isQuoted = true;
131                 }
132
133                 if (m_list[i] == '\0' || m_list[i] == SEP_CHAR || m_list[i] == '(')
134                 {
135                 DONE_FUNC_NAME:
136                     assert(m_list[i] == '\0' || m_list[i] == SEP_CHAR || m_list[i] == '(');
137
138                     if (m_list[nameStart] == '*' && !isQuoted)
139                     {
140                         // The method name is a wildcard; mark it invalid.
141                         currentName.m_methodNameStart = -1;
142                         currentName.m_methodNameLen   = -1;
143                     }
144                     else
145                     {
146                         currentName.m_methodNameStart = nameStart;
147                         currentName.m_methodNameLen   = i - nameStart;
148
149                         // Remove the trailing quote, if any
150                         if (isQuoted)
151                         {
152                             currentName.m_classNameLen--;
153                             isQuoted = false;
154                         }
155                     }
156
157                     if (m_list[i] == '\0' || m_list[i] == SEP_CHAR)
158                     {
159                         currentName.m_numArgs = -1;
160                         goto DONE_ARG_LIST;
161                     }
162                     else
163                     {
164                         assert(m_list[i] == '(');
165                         currentName.m_numArgs = -1;
166                         state                 = ARG_LIST;
167                     }
168                 }
169                 break;
170
171             case ARG_LIST:
172                 if (m_list[i] == '\0' || m_list[i] == ')')
173                 {
174                     if (currentName.m_numArgs == -1)
175                     {
176                         currentName.m_numArgs = 0;
177                     }
178
179                 DONE_ARG_LIST:
180                     assert(m_list[i] == '\0' || m_list[i] == SEP_CHAR || m_list[i] == ')');
181
182                     // We have parsed an entire method name; create a new entry in the list for it.
183                     MethodName* name = (MethodName*)host->allocateMemory(sizeof(MethodName));
184                     *name            = currentName;
185
186                     assert(name->m_next == nullptr);
187                     *lastName = name;
188                     lastName  = &name->m_next;
189
190                     state = NO_NAME;
191
192                     // Skip anything after the argument list until we find the next
193                     // separator character. Otherwise if we see "func(a,b):foo" we
194                     // create entries for "func(a,b)" as well as ":foo".
195                     if (m_list[i] == ')')
196                     {
197                         for (; m_list[i] && m_list[i] != SEP_CHAR; i++)
198                         {
199                             ;
200                         }
201
202                         lastChar = m_list[i];
203                     }
204                 }
205                 else
206                 {
207                     if (m_list[i] != SEP_CHAR && currentName.m_numArgs == -1)
208                     {
209                         currentName.m_numArgs = 1;
210                     }
211
212                     if (m_list[i] == ',')
213                     {
214                         currentName.m_numArgs++;
215                     }
216                 }
217                 break;
218
219             default:
220                 assert(!"Bad state");
221                 break;
222         }
223     }
224 }
225
226 void JitConfigValues::MethodSet::destroy(ICorJitHost* host)
227 {
228     // Free method names, free the list string, and reset our state
229     for (MethodName *name = m_names, *next = nullptr; name != nullptr; name = next)
230     {
231         next = name->m_next;
232         host->freeMemory(reinterpret_cast<void*>(const_cast<MethodName*>(name)));
233     }
234     if (m_list != nullptr)
235     {
236         host->freeMemory(reinterpret_cast<void*>(const_cast<char*>(m_list)));
237         m_list = nullptr;
238     }
239     m_names = nullptr;
240 }
241
242 static bool matchesName(const char* const name, int nameLen, const char* const s2)
243 {
244     return strncmp(name, s2, nameLen) == 0 && s2[nameLen] == '\0';
245 }
246
247 bool JitConfigValues::MethodSet::contains(const char*       methodName,
248                                           const char*       className,
249                                           CORINFO_SIG_INFO* sigInfo) const
250 {
251     int numArgs = sigInfo != nullptr ? sigInfo->numArgs : -1;
252
253     // Try to match any the entries in the list.
254     for (MethodName* name = m_names; name != nullptr; name = name->m_next)
255     {
256         // If m_numArgs is valid, check for a mismatch
257         if (name->m_numArgs != -1 && name->m_numArgs != numArgs)
258         {
259             continue;
260         }
261
262         // If m_methodNameStart is valid, check for a mismatch
263         if (name->m_methodNameStart != -1)
264         {
265             const char* expectedMethodName = &m_list[name->m_methodNameStart];
266             if (!matchesName(expectedMethodName, name->m_methodNameLen, methodName))
267             {
268                 // C++ embeds the class name into the method name; deal with that here.
269                 const char* colon = strchr(methodName, ':');
270                 if (colon != nullptr && colon[1] == ':' &&
271                     matchesName(expectedMethodName, name->m_methodNameLen, methodName))
272                 {
273                     int classLen = (int)(colon - methodName);
274                     if (name->m_classNameStart == -1 ||
275                         (classLen == name->m_classNameLen &&
276                          strncmp(&m_list[name->m_classNameStart], methodName, classLen) == 0))
277                     {
278                         return true;
279                     }
280                 }
281                 continue;
282             }
283         }
284
285         // If m_classNameStart is valid, check for a mismatch
286         if (className == nullptr || name->m_classNameStart == -1 ||
287             matchesName(&m_list[name->m_classNameStart], name->m_classNameLen, className))
288         {
289             return true;
290         }
291
292         // Check for suffix wildcard like System.*
293         if (name->m_classNameLen > 0 && m_list[name->m_classNameStart + name->m_classNameLen - 1] == '*' &&
294             strncmp(&m_list[name->m_classNameStart], className, name->m_classNameLen - 1) == 0)
295         {
296             return true;
297         }
298
299 #ifdef _DEBUG
300         // Maybe className doesn't include the namespace. Try to match that
301         const char* nsSep = strrchr(className, '.');
302         if (nsSep != nullptr && nsSep != className)
303         {
304             const char* onlyClass = nsSep[-1] == '.' ? nsSep : &nsSep[1];
305             if (matchesName(&m_list[name->m_classNameStart], name->m_classNameLen, onlyClass))
306             {
307                 return true;
308             }
309         }
310 #endif
311     }
312
313     return false;
314 }
315
316 void JitConfigValues::initialize(ICorJitHost* host)
317 {
318     assert(!m_isInitialized);
319
320 #define CONFIG_INTEGER(name, key, defaultValue) m_##name = host->getIntConfigValue(key, defaultValue);
321 #define CONFIG_STRING(name, key) m_##name = host->getStringConfigValue(key);
322 #define CONFIG_METHODSET(name, key)                                                                                    \
323     const wchar_t* name##value = host->getStringConfigValue(key);                                                      \
324     m_##name.initialize(name##value, host);                                                                            \
325     host->freeStringConfigValue(name##value);
326
327 #include "jitconfigvalues.h"
328
329     m_isInitialized = true;
330 }
331
332 void JitConfigValues::destroy(ICorJitHost* host)
333 {
334     if (!m_isInitialized)
335     {
336         return;
337     }
338
339 #define CONFIG_INTEGER(name, key, defaultValue)
340 #define CONFIG_STRING(name, key) host->freeStringConfigValue(m_##name);
341 #define CONFIG_METHODSET(name, key) m_##name.destroy(host);
342
343 #include "jitconfigvalues.h"
344
345     m_isInitialized = false;
346 }