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 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7 | XX XX |
8 | XX typeInfo XX |
9 | XX XX |
10 | XX XX |
11 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
12 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
13 | */ |
14 | |
15 | #include "jitpch.h" |
16 | #ifdef _MSC_VER |
17 | #pragma hdrstop |
18 | #endif |
19 | |
20 | #include "_typeinfo.h" |
21 | |
22 | BOOL Compiler::tiCompatibleWith(const typeInfo& child, const typeInfo& parent, bool normalisedForStack) const |
23 | { |
24 | #ifdef DEBUG |
25 | #if VERBOSE_VERIFY |
26 | if (VERBOSE && tiVerificationNeeded) |
27 | { |
28 | printf("\n" ); |
29 | printf(TI_DUMP_PADDING); |
30 | printf("Verifying compatibility against types: " ); |
31 | child.Dump(); |
32 | printf(" and " ); |
33 | parent.Dump(); |
34 | } |
35 | #endif // VERBOSE_VERIFY |
36 | #endif // DEBUG |
37 | |
38 | BOOL compatible = typeInfo::tiCompatibleWith(info.compCompHnd, child, parent, normalisedForStack); |
39 | |
40 | #ifdef DEBUG |
41 | #if VERBOSE_VERIFY |
42 | if (VERBOSE && tiVerificationNeeded) |
43 | { |
44 | printf(compatible ? " [YES]" : " [NO]" ); |
45 | } |
46 | #endif // VERBOSE_VERIFY |
47 | #endif // DEBUG |
48 | |
49 | return compatible; |
50 | } |
51 | |
52 | BOOL Compiler::tiMergeCompatibleWith(const typeInfo& child, const typeInfo& parent, bool normalisedForStack) const |
53 | { |
54 | return typeInfo::tiMergeCompatibleWith(info.compCompHnd, child, parent, normalisedForStack); |
55 | } |
56 | |
57 | BOOL Compiler::tiMergeToCommonParent(typeInfo* pDest, const typeInfo* pSrc, bool* changed) const |
58 | { |
59 | #ifdef DEBUG |
60 | #if VERBOSE_VERIFY |
61 | if (VERBOSE && tiVerificationNeeded) |
62 | { |
63 | printf("\n" ); |
64 | printf(TI_DUMP_PADDING); |
65 | printf("Attempting to merge types: " ); |
66 | pDest->Dump(); |
67 | printf(" and " ); |
68 | pSrc->Dump(); |
69 | printf("\n" ); |
70 | } |
71 | #endif // VERBOSE_VERIFY |
72 | #endif // DEBUG |
73 | |
74 | BOOL mergeable = typeInfo::tiMergeToCommonParent(info.compCompHnd, pDest, pSrc, changed); |
75 | |
76 | #ifdef DEBUG |
77 | #if VERBOSE_VERIFY |
78 | if (VERBOSE && tiVerificationNeeded) |
79 | { |
80 | printf(TI_DUMP_PADDING); |
81 | printf((mergeable == TRUE) ? "Merge successful" : "Couldn't merge types" ); |
82 | if (*changed) |
83 | { |
84 | assert(mergeable); |
85 | printf(", destination type changed to: " ); |
86 | pDest->Dump(); |
87 | } |
88 | printf("\n" ); |
89 | } |
90 | #endif // VERBOSE_VERIFY |
91 | #endif // DEBUG |
92 | |
93 | return mergeable; |
94 | } |
95 | |
96 | static BOOL tiCompatibleWithByRef(COMP_HANDLE CompHnd, const typeInfo& child, const typeInfo& parent) |
97 | { |
98 | assert(parent.IsByRef()); |
99 | |
100 | if (!child.IsByRef()) |
101 | { |
102 | return FALSE; |
103 | } |
104 | |
105 | if (child.IsReadonlyByRef() && !parent.IsReadonlyByRef()) |
106 | { |
107 | return FALSE; |
108 | } |
109 | |
110 | // Byrefs are compatible if the underlying types are equivalent |
111 | typeInfo childTarget = ::DereferenceByRef(child); |
112 | typeInfo parentTarget = ::DereferenceByRef(parent); |
113 | |
114 | if (typeInfo::AreEquivalent(childTarget, parentTarget)) |
115 | { |
116 | return TRUE; |
117 | } |
118 | |
119 | // Make sure that both types have a valid m_cls |
120 | if ((childTarget.IsType(TI_REF) || childTarget.IsType(TI_STRUCT)) && |
121 | (parentTarget.IsType(TI_REF) || parentTarget.IsType(TI_STRUCT))) |
122 | { |
123 | return CompHnd->areTypesEquivalent(childTarget.GetClassHandle(), parentTarget.GetClassHandle()); |
124 | } |
125 | |
126 | return FALSE; |
127 | } |
128 | |
129 | /***************************************************************************** |
130 | * Verify child is compatible with the template parent. Basically, that |
131 | * child is a "subclass" of parent -it can be substituted for parent |
132 | * anywhere. Note that if parent contains fancy flags, such as "uninitialized" |
133 | * , "is this ptr", or "has byref local/field" info, then child must also |
134 | * contain those flags, otherwise FALSE will be returned ! |
135 | * |
136 | * Rules for determining compatibility: |
137 | * |
138 | * If parent is a primitive type or value class, then child must be the |
139 | * same primitive type or value class. The exception is that the built in |
140 | * value classes System/Boolean etc. are treated as synonyms for |
141 | * TI_BYTE etc. |
142 | * |
143 | * If parent is a byref of a primitive type or value class, then child |
144 | * must be a byref of the same (rules same as above case). |
145 | * |
146 | * Byrefs are compatible only with byrefs. |
147 | * |
148 | * If parent is an object, child must be a subclass of it, implement it |
149 | * (if it is an interface), or be null. |
150 | * |
151 | * If parent is an array, child must be the same or subclassed array. |
152 | * |
153 | * If parent is a null objref, only null is compatible with it. |
154 | * |
155 | * If the "uninitialized", "by ref local/field", "this pointer" or other flags |
156 | * are different, the items are incompatible. |
157 | * |
158 | * parent CANNOT be an undefined (dead) item. |
159 | * |
160 | */ |
161 | |
162 | BOOL typeInfo::tiCompatibleWith(COMP_HANDLE CompHnd, |
163 | const typeInfo& child, |
164 | const typeInfo& parent, |
165 | bool normalisedForStack) |
166 | { |
167 | assert(child.IsDead() || !normalisedForStack || typeInfo::AreEquivalent(::NormaliseForStack(child), child)); |
168 | assert(parent.IsDead() || !normalisedForStack || typeInfo::AreEquivalent(::NormaliseForStack(parent), parent)); |
169 | |
170 | if (typeInfo::AreEquivalent(child, parent)) |
171 | { |
172 | return TRUE; |
173 | } |
174 | |
175 | if (parent.IsUnboxedGenericTypeVar() || child.IsUnboxedGenericTypeVar()) |
176 | { |
177 | return (FALSE); // need to have had child == parent |
178 | } |
179 | else if (parent.IsType(TI_REF)) |
180 | { |
181 | // An uninitialized objRef is not compatible to initialized. |
182 | if (child.IsUninitialisedObjRef() && !parent.IsUninitialisedObjRef()) |
183 | { |
184 | return FALSE; |
185 | } |
186 | |
187 | if (child.IsNullObjRef()) |
188 | { // NULL can be any reference type |
189 | return TRUE; |
190 | } |
191 | if (!child.IsType(TI_REF)) |
192 | { |
193 | return FALSE; |
194 | } |
195 | |
196 | return CompHnd->canCast(child.m_cls, parent.m_cls); |
197 | } |
198 | else if (parent.IsType(TI_METHOD)) |
199 | { |
200 | if (!child.IsType(TI_METHOD)) |
201 | { |
202 | return FALSE; |
203 | } |
204 | |
205 | // Right now we don't bother merging method handles |
206 | return FALSE; |
207 | } |
208 | else if (parent.IsType(TI_STRUCT)) |
209 | { |
210 | if (!child.IsType(TI_STRUCT)) |
211 | { |
212 | return FALSE; |
213 | } |
214 | |
215 | // Structures are compatible if they are equivalent |
216 | return CompHnd->areTypesEquivalent(child.m_cls, parent.m_cls); |
217 | } |
218 | else if (parent.IsByRef()) |
219 | { |
220 | return tiCompatibleWithByRef(CompHnd, child, parent); |
221 | } |
222 | #ifdef _TARGET_64BIT_ |
223 | // On 64-bit targets we have precise representation for native int, so these rules |
224 | // represent the fact that the ECMA spec permits the implicit conversion |
225 | // between an int32 and a native int. |
226 | else if (parent.IsType(TI_INT) && typeInfo::AreEquivalent(nativeInt(), child)) |
227 | { |
228 | return TRUE; |
229 | } |
230 | else if (typeInfo::AreEquivalent(nativeInt(), parent) && child.IsType(TI_INT)) |
231 | { |
232 | return TRUE; |
233 | } |
234 | #endif // _TARGET_64BIT_ |
235 | return FALSE; |
236 | } |
237 | |
238 | BOOL typeInfo::tiMergeCompatibleWith(COMP_HANDLE CompHnd, |
239 | const typeInfo& child, |
240 | const typeInfo& parent, |
241 | bool normalisedForStack) |
242 | { |
243 | if (!child.IsPermanentHomeByRef() && parent.IsPermanentHomeByRef()) |
244 | { |
245 | return FALSE; |
246 | } |
247 | |
248 | return typeInfo::tiCompatibleWith(CompHnd, child, parent, normalisedForStack); |
249 | } |
250 | |
251 | /***************************************************************************** |
252 | * Merge pDest and pSrc to find some commonality (e.g. a common parent). |
253 | * Copy the result to pDest, marking it dead if no commonality can be found. |
254 | * |
255 | * null ^ null -> null |
256 | * Object ^ null -> Object |
257 | * [I4 ^ null -> [I4 |
258 | * InputStream ^ OutputStream -> Stream |
259 | * InputStream ^ NULL -> InputStream |
260 | * [I4 ^ Object -> Object |
261 | * [I4 ^ [Object -> Array |
262 | * [I4 ^ [R8 -> Array |
263 | * [Foo ^ I4 -> DEAD |
264 | * [Foo ^ [I1 -> Array |
265 | * [InputStream ^ [OutputStream -> Array |
266 | * DEAD ^ X -> DEAD |
267 | * [Intfc ^ [OutputStream -> Array |
268 | * Intf ^ [OutputStream -> Object |
269 | * [[InStream ^ [[OutStream -> Array |
270 | * [[InStream ^ [OutStream -> Array |
271 | * [[Foo ^ [Object -> Array |
272 | * |
273 | * Importantly: |
274 | * [I1 ^ [U1 -> either [I1 or [U1 |
275 | * etc. |
276 | * |
277 | * Also, System/Int32 and I4 merge -> I4, etc. |
278 | * |
279 | * Returns FALSE if the merge was completely incompatible (i.e. the item became |
280 | * dead). |
281 | * |
282 | */ |
283 | |
284 | BOOL typeInfo::tiMergeToCommonParent(COMP_HANDLE CompHnd, typeInfo* pDest, const typeInfo* pSrc, bool* changed) |
285 | { |
286 | assert(pSrc->IsDead() || typeInfo::AreEquivalent(::NormaliseForStack(*pSrc), *pSrc)); |
287 | assert(pDest->IsDead() || typeInfo::AreEquivalent(::NormaliseForStack(*pDest), *pDest)); |
288 | |
289 | // Merge the auxiliary information like "this" pointer tracking, etc... |
290 | |
291 | // Remember the pre-state, so we can tell if it changed. |
292 | *changed = false; |
293 | DWORD destFlagsBefore = pDest->m_flags; |
294 | |
295 | // This bit is only set if both pDest and pSrc have it set |
296 | pDest->m_flags &= (pSrc->m_flags | ~TI_FLAG_THIS_PTR); |
297 | |
298 | // This bit is set if either pDest or pSrc have it set |
299 | pDest->m_flags |= (pSrc->m_flags & TI_FLAG_UNINIT_OBJREF); |
300 | |
301 | // This bit is set if either pDest or pSrc have it set |
302 | pDest->m_flags |= (pSrc->m_flags & TI_FLAG_BYREF_READONLY); |
303 | |
304 | // If the byref wasn't permanent home in both sides, then merge won't have the bit set |
305 | pDest->m_flags &= (pSrc->m_flags | ~TI_FLAG_BYREF_PERMANENT_HOME); |
306 | |
307 | if (pDest->m_flags != destFlagsBefore) |
308 | { |
309 | *changed = true; |
310 | } |
311 | |
312 | // OK the main event. Merge the main types |
313 | if (typeInfo::AreEquivalent(*pDest, *pSrc)) |
314 | { |
315 | return (TRUE); |
316 | } |
317 | |
318 | if (pDest->IsUnboxedGenericTypeVar() || pSrc->IsUnboxedGenericTypeVar()) |
319 | { |
320 | // Should have had *pDest == *pSrc |
321 | goto FAIL; |
322 | } |
323 | if (pDest->IsType(TI_REF)) |
324 | { |
325 | if (pSrc->IsType(TI_NULL)) |
326 | { // NULL can be any reference type |
327 | return TRUE; |
328 | } |
329 | if (!pSrc->IsType(TI_REF)) |
330 | { |
331 | goto FAIL; |
332 | } |
333 | |
334 | // Ask the EE to find the common parent, This always succeeds since System.Object always works |
335 | CORINFO_CLASS_HANDLE pDestClsBefore = pDest->m_cls; |
336 | pDest->m_cls = CompHnd->mergeClasses(pDest->GetClassHandle(), pSrc->GetClassHandle()); |
337 | if (pDestClsBefore != pDest->m_cls) |
338 | { |
339 | *changed = true; |
340 | } |
341 | return TRUE; |
342 | } |
343 | else if (pDest->IsType(TI_NULL)) |
344 | { |
345 | if (pSrc->IsType(TI_REF)) // NULL can be any reference type |
346 | { |
347 | *pDest = *pSrc; |
348 | *changed = true; |
349 | return TRUE; |
350 | } |
351 | goto FAIL; |
352 | } |
353 | else if (pDest->IsType(TI_STRUCT)) |
354 | { |
355 | if (pSrc->IsType(TI_STRUCT) && CompHnd->areTypesEquivalent(pDest->GetClassHandle(), pSrc->GetClassHandle())) |
356 | { |
357 | return TRUE; |
358 | } |
359 | goto FAIL; |
360 | } |
361 | else if (pDest->IsByRef()) |
362 | { |
363 | return tiCompatibleWithByRef(CompHnd, *pSrc, *pDest); |
364 | } |
365 | #ifdef _TARGET_64BIT_ |
366 | // On 64-bit targets we have precise representation for native int, so these rules |
367 | // represent the fact that the ECMA spec permits the implicit conversion |
368 | // between an int32 and a native int. |
369 | else if (typeInfo::AreEquivalent(*pDest, typeInfo::nativeInt()) && pSrc->IsType(TI_INT)) |
370 | { |
371 | return TRUE; |
372 | } |
373 | else if (typeInfo::AreEquivalent(*pSrc, typeInfo::nativeInt()) && pDest->IsType(TI_INT)) |
374 | { |
375 | *pDest = *pSrc; |
376 | *changed = true; |
377 | return TRUE; |
378 | } |
379 | #endif // _TARGET_64BIT_ |
380 | |
381 | FAIL: |
382 | *pDest = typeInfo(); |
383 | return FALSE; |
384 | } |
385 | |
386 | #ifdef DEBUG |
387 | #if VERBOSE_VERIFY |
388 | // Utility method to have a detailed dump of a TypeInfo object |
389 | void typeInfo::Dump() const |
390 | { |
391 | char flagsStr[8]; |
392 | |
393 | flagsStr[0] = ((m_flags & TI_FLAG_UNINIT_OBJREF) != 0) ? 'U' : '-'; |
394 | flagsStr[1] = ((m_flags & TI_FLAG_BYREF) != 0) ? 'B' : '-'; |
395 | flagsStr[2] = ((m_flags & TI_FLAG_BYREF_READONLY) != 0) ? 'R' : '-'; |
396 | flagsStr[3] = ((m_flags & TI_FLAG_NATIVE_INT) != 0) ? 'N' : '-'; |
397 | flagsStr[4] = ((m_flags & TI_FLAG_THIS_PTR) != 0) ? 'T' : '-'; |
398 | flagsStr[5] = ((m_flags & TI_FLAG_BYREF_PERMANENT_HOME) != 0) ? 'P' : '-'; |
399 | flagsStr[6] = ((m_flags & TI_FLAG_GENERIC_TYPE_VAR) != 0) ? 'G' : '-'; |
400 | flagsStr[7] = '\0'; |
401 | |
402 | printf("[%s(%X) {%s}]" , tiType2Str(m_bits.type), m_cls, flagsStr); |
403 | } |
404 | #endif // VERBOSE_VERIFY |
405 | #endif // DEBUG |
406 | |