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 | // 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 | |
297 | void 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 | |
313 | static 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 | |
331 | bool 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 | |
395 | void 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 | |
411 | void 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 | |