1 /**
2  * \file
3  * Copyright 2016 Microsoft
4  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
5  */
6 #ifndef __MONO_METADATA_REFLECTION_CACHE_H__
7 #define __MONO_METADATA_REFLECTION_CACHE_H__
8 
9 #include <glib.h>
10 #include <mono/metadata/domain-internals.h>
11 #include <mono/metadata/handle.h>
12 #include <mono/metadata/mono-hash.h>
13 #include <mono/metadata/mempool.h>
14 #include <mono/utils/mono-error-internals.h>
15 
16 /*
17  * We need to return always the same object for MethodInfo, FieldInfo etc..
18  * but we need to consider the reflected type.
19  * type uses a different hash, since it uses custom hash/equal functions.
20  */
21 
22 typedef struct {
23 	gpointer item;
24 	MonoClass *refclass;
25 } ReflectedEntry;
26 
27 gboolean
28 reflected_equal (gconstpointer a, gconstpointer b);
29 
30 guint
31 reflected_hash (gconstpointer a);
32 
33 static inline ReflectedEntry*
alloc_reflected_entry(MonoDomain * domain)34 alloc_reflected_entry (MonoDomain *domain)
35 {
36 	if (!mono_gc_is_moving ())
37 		return g_new0 (ReflectedEntry, 1);
38 	else
39 		return (ReflectedEntry *)mono_mempool_alloc (domain->mp, sizeof (ReflectedEntry));
40 }
41 
42 static void
free_reflected_entry(ReflectedEntry * entry)43 free_reflected_entry (ReflectedEntry *entry)
44 {
45 	if (!mono_gc_is_moving ())
46 		g_free (entry);
47 }
48 
49 static inline MonoObject*
cache_object(MonoDomain * domain,MonoClass * klass,gpointer item,MonoObject * o)50 cache_object (MonoDomain *domain, MonoClass *klass, gpointer item, MonoObject* o)
51 {
52 	MonoObject *obj;
53 	ReflectedEntry pe;
54 	pe.item = item;
55 	pe.refclass = klass;
56 
57 	mono_domain_lock (domain);
58 	if (!domain->refobject_hash)
59 		domain->refobject_hash = mono_conc_g_hash_table_new_type (reflected_hash, reflected_equal, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, "domain reflection objects table");
60 
61 	obj = (MonoObject*) mono_conc_g_hash_table_lookup (domain->refobject_hash, &pe);
62 	if (obj == NULL) {
63 		ReflectedEntry *e = alloc_reflected_entry (domain);
64 		e->item = item;
65 		e->refclass = klass;
66 		mono_conc_g_hash_table_insert (domain->refobject_hash, e, o);
67 		obj = o;
68 	}
69 	mono_domain_unlock (domain);
70 	return obj;
71 }
72 
73 
74 static inline MonoObjectHandle
cache_object_handle(MonoDomain * domain,MonoClass * klass,gpointer item,MonoObjectHandle o)75 cache_object_handle (MonoDomain *domain, MonoClass *klass, gpointer item, MonoObjectHandle o)
76 {
77 	ReflectedEntry pe;
78 	pe.item = item;
79 	pe.refclass = klass;
80 
81 	mono_domain_lock (domain);
82 	if (!domain->refobject_hash)
83 		domain->refobject_hash = mono_conc_g_hash_table_new_type (reflected_hash, reflected_equal, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, "domain reflection objects table");
84 
85 	MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, mono_conc_g_hash_table_lookup (domain->refobject_hash, &pe));
86 	if (MONO_HANDLE_IS_NULL (obj)) {
87 		ReflectedEntry *e = alloc_reflected_entry (domain);
88 		e->item = item;
89 		e->refclass = klass;
90 		mono_conc_g_hash_table_insert (domain->refobject_hash, e, MONO_HANDLE_RAW (o));
91 		MONO_HANDLE_ASSIGN (obj, o);
92 	}
93 	mono_domain_unlock (domain);
94 	return obj;
95 }
96 
97 #define CACHE_OBJECT(t,p,o,k) ((t) (cache_object (domain, (k), (p), (o))))
98 #define CACHE_OBJECT_HANDLE(t,p,o,k) ((t) (cache_object_handle (domain, (k), (p), (o))))
99 
100 static inline MonoObjectHandle
check_object_handle(MonoDomain * domain,MonoClass * klass,gpointer item)101 check_object_handle (MonoDomain* domain, MonoClass *klass, gpointer item)
102 {
103 	ReflectedEntry e;
104 	e.item = item;
105 	e.refclass = klass;
106 	MonoConcGHashTable *hash = domain->refobject_hash;
107 	if (!hash)
108 		return MONO_HANDLE_NEW (MonoObject, NULL);
109 
110 	MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, mono_conc_g_hash_table_lookup (hash, &e));
111 	return obj;
112 }
113 
114 
115 typedef MonoObjectHandle (*ReflectionCacheConstructFunc_handle) (MonoDomain*, MonoClass*, gpointer, gpointer, MonoError *);
116 
117 static inline MonoObjectHandle
check_or_construct_handle(MonoDomain * domain,MonoClass * klass,gpointer item,gpointer user_data,MonoError * error,ReflectionCacheConstructFunc_handle construct)118 check_or_construct_handle (MonoDomain *domain, MonoClass *klass, gpointer item, gpointer user_data, MonoError *error, ReflectionCacheConstructFunc_handle construct)
119 {
120 	error_init (error);
121 	MonoObjectHandle obj = check_object_handle (domain, klass, item);
122 	if (!MONO_HANDLE_IS_NULL (obj))
123 		return obj;
124 	MONO_HANDLE_ASSIGN (obj, construct (domain, klass, item, user_data, error));
125 	return_val_if_nok (error, NULL);
126 	if (MONO_HANDLE_IS_NULL (obj))
127 		return obj;
128 	/* note no caching if there was an error in construction */
129 	return cache_object_handle (domain, klass, item, obj);
130 }
131 
132 
133 #define CHECK_OR_CONSTRUCT_HANDLE(t,p,k,construct,ud) ((t) check_or_construct_handle (domain, (k), (p), (ud), error, (ReflectionCacheConstructFunc_handle) (construct)))
134 
135 
136 #endif /*__MONO_METADATA_REFLECTION_CACHE_H__*/
137