[metadata] Fix leaks when handling a few attributes (#16675)
[mono-project.git] / mono / metadata / icall-table.c
blobf019858ae782bbc3032ce3dac83cd5d33550ebc5
1 /**
2 * \file
4 * Authors:
5 * Dietmar Maurer (dietmar@ximian.com)
6 * Paolo Molaro (lupus@ximian.com)
7 * Patrik Torstensson (patrik.torstensson@labs2.com)
8 * Marek Safar (marek.safar@gmail.com)
9 * Aleksey Kliger (aleksey@xamarin.com)
11 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
12 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
13 * Copyright 2011-2015 Xamarin Inc (http://www.xamarin.com).
14 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
17 #include <config.h>
18 #include <glib.h>
19 #include <stdarg.h>
20 #include <string.h>
21 #include <ctype.h>
22 #ifdef HAVE_ALLOCA_H
23 #include <alloca.h>
24 #endif
25 #ifdef HAVE_SYS_TIME_H
26 #include <sys/time.h>
27 #endif
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #if defined (HAVE_WCHAR_H)
32 #include <wchar.h>
33 #endif
35 #include <mono/utils/mono-publib.h>
36 #include <mono/utils/bsearch.h>
37 #include <mono/metadata/icalls.h>
38 #include "handle-decl.h"
39 #include "icall-decl.h"
41 // These definitions are used for multiple includes of icall-def.h and eventually undefined.
42 #define NOHANDLES(inner) inner
43 #define HANDLES(id, name, func, ...) ICALL (id, name, func ## _raw)
44 #define HANDLES_REUSE_WRAPPER HANDLES
45 #define MONO_HANDLE_REGISTER_ICALL(...) /* nothing */
47 //#define TEST_ICALL_SYMBOL_MAP 1
49 // Generate Icall_ constants
50 enum {
51 #define ICALL_TYPE(id,name,first) /* nothing */
52 #define ICALL_TYPE(id,name,first)
53 #define ICALL(id,name,func) Icall_ ## id,
54 #include "metadata/icall-def.h"
55 #undef ICALL_TYPE
56 #undef ICALL
57 Icall_last
60 enum {
61 #define ICALL_TYPE(id,name,first) Icall_type_ ## id,
62 #define ICALL(id,name,func) /* nothing */
63 #include "metadata/icall-def.h"
64 #undef ICALL_TYPE
65 #undef ICALL
66 Icall_type_num
69 typedef struct {
70 guint16 first_icall;
71 } IcallTypeDesc;
73 static const IcallTypeDesc icall_type_descs [] = {
74 #define ICALL_TYPE(id,name,firstic) {(Icall_ ## firstic)},
75 #define ICALL(id,name,func) /* nothing */
76 #include "metadata/icall-def.h"
77 #undef ICALL_TYPE
78 #undef ICALL
79 {Icall_last}
82 #define icall_desc_num_icalls(desc) ((desc) [1].first_icall - (desc) [0].first_icall)
84 // This, instead of an array of pointers, to optimize away a pointer and a relocation per string.
86 #define MSGSTRFIELD(line) MSGSTRFIELD1(line)
87 #define MSGSTRFIELD1(line) str##line
89 static const struct msgstrtn_t {
90 #define ICALL_TYPE(id,name,first) char MSGSTRFIELD(__LINE__) [sizeof (name)];
91 #define ICALL(id,name,func) /* nothing */
92 #include "metadata/icall-def.h"
93 #undef ICALL_TYPE
94 #undef ICALL
95 } icall_type_names_str = {
96 #define ICALL_TYPE(id,name,first) (name),
97 #define ICALL(id,name,func)
98 #include "metadata/icall-def.h"
99 #undef ICALL_TYPE
100 #undef ICALL
103 static const guint16 icall_type_names_idx [] = {
104 #define ICALL_TYPE(id,name,first) (offsetof (struct msgstrtn_t, MSGSTRFIELD(__LINE__))),
105 #define ICALL(id,name,func) /* nothing */
106 #include "metadata/icall-def.h"
107 #undef ICALL_TYPE
108 #undef ICALL
111 #define icall_type_name_get(id) ((const char*)&icall_type_names_str + icall_type_names_idx [(id)])
113 static const struct msgstr_t {
114 #define ICALL_TYPE(id,name,first) /* nothing */
115 #define ICALL(id,name,func) char MSGSTRFIELD(__LINE__) [sizeof (name)];
116 #include "metadata/icall-def.h"
117 #undef ICALL_TYPE
118 #undef ICALL
119 } icall_names_str = {
120 #define ICALL_TYPE(id,name,first)
121 #define ICALL(id,name,func) (name),
122 #include "metadata/icall-def.h"
123 #undef ICALL_TYPE
124 #undef ICALL
127 static const guint16 icall_names_idx [] = {
128 #define ICALL_TYPE(id,name,first) /* nothing */
129 #define ICALL(id,name,func) (offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__))),
130 #include "metadata/icall-def.h"
131 #undef ICALL_TYPE
132 #undef ICALL
135 #define icall_name_get(id) ((const char*)&icall_names_str + icall_names_idx [(id)])
137 typedef struct MonoICallFunction {
138 gpointer func;
139 #if ENABLE_ICALL_SYMBOL_MAP || TEST_ICALL_SYMBOL_MAP
140 const char *name;
141 #endif
142 } MonoICallFunction;
144 static const MonoICallFunction icall_functions [] = {
145 #define ICALL_TYPE(id,name,first) /* nothing */
146 #if ENABLE_ICALL_SYMBOL_MAP || TEST_ICALL_SYMBOL_MAP
147 #define ICALL(id, name, func) { (gpointer)func, #func },
148 #else
149 #define ICALL(id, name, func) { (gpointer)func },
150 #endif
151 #include "metadata/icall-def.h"
152 #undef ICALL_TYPE
153 #undef ICALL
154 { NULL },
157 #undef HANDLES
158 #undef NOHANDLES
160 static const guchar icall_uses_handles [] = {
161 #define ICALL_TYPE(id,name,first) /* nothing */
162 #define ICALL(id,name,func) 0,
163 #define HANDLES(...) 1,
164 #define NOHANDLES(inner) 0,
165 #include "metadata/icall-def.h"
166 #undef ICALL_TYPE
167 #undef ICALL
170 #undef HANDLES
171 #undef HANDLES_REUSE_WRAPPER
172 #undef NOHANDLES
173 #undef MONO_HANDLE_REGISTER_ICALL
175 static int
176 compare_method_imap (const void *key, const void *elem)
178 const char* method_name = (const char*)&icall_names_str + (*(guint16*)elem);
179 return strcmp ((const char*)key, method_name);
182 static gssize
183 find_slot_icall (const IcallTypeDesc *imap, const char *name)
185 const guint16 *nameslot = (const guint16 *)mono_binary_search (name, icall_names_idx + imap->first_icall, icall_desc_num_icalls (imap), sizeof (icall_names_idx [0]), compare_method_imap);
186 if (!nameslot)
187 return -1;
188 return (nameslot - &icall_names_idx [0]);
191 static gboolean
192 find_uses_handles_icall (const IcallTypeDesc *imap, const char *name)
194 const gssize slotnum = find_slot_icall (imap, name);
195 if (slotnum == -1)
196 return FALSE;
197 return (gboolean)icall_uses_handles [slotnum];
200 static gpointer
201 find_method_icall (const IcallTypeDesc *imap, const char *name)
203 const gssize slotnum = find_slot_icall (imap, name);
204 if (slotnum == -1)
205 return NULL;
206 return icall_functions [slotnum].func;
209 static int
210 compare_class_imap (const void *key, const void *elem)
212 const char* class_name = (const char*)&icall_type_names_str + (*(guint16*)elem);
213 return strcmp ((const char*)key, class_name);
216 static const IcallTypeDesc*
217 find_class_icalls (const char *name)
219 const guint16 *nameslot = (const guint16 *)mono_binary_search (name, icall_type_names_idx, Icall_type_num, sizeof (icall_type_names_idx [0]), compare_class_imap);
220 if (!nameslot)
221 return NULL;
222 return &icall_type_descs [nameslot - &icall_type_names_idx [0]];
225 static gpointer
226 icall_table_lookup (MonoMethod *method, char *classname, char *methodname, char *sigstart, gboolean *uses_handles)
228 const IcallTypeDesc *imap = NULL;
229 gpointer res;
231 imap = find_class_icalls (classname);
233 /* it wasn't found in the static call tables */
234 if (!imap) {
235 if (uses_handles)
236 *uses_handles = FALSE;
237 return NULL;
239 res = find_method_icall (imap, methodname);
240 if (res) {
241 if (uses_handles)
242 *uses_handles = find_uses_handles_icall (imap, methodname);
243 return res;
245 /* try _with_ signature */
246 *sigstart = '(';
247 res = find_method_icall (imap, methodname);
248 if (res) {
249 if (uses_handles)
250 *uses_handles = find_uses_handles_icall (imap, methodname);
251 return res;
253 return NULL;
256 #if ENABLE_ICALL_SYMBOL_MAP || TEST_ICALL_SYMBOL_MAP
258 static
260 #if _WIN32
261 __cdecl
262 #endif
263 mono_qsort_icall_function_compare_indirect (gconstpointer va, gconstpointer vb)
265 const gpointer a = icall_functions [*(const guint16*)va].func;
266 const gpointer b = icall_functions [*(const guint16*)vb].func;
267 return (a < b) ? -1 : (a > b) ? 1 : 0;
270 static
272 #if _WIN32
273 __cdecl
274 #endif
275 mono_bsearch_icall_function_compare_indirect (gconstpointer a, gconstpointer vb)
277 const gpointer b = icall_functions [*(const guint16*)vb].func;
278 return (a < b) ? -1 : (a > b) ? 1 : 0;
281 #endif // ENABLE_ICALL_SYMBOL_MAP || TEST_ICALL_SYMBOL_MAP
283 #if !TEST_ICALL_SYMBOL_MAP
284 static
285 #endif
286 const char*
287 mono_lookup_icall_symbol_internal (gpointer func)
289 #if ENABLE_ICALL_SYMBOL_MAP || TEST_ICALL_SYMBOL_MAP
290 typedef guint16 T;
291 const gsize N = G_N_ELEMENTS (icall_functions) - 1; // skip terminal null element
292 g_static_assert (N <= 0xFFFF); // If this fails, change T to guint32
293 static T *static_functions_sorted;
295 if (!func)
296 return NULL;
298 if (!static_functions_sorted) {
300 T *functions_sorted = g_new (T, N);
302 // Initialize with identity mapping. One line is easier to step over.
303 for (T i = 0; i < N; ++i) functions_sorted [i] = i;
305 mono_qsort (functions_sorted, N, sizeof (T), mono_qsort_icall_function_compare_indirect);
307 gpointer old = mono_atomic_cas_ptr ((gpointer*)&static_functions_sorted, functions_sorted, NULL);
308 if (old)
309 g_free (functions_sorted);
312 T const * const slot = (const T*)bsearch (func, static_functions_sorted, N, sizeof (T), mono_bsearch_icall_function_compare_indirect);
313 if (!slot)
314 return NULL;
315 return icall_functions [*slot].name;
316 #else
317 fprintf (stderr, "icall symbol maps not enabled, pass --enable-icall-symbol-map to configure.\n");
318 g_assert_not_reached ();
319 return NULL;
320 #endif
323 void
324 mono_icall_table_init (void)
326 int i = 0;
328 /* check that tables are sorted: disable in release */
329 if (TRUE) {
330 int j;
331 const char *prev_class = NULL;
332 const char *prev_method;
334 for (i = 0; i < Icall_type_num; ++i) {
335 const IcallTypeDesc *desc;
336 int num_icalls;
337 prev_method = NULL;
338 if (prev_class && strcmp (prev_class, icall_type_name_get (i)) >= 0)
339 g_print ("class %s should come before class %s\n", icall_type_name_get (i), prev_class);
340 prev_class = icall_type_name_get (i);
341 desc = &icall_type_descs [i];
342 num_icalls = icall_desc_num_icalls (desc);
343 /*g_print ("class %s has %d icalls starting at %d\n", prev_class, num_icalls, desc->first_icall);*/
344 for (j = 0; j < num_icalls; ++j) {
345 const char *methodn = icall_name_get (desc->first_icall + j);
346 if (prev_method && strcmp (prev_method, methodn) >= 0)
347 g_print ("method %s should come before method %s\n", methodn, prev_method);
348 prev_method = methodn;
353 static const MonoIcallTableCallbacks mono_icall_table_callbacks =
355 MONO_ICALL_TABLE_CALLBACKS_VERSION,
356 icall_table_lookup,
357 mono_lookup_icall_symbol_internal,
359 mono_install_icall_table_callbacks (&mono_icall_table_callbacks);