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
12JitConfigValues JitConfig;
13
14void JitConfigValues::MethodSet::initialize(const wchar_t* list, ICorJitHost* host)
15{
16 assert(m_list == nullptr);
17 assert(m_names == nullptr);
18
19 // Convert the input list to UTF-8
20 int utf8ListLen = WszWideCharToMultiByte(CP_UTF8, 0, list, -1, nullptr, 0, nullptr, nullptr);
21 if (utf8ListLen == 0)
22 {
23 return;
24 }
25 else
26 {
27 // char* m_list;
28 //
29 m_list = static_cast<char*>(host->allocateMemory(utf8ListLen));
30 if (WszWideCharToMultiByte(CP_UTF8, 0, list, -1, static_cast<LPSTR>(m_list), utf8ListLen, nullptr, nullptr) ==
31 0)
32 {
33 // Failed to convert the list. Free the memory and ignore the list.
34 host->freeMemory(static_cast<void*>(m_list));
35 m_list = nullptr;
36 return;
37 }
38 }
39
40 const char SEP_CHAR = ' '; // character used to separate each entry
41 const char WILD_CHAR = '*'; // character used as the wildcard match everything
42 char currChar = '?'; // The current character
43 int nameStart = -1; // Index of the start of the current class or method name
44 MethodName** lastName = &m_names; // Last entry inserted into the list
45 bool isQuoted = false; // true while parsing inside a quote "this-is-a-quoted-region"
46 MethodName currentName; // Buffer used while parsing the current entry
47
48 currentName.m_next = nullptr;
49 currentName.m_methodNameStart = -1;
50 currentName.m_methodNameLen = -1;
51 currentName.m_methodNameWildcardAtEnd = false;
52 currentName.m_classNameStart = -1;
53 currentName.m_classNameLen = -1;
54 currentName.m_classNameWildcardAtEnd = false;
55 currentName.m_numArgs = -1;
56
57 enum State
58 {
59 NO_NAME,
60 CLS_NAME,
61 FUNC_NAME,
62 ARG_LIST
63 }; // parsing state machine
64
65 State state = NO_NAME;
66 for (int i = 0; (currChar != '\0'); i++)
67 {
68 currChar = m_list[i];
69
70 switch (state)
71 {
72 case NO_NAME:
73 // skip over zero or more blanks, then expect CLS_NAME
74 if (currChar != SEP_CHAR)
75 {
76 nameStart = i;
77 state = CLS_NAME; // we have found the start of the next entry
78 }
79 break;
80
81 case CLS_NAME:
82 // Check for a quoted Class Name: (i.e. "MyClass")
83 if (m_list[nameStart] == '"')
84 {
85 // Advance until we see the second "
86 //
87 for (; (currChar != '\0'); i++)
88 {
89 currChar = m_list[i];
90 // Advance until we see the second "
91 if (currChar == '"')
92 {
93 break;
94 }
95 // or until we see the end of string
96 if (currChar == '\0')
97 {
98 break;
99 }
100 }
101
102 // skip the initial "
103 nameStart++;
104 isQuoted = true;
105 }
106
107 // A colon denotes the end of the Class name and the start of the Method name
108 if (currChar == ':')
109 {
110 // Record the class name
111 currentName.m_classNameStart = nameStart;
112 currentName.m_classNameLen = i - nameStart;
113
114 // Also accept the double colon syntax as well (i.e class::method)
115 //
116 if (m_list[i + 1] == ':')
117 {
118 i++;
119 }
120
121 if (isQuoted)
122 {
123 // Remove the trailing "
124 currentName.m_classNameLen--;
125 isQuoted = false;
126 }
127
128 // Is the first character a wildcard?
129 if (m_list[currentName.m_classNameStart] == WILD_CHAR)
130 {
131 // The class name is a full wildcard; mark it as such.
132 currentName.m_classNameStart = -1;
133 currentName.m_classNameLen = -1;
134 }
135 // Is there a wildcard at the end of the class name?
136 //
137 else if (m_list[currentName.m_classNameStart + currentName.m_classNameLen - 1] == WILD_CHAR)
138 {
139 // i.e. bar*:method, will match any class that starts with "bar"
140
141 // Remove the trailing WILD_CHAR from class name
142 currentName.m_classNameWildcardAtEnd = true;
143 currentName.m_classNameLen--; // backup for WILD_CHAR
144 }
145
146 // The method name will start at the next character
147 nameStart = i + 1;
148
149 // Now expect FUNC_NAME
150 state = FUNC_NAME;
151 }
152 else if ((currChar == '\0') || (currChar == SEP_CHAR) || (currChar == '('))
153 {
154 // Treat this as a method name without a class name.
155 currentName.m_classNameStart = -1;
156 currentName.m_classNameLen = -1;
157 goto DONE_FUNC_NAME;
158 }
159 break;
160
161 case FUNC_NAME:
162 // Check for a quoted method name: i.e. className:"MyFunc"
163 //
164 // Note that we may have already parsed a quoted string above in CLS_NAME, i.e. "Func":
165 if (!isQuoted && (m_list[nameStart] == '"'))
166 {
167 // Advance until we see the second "
168 //
169 for (; (currChar != '\0'); i++)
170 {
171 currChar = m_list[i];
172 // Advance until we see the second "
173 if (currChar == '"')
174 {
175 break;
176 }
177 // or until we see the end of string
178 if (currChar == '\0')
179 {
180 break;
181 }
182 }
183
184 // skip the initial "
185 nameStart++;
186 isQuoted = true;
187 }
188
189 if ((currChar == '\0') || (currChar == SEP_CHAR) || (currChar == '('))
190 {
191 DONE_FUNC_NAME:
192 assert((currChar == '\0') || (currChar == SEP_CHAR) || (currChar == '('));
193
194 // Record the method name
195 currentName.m_methodNameStart = nameStart;
196 currentName.m_methodNameLen = i - nameStart;
197
198 if (isQuoted)
199 {
200 // Remove the trailing "
201 currentName.m_methodNameLen--;
202 isQuoted = false;
203 }
204
205 // Is the first character a wildcard?
206 if (m_list[currentName.m_methodNameStart] == WILD_CHAR)
207 {
208 // The method name is a full wildcard; mark it as such.
209 currentName.m_methodNameStart = -1;
210 currentName.m_methodNameLen = -1;
211 }
212 // Is there a wildcard at the end of the method name?
213 //
214 else if (m_list[currentName.m_methodNameStart + currentName.m_methodNameLen - 1] == WILD_CHAR)
215 {
216 // i.e. class:foo*, will match any method that starts with "foo"
217
218 // Remove the trailing WILD_CHAR from method name
219 currentName.m_methodNameLen--; // backup for WILD_CHAR
220 currentName.m_methodNameWildcardAtEnd = true;
221 }
222
223 // should we expect an ARG_LIST?
224 //
225 if (currChar == '(')
226 {
227 currentName.m_numArgs = -1;
228 // Expect an ARG_LIST
229 state = ARG_LIST;
230 }
231 else // reached the end of string or a SEP_CHAR
232 {
233 assert((currChar == '\0') || (currChar == SEP_CHAR));
234
235 currentName.m_numArgs = -1;
236
237 // There isn't an ARG_LIST
238 goto DONE_ARG_LIST;
239 }
240 }
241 break;
242
243 case ARG_LIST:
244 if ((currChar == '\0') || (currChar == ')'))
245 {
246 if (currentName.m_numArgs == -1)
247 {
248 currentName.m_numArgs = 0;
249 }
250
251 DONE_ARG_LIST:
252 assert((currChar == '\0') || (currChar == SEP_CHAR) || (currChar == ')'));
253
254 // We have parsed an entire method name; create a new entry in the list for it.
255 MethodName* name = static_cast<MethodName*>(host->allocateMemory(sizeof(MethodName)));
256 *name = currentName;
257
258 assert(name->m_next == nullptr);
259 *lastName = name;
260 lastName = &name->m_next;
261
262 state = NO_NAME;
263
264 // Skip anything after the argument list until we find the next
265 // separator character. Otherwise if we see "func(a,b):foo" we
266 // would create entries for "func(a,b)" as well as ":foo".
267 if (currChar == ')')
268 {
269 do
270 {
271 currChar = m_list[++i];
272 } while ((currChar != '\0') && (currChar != SEP_CHAR));
273 }
274 }
275 else // We are looking at the ARG_LIST
276 {
277 if ((currChar != SEP_CHAR) && (currentName.m_numArgs == -1))
278 {
279 currentName.m_numArgs = 1;
280 }
281
282 // A comma means that there is an additional arg
283 if (currChar == ',')
284 {
285 currentName.m_numArgs++;
286 }
287 }
288 break;
289
290 default:
291 assert(!"Bad state");
292 break;
293 }
294 }
295}
296
297void JitConfigValues::MethodSet::destroy(ICorJitHost* host)
298{
299 // Free method names, free the list string, and reset our state
300 for (MethodName *name = m_names, *next = nullptr; name != nullptr; name = next)
301 {
302 next = name->m_next;
303 host->freeMemory(static_cast<void*>(name));
304 }
305 if (m_list != nullptr)
306 {
307 host->freeMemory(static_cast<void*>(m_list));
308 m_list = nullptr;
309 }
310 m_names = nullptr;
311}
312
313static bool matchesName(const char* const name, int nameLen, bool wildcardAtEnd, const char* const s2)
314{
315 // 's2' must start with 'nameLen' characters of 'name'
316 if (strncmp(name, s2, nameLen) != 0)
317 {
318 return false;
319 }
320
321 // if we don't have a wildcardAtEnd then s2 also need to be zero terminated
322 if (!wildcardAtEnd && (s2[nameLen] != '\0'))
323 {
324 return false;
325 }
326
327 // we have a successful match
328 return true;
329}
330
331bool JitConfigValues::MethodSet::contains(const char* methodName,
332 const char* className,
333 CORINFO_SIG_INFO* sigInfo) const
334{
335 int numArgs = sigInfo != nullptr ? sigInfo->numArgs : -1;
336
337 // Try to match any the entries in the list.
338 for (MethodName* name = m_names; name != nullptr; name = name->m_next)
339 {
340 // If m_numArgs is valid, check for a mismatch
341 if (name->m_numArgs != -1 && name->m_numArgs != numArgs)
342 {
343 continue;
344 }
345
346 // If m_methodNameStart is valid, check for a mismatch
347 if (name->m_methodNameStart != -1)
348 {
349 const char* expectedMethodName = &m_list[name->m_methodNameStart];
350 if (!matchesName(expectedMethodName, name->m_methodNameLen, name->m_methodNameWildcardAtEnd, methodName))
351 {
352 // C++ embeds the class name into the method name; deal with that here.
353 const char* colon = strchr(methodName, ':');
354 if (colon != nullptr && colon[1] == ':' &&
355 matchesName(expectedMethodName, name->m_methodNameLen, name->m_methodNameWildcardAtEnd, methodName))
356 {
357 int classLen = (int)(colon - methodName);
358 if (name->m_classNameStart == -1 ||
359 (classLen == name->m_classNameLen &&
360 strncmp(&m_list[name->m_classNameStart], methodName, classLen) == 0))
361 {
362 return true;
363 }
364 }
365 continue;
366 }
367 }
368
369 // If m_classNameStart is valid, check for a mismatch
370 if (className == nullptr || name->m_classNameStart == -1 ||
371 matchesName(&m_list[name->m_classNameStart], name->m_classNameLen, name->m_classNameWildcardAtEnd,
372 className))
373 {
374 return true;
375 }
376
377#ifdef _DEBUG
378 // Maybe className doesn't include the namespace. Try to match that
379 const char* nsSep = strrchr(className, '.');
380 if (nsSep != nullptr && nsSep != className)
381 {
382 const char* onlyClass = nsSep[-1] == '.' ? nsSep : &nsSep[1];
383 if (matchesName(&m_list[name->m_classNameStart], name->m_classNameLen, name->m_classNameWildcardAtEnd,
384 onlyClass))
385 {
386 return true;
387 }
388 }
389#endif
390 }
391
392 return false;
393}
394
395void JitConfigValues::initialize(ICorJitHost* host)
396{
397 assert(!m_isInitialized);
398
399#define CONFIG_INTEGER(name, key, defaultValue) m_##name = host->getIntConfigValue(key, defaultValue);
400#define CONFIG_STRING(name, key) m_##name = host->getStringConfigValue(key);
401#define CONFIG_METHODSET(name, key) \
402 const wchar_t* name##value = host->getStringConfigValue(key); \
403 m_##name.initialize(name##value, host); \
404 host->freeStringConfigValue(name##value);
405
406#include "jitconfigvalues.h"
407
408 m_isInitialized = true;
409}
410
411void JitConfigValues::destroy(ICorJitHost* host)
412{
413 if (!m_isInitialized)
414 {
415 return;
416 }
417
418#define CONFIG_INTEGER(name, key, defaultValue)
419#define CONFIG_STRING(name, key) host->freeStringConfigValue(m_##name);
420#define CONFIG_METHODSET(name, key) m_##name.destroy(host);
421
422#include "jitconfigvalues.h"
423
424 m_isInitialized = false;
425}
426