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 | |
6 | //***************************************************************************** |
7 | |
8 | // |
9 | // RegMeta.cpp |
10 | // |
11 | // Implementation for meta data public interface methods for full version. |
12 | // |
13 | //***************************************************************************** |
14 | #include "stdafx.h" |
15 | #include "regmeta.h" |
16 | #include "metadata.h" |
17 | #include "corerror.h" |
18 | #include "mdutil.h" |
19 | #include "rwutil.h" |
20 | #include "mdlog.h" |
21 | #include "importhelper.h" |
22 | #include "filtermanager.h" |
23 | #include "mdperf.h" |
24 | #include "switches.h" |
25 | #include "posterror.h" |
26 | #include "stgio.h" |
27 | #include "sstring.h" |
28 | |
29 | #include <metamodelrw.h> |
30 | |
31 | |
32 | |
33 | |
34 | #define DEFINE_CUSTOM_NODUPCHECK 1 |
35 | #define DEFINE_CUSTOM_DUPCHECK 2 |
36 | #define SET_CUSTOM 3 |
37 | |
38 | #if defined(_DEBUG) && defined(_TRACE_REMAPS) |
39 | #define LOGGING |
40 | #endif |
41 | #include <log.h> |
42 | |
43 | #ifdef _MSC_VER |
44 | #pragma warning(disable: 4102) |
45 | #endif |
46 | |
47 | //***************************************************************************** |
48 | // Call this after initialization is complete. |
49 | //***************************************************************************** |
50 | HRESULT RegMeta::AddToCache() |
51 | { |
52 | #if defined(FEATURE_METADATA_IN_VM) |
53 | HRESULT hr = S_OK; |
54 | |
55 | // The ref count must be > 0 before the module is published, else another |
56 | // thread could find, use, and release the module, causing it to be deleted |
57 | // before this thread gets a chance to addref. |
58 | _ASSERTE(GetRefCount() > 0); |
59 | // add this RegMeta to the loaded module list. |
60 | m_bCached = true; |
61 | IfFailGo(LOADEDMODULES::AddModuleToLoadedList(this)); |
62 | ErrExit: |
63 | if (FAILED(hr)) |
64 | { |
65 | _ASSERTE(!LOADEDMODULES::IsEntryInList(this)); |
66 | m_bCached = false; |
67 | } |
68 | return hr; |
69 | #else // FEATURE_METADATA_IN_VM |
70 | return S_OK; |
71 | #endif // FEATURE_METADATA_IN_VM |
72 | } // RegMeta::AddToCache |
73 | |
74 | |
75 | //***************************************************************************** |
76 | // Search the cached RegMetas for a given scope. |
77 | //***************************************************************************** |
78 | HRESULT RegMeta::FindCachedReadOnlyEntry( |
79 | LPCWSTR szName, // Name of the desired file. |
80 | DWORD dwOpenFlags, // Flags the new file is opened with. |
81 | RegMeta **ppMeta) // Put found RegMeta here. |
82 | { |
83 | #if defined(FEATURE_METADATA_IN_VM) |
84 | return LOADEDMODULES::FindCachedReadOnlyEntry(szName, dwOpenFlags, ppMeta); |
85 | #else // FEATURE_METADATA_IN_VM |
86 | // No cache support in standalone version. |
87 | *ppMeta = NULL; |
88 | return S_FALSE; |
89 | #endif // FEATURE_METADATA_IN_VM |
90 | } // RegMeta::FindCachedReadOnlyEntry |
91 | |
92 | |
93 | #ifdef FEATURE_METADATA_EMIT_ALL |
94 | |
95 | //***************************************************************************** |
96 | // Helper function to startup the EE |
97 | // |
98 | // Notes: |
99 | // This is called by code:RegMeta.DefineSecurityAttributeSet. |
100 | //***************************************************************************** |
101 | HRESULT RegMeta::StartupEE() |
102 | { |
103 | UNREACHABLE_MSG_RET("About to CoCreateInstance! This code should not be " |
104 | "reachable or needs to be reimplemented for CoreCLR!" ); |
105 | } |
106 | |
107 | #endif //FEATURE_METADATA_EMIT_ALL |
108 | |
109 | #ifdef FEATURE_METADATA_EMIT |
110 | |
111 | //***************************************************************************** |
112 | // Persist a set of security custom attributes into a set of permission set |
113 | // blobs on the same class or method. |
114 | // |
115 | // Notes: |
116 | // Only in the full version because this is an emit operation. |
117 | //***************************************************************************** |
118 | HRESULT RegMeta::DefineSecurityAttributeSet(// Return code. |
119 | mdToken tkObj, // [IN] Class or method requiring security attributes. |
120 | COR_SECATTR rSecAttrs[], // [IN] Array of security attribute descriptions. |
121 | ULONG cSecAttrs, // [IN] Count of elements in above array. |
122 | ULONG *pulErrorAttr) // [OUT] On error, index of attribute causing problem. |
123 | { |
124 | return E_NOTIMPL; |
125 | } // RegMeta::DefineSecurityAttributeSet |
126 | |
127 | #endif //FEATURE_METADATA_EMIT |
128 | |
129 | |
130 | //***************************************************************************** |
131 | // Implementation of IMetaDataImport::ResolveTypeRef to resolve a typeref across scopes. |
132 | // |
133 | // Arguments: |
134 | // tr - typeref within this scope to resolve |
135 | // riid - interface on ppIScope to support |
136 | // ppIScope - out-parameter to get metadata scope for typedef (*ptd) |
137 | // ptd - out-parameter to get typedef that the ref resolves to. |
138 | // |
139 | // Notes: |
140 | // TypeDefs define a type within a scope. TypeRefs refer to type-defs in other scopes |
141 | // and allow you to import a type from another scope. This function attempts to determine |
142 | // which type-def a type-ref points to. |
143 | // |
144 | // This resolve (type-ref, this cope) --> (type-def=*ptd, other scope=*ppIScope) |
145 | // |
146 | // However, this resolution requires knowing what modules have been loaded, which is not decided |
147 | // until runtime via loader / fusion policy. Thus this interface can't possibly be correct since |
148 | // it doesn't have that knowledge. Furthermore, when inspecting metadata from another process |
149 | // (such as a debugger inspecting the debuggee's metadata), this API can be truly misleading. |
150 | // |
151 | // This API usage should be avoided. |
152 | // |
153 | //***************************************************************************** |
154 | STDMETHODIMP |
155 | RegMeta::ResolveTypeRef( |
156 | mdTypeRef tr, |
157 | REFIID riid, |
158 | IUnknown ** ppIScope, |
159 | mdTypeDef * ptd) |
160 | { |
161 | #ifdef FEATURE_METADATA_IN_VM |
162 | HRESULT hr; |
163 | |
164 | BEGIN_ENTRYPOINT_NOTHROW; |
165 | |
166 | TypeRefRec * pTypeRefRec; |
167 | WCHAR wzNameSpace[_MAX_PATH]; |
168 | CMiniMdRW * pMiniMd = NULL; |
169 | |
170 | LOG((LOGMD, "{%08x} RegMeta::ResolveTypeRef(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n" , |
171 | this, tr, riid, ppIScope, ptd)); |
172 | |
173 | START_MD_PERF(); |
174 | LOCKREAD(); |
175 | |
176 | pMiniMd = &(m_pStgdb->m_MiniMd); |
177 | |
178 | _ASSERTE((ppIScope != NULL) && (ptd != NULL)); |
179 | |
180 | // Init the output values. |
181 | *ppIScope = NULL; |
182 | *ptd = 0; |
183 | |
184 | if (IsNilToken(tr)) |
185 | { |
186 | if (ptd != NULL) |
187 | { |
188 | *ptd = mdTypeDefNil; |
189 | } |
190 | |
191 | if (ppIScope != NULL) |
192 | { |
193 | *ppIScope = NULL; |
194 | } |
195 | |
196 | STOP_MD_PERF(ResolveTypeRef); |
197 | hr = E_INVALIDARG; |
198 | goto ErrExit; |
199 | } |
200 | |
201 | if (TypeFromToken(tr) == mdtTypeDef) |
202 | { |
203 | // Shortcut when we receive a TypeDef token |
204 | *ptd = tr; |
205 | STOP_MD_PERF(ResolveTypeRef); |
206 | hr = this->QueryInterface(riid, (void **)ppIScope); |
207 | goto ErrExit; |
208 | } |
209 | |
210 | // Get the class ref row. |
211 | _ASSERTE(TypeFromToken(tr) == mdtTypeRef); |
212 | |
213 | IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tr), &pTypeRefRec)); |
214 | IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, wzNameSpace, lengthof(wzNameSpace), NULL)); |
215 | if (hr != NOERROR) |
216 | { |
217 | _ASSERTE(hr == CLDB_S_TRUNCATION); |
218 | // Truncate the namespace string |
219 | wzNameSpace[lengthof(wzNameSpace) - 1] = 0; |
220 | } |
221 | |
222 | //*********************** |
223 | // before we go off to CORPATH, check the loaded modules! |
224 | //*********************** |
225 | if (LOADEDMODULES::ResolveTypeRefWithLoadedModules( |
226 | tr, |
227 | this, |
228 | pMiniMd, |
229 | riid, |
230 | ppIScope, |
231 | ptd) == NOERROR) |
232 | { |
233 | // Done!! We found one match among the loaded modules. |
234 | goto ErrExit; |
235 | } |
236 | |
237 | IfFailGo(META_E_CANNOTRESOLVETYPEREF); |
238 | |
239 | ErrExit: |
240 | STOP_MD_PERF(ResolveTypeRef); |
241 | END_ENTRYPOINT_NOTHROW; |
242 | |
243 | return hr; |
244 | #else // FEATURE_METADATA_IN_VM |
245 | return E_NOTIMPL; |
246 | #endif // FEATURE_METADATA_IN_VM |
247 | } // RegMeta::ResolveTypeRef |
248 | |
249 | |
250 | |
251 | // Full version handles metadata caching, which Release() needs to coordinate with. |
252 | // Thus Release() is in a satellite lib. |
253 | ULONG RegMeta::Release() |
254 | { |
255 | // This is called during cleanup. We can not fail this call by probing. |
256 | // As long as we make sure the cleanup does not use too much space through |
257 | // BEGIN_CLEANUP_ENTRYPOINT, we are OK. |
258 | CONTRACT_VIOLATION (SOToleranceViolation); |
259 | BEGIN_CLEANUP_ENTRYPOINT; |
260 | |
261 | #if defined(FEATURE_METADATA_IN_VM) |
262 | _ASSERTE(!m_bCached || LOADEDMODULES::IsEntryInList(this)); |
263 | #else |
264 | _ASSERTE(!m_bCached); |
265 | #endif // FEATURE_METADATA_IN_VM |
266 | BOOL bCached = m_bCached; |
267 | ULONG cRef = InterlockedDecrement(&m_cRef); |
268 | // NOTE: 'this' may be unsafe after this point, if the module is cached, and |
269 | // another thread finds the module in the cache, releases it, and deletes it |
270 | // before we get around to deleting it. (That's why we must make a local copy |
271 | // of m_bCached.) |
272 | // If no references left... |
273 | if (cRef == 0) |
274 | { |
275 | if (!bCached) |
276 | { // If the module is not (was not) cached, no other thread can have |
277 | // discovered the module, so this thread can now safely delete it. |
278 | delete this; |
279 | } |
280 | #if defined(FEATURE_METADATA_IN_VM) |
281 | else if (LOADEDMODULES::RemoveModuleFromLoadedList(this)) |
282 | { // If the module was cached, RemoveModuleFromLoadedList() will try to |
283 | // safely un-publish the module, and if it succeeds, no other thread |
284 | // has (or will) discover the module, so this thread can delete it. |
285 | m_bCached = false; |
286 | delete this; |
287 | } |
288 | #endif // FEATURE_METADATA_IN_VM |
289 | } |
290 | END_CLEANUP_ENTRYPOINT |
291 | |
292 | return cRef; |
293 | } // RegMeta::Release |
294 | |