1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation.
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6  * copy of the license can be found in the License.html file at the root of this distribution. If
7  * you cannot locate the  Apache License, Version 2.0, please send an email to
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 
16 #if CLR2
17 using Microsoft.Scripting.Ast;
18 #else
19 using System.Linq.Expressions;
20 #endif
21 #if SILVERLIGHT
22 using System.Core;
23 #endif
24 
25 using System.Collections.Generic;
26 using System.Diagnostics;
27 using System.Reflection;
28 
29 namespace System.Dynamic.Utils {
30 
31     internal static class TypeUtils {
32         private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
33         internal const MethodAttributes PublicStatic = MethodAttributes.Public | MethodAttributes.Static;
34 
GetNonNullableType(this Type type)35         internal static Type GetNonNullableType(this Type type) {
36             if (IsNullableType(type)) {
37                 return type.GetGenericArguments()[0];
38             }
39             return type;
40         }
41 
GetNullableType(Type type)42         internal static Type GetNullableType(Type type) {
43             Debug.Assert(type != null, "type cannot be null");
44             if (type.IsValueType && !IsNullableType(type)) {
45                 return typeof(Nullable<>).MakeGenericType(type);
46             }
47             return type;
48         }
49 
IsNullableType(this Type type)50         internal static bool IsNullableType(this Type type) {
51             return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
52         }
53 
IsBool(Type type)54         internal static bool IsBool(Type type) {
55             return GetNonNullableType(type) == typeof(bool);
56         }
57 
IsNumeric(Type type)58         internal static bool IsNumeric(Type type) {
59             type = GetNonNullableType(type);
60             if (!type.IsEnum) {
61                 switch (Type.GetTypeCode(type)) {
62                     case TypeCode.Char:
63                     case TypeCode.SByte:
64                     case TypeCode.Byte:
65                     case TypeCode.Int16:
66                     case TypeCode.Int32:
67                     case TypeCode.Int64:
68                     case TypeCode.Double:
69                     case TypeCode.Single:
70                     case TypeCode.UInt16:
71                     case TypeCode.UInt32:
72                     case TypeCode.UInt64:
73                         return true;
74                 }
75             }
76             return false;
77         }
78 
IsInteger(Type type)79         internal static bool IsInteger(Type type) {
80             type = GetNonNullableType(type);
81             if (type.IsEnum) {
82                 return false;
83             }
84             switch (Type.GetTypeCode(type)) {
85                 case TypeCode.Byte:
86                 case TypeCode.SByte:
87                 case TypeCode.Int16:
88                 case TypeCode.Int32:
89                 case TypeCode.Int64:
90                 case TypeCode.UInt16:
91                 case TypeCode.UInt32:
92                 case TypeCode.UInt64:
93                     return true;
94                 default:
95                     return false;
96             }
97         }
98 
99 
IsArithmetic(Type type)100         internal static bool IsArithmetic(Type type) {
101             type = GetNonNullableType(type);
102             if (!type.IsEnum) {
103                 switch (Type.GetTypeCode(type)) {
104                     case TypeCode.Int16:
105                     case TypeCode.Int32:
106                     case TypeCode.Int64:
107                     case TypeCode.Double:
108                     case TypeCode.Single:
109                     case TypeCode.UInt16:
110                     case TypeCode.UInt32:
111                     case TypeCode.UInt64:
112                         return true;
113                 }
114             }
115             return false;
116         }
117 
IsUnsignedInt(Type type)118         internal static bool IsUnsignedInt(Type type) {
119             type = GetNonNullableType(type);
120             if (!type.IsEnum) {
121                 switch (Type.GetTypeCode(type)) {
122                     case TypeCode.UInt16:
123                     case TypeCode.UInt32:
124                     case TypeCode.UInt64:
125                         return true;
126                 }
127             }
128             return false;
129         }
130 
IsIntegerOrBool(Type type)131         internal static bool IsIntegerOrBool(Type type) {
132             type = GetNonNullableType(type);
133             if (!type.IsEnum) {
134                 switch (Type.GetTypeCode(type)) {
135                     case TypeCode.Int64:
136                     case TypeCode.Int32:
137                     case TypeCode.Int16:
138                     case TypeCode.UInt64:
139                     case TypeCode.UInt32:
140                     case TypeCode.UInt16:
141                     case TypeCode.Boolean:
142                     case TypeCode.SByte:
143                     case TypeCode.Byte:
144                         return true;
145                 }
146             }
147             return false;
148         }
149 
AreEquivalent(Type t1, Type t2)150         internal static bool AreEquivalent(Type t1, Type t2)
151         {
152 #if CLR2 || SILVERLIGHT
153             return t1 == t2;
154 #else
155             return t1 == t2 || t1.IsEquivalentTo(t2);
156 #endif
157         }
158 
AreReferenceAssignable(Type dest, Type src)159         internal static bool AreReferenceAssignable(Type dest, Type src) {
160             // WARNING: This actually implements "Is this identity assignable and/or reference assignable?"
161             if (AreEquivalent(dest, src)) {
162                 return true;
163             }
164             if (!dest.IsValueType && !src.IsValueType && dest.IsAssignableFrom(src)) {
165                 return true;
166             }
167             return false;
168         }
169 
170         // Checks if the type is a valid target for an instance call
IsValidInstanceType(MemberInfo member, Type instanceType)171         internal static bool IsValidInstanceType(MemberInfo member, Type instanceType) {
172             Type targetType = member.DeclaringType;
173             if (AreReferenceAssignable(targetType, instanceType)) {
174                 return true;
175             }
176             if (instanceType.IsValueType) {
177                 if (AreReferenceAssignable(targetType, typeof(System.Object))) {
178                     return true;
179                 }
180                 if (AreReferenceAssignable(targetType, typeof(System.ValueType))) {
181                     return true;
182                 }
183                 if (instanceType.IsEnum && AreReferenceAssignable(targetType, typeof(System.Enum))) {
184                     return true;
185                 }
186                 // A call to an interface implemented by a struct is legal whether the struct has
187                 // been boxed or not.
188                 if (targetType.IsInterface) {
189                     foreach (Type interfaceType in instanceType.GetInterfaces()) {
190                         if (AreReferenceAssignable(targetType, interfaceType)) {
191                             return true;
192                         }
193                     }
194                 }
195             }
196             return false;
197         }
198 
HasIdentityPrimitiveOrNullableConversion(Type source, Type dest)199         internal static bool HasIdentityPrimitiveOrNullableConversion(Type source, Type dest) {
200             Debug.Assert(source != null && dest != null);
201 
202             // Identity conversion
203             if (AreEquivalent(source, dest)) {
204                 return true;
205             }
206 
207             // Nullable conversions
208             if (IsNullableType(source) && AreEquivalent(dest, GetNonNullableType(source))) {
209                 return true;
210             }
211             if (IsNullableType(dest) && AreEquivalent(source, GetNonNullableType(dest))) {
212                 return true;
213             }
214             // Primitive runtime conversions
215             // All conversions amongst enum, bool, char, integer and float types
216             // (and their corresponding nullable types) are legal except for
217             // nonbool==>bool and nonbool==>bool?
218             // Since we have already covered bool==>bool, bool==>bool?, etc, above,
219             // we can just disallow having a bool or bool? destination type here.
220             if (IsConvertible(source) && IsConvertible(dest) && GetNonNullableType(dest) != typeof(bool)) {
221                 return true;
222             }
223             return false;
224         }
225 
HasReferenceConversion(Type source, Type dest)226         internal static bool HasReferenceConversion(Type source, Type dest) {
227             Debug.Assert(source != null && dest != null);
228 
229             // void -> void conversion is handled elsewhere
230             // (it's an identity conversion)
231             // All other void conversions are disallowed.
232             if (source == typeof(void) || dest == typeof(void)) {
233                 return false;
234             }
235 
236             Type nnSourceType = TypeUtils.GetNonNullableType(source);
237             Type nnDestType = TypeUtils.GetNonNullableType(dest);
238 
239             // Down conversion
240             if (nnSourceType.IsAssignableFrom(nnDestType)) {
241                 return true;
242             }
243             // Up conversion
244             if (nnDestType.IsAssignableFrom(nnSourceType)) {
245                 return true;
246             }
247             // Interface conversion
248             if (source.IsInterface || dest.IsInterface) {
249                 return true;
250             }
251             // Variant delegate conversion
252             if (IsLegalExplicitVariantDelegateConversion(source, dest))
253                 return true;
254 
255             // Object conversion
256             if (source == typeof(object) || dest == typeof(object)) {
257                 return true;
258             }
259             return false;
260         }
261 
IsCovariant(Type t)262         private static bool IsCovariant(Type t)
263         {
264             Debug.Assert(t != null);
265             return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Covariant);
266         }
267 
IsContravariant(Type t)268         private static bool IsContravariant(Type t)
269         {
270             Debug.Assert(t != null);
271             return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Contravariant);
272         }
273 
IsInvariant(Type t)274         private static bool IsInvariant(Type t)
275         {
276             Debug.Assert(t != null);
277             return 0 == (t.GenericParameterAttributes & GenericParameterAttributes.VarianceMask);
278         }
279 
IsDelegate(Type t)280         private static bool IsDelegate(Type t)
281         {
282             Debug.Assert(t != null);
283             return t.IsSubclassOf(typeof(System.MulticastDelegate));
284         }
285 
IsLegalExplicitVariantDelegateConversion(Type source, Type dest)286         internal static bool IsLegalExplicitVariantDelegateConversion(Type source, Type dest)
287         {
288             Debug.Assert(source != null && dest != null);
289 
290             // There *might* be a legal conversion from a generic delegate type S to generic delegate type  T,
291             // provided all of the follow are true:
292             //   o Both types are constructed generic types of the same generic delegate type, D<X1,... Xk>.
293             //     That is, S = D<S1...>, T = D<T1...>.
294             //   o If type parameter Xi is declared to be invariant then Si must be identical to Ti.
295             //   o If type parameter Xi is declared to be covariant ("out") then Si must be convertible
296             //     to Ti via an identify conversion,  implicit reference conversion, or explicit reference conversion.
297             //   o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti,
298             //     or Si and Ti must both be reference types.
299 
300             if (!IsDelegate(source) || !IsDelegate(dest) || !source.IsGenericType || !dest.IsGenericType)
301                 return false;
302 
303             Type genericDelegate = source.GetGenericTypeDefinition();
304 
305             if (dest.GetGenericTypeDefinition() != genericDelegate)
306                 return false;
307 
308             Type[] genericParameters = genericDelegate.GetGenericArguments();
309             Type[] sourceArguments = source.GetGenericArguments();
310             Type[] destArguments = dest.GetGenericArguments();
311 
312             Debug.Assert(genericParameters != null);
313             Debug.Assert(sourceArguments != null);
314             Debug.Assert(destArguments != null);
315             Debug.Assert(genericParameters.Length == sourceArguments.Length);
316             Debug.Assert(genericParameters.Length == destArguments.Length);
317 
318             for (int iParam = 0; iParam < genericParameters.Length; ++iParam)
319             {
320                 Type sourceArgument = sourceArguments[iParam];
321                 Type destArgument = destArguments[iParam];
322 
323                 Debug.Assert(sourceArgument != null && destArgument != null);
324 
325                 // If the arguments are identical then this one is automatically good, so skip it.
326                 if (AreEquivalent(sourceArgument, destArgument))
327                 {
328                     continue;
329                 }
330 
331                 Type genericParameter = genericParameters[iParam];
332 
333                 Debug.Assert(genericParameter != null);
334 
335                 if (IsInvariant(genericParameter))
336                 {
337                     return false;
338                 }
339 
340                 if (IsCovariant(genericParameter))
341                 {
342                     if (!HasReferenceConversion(sourceArgument, destArgument))
343                     {
344                         return false;
345                     }
346                 }
347                 else if (IsContravariant(genericParameter))
348                 {
349                     if (sourceArgument.IsValueType || destArgument.IsValueType)
350                     {
351                         return false;
352                     }
353                 }
354             }
355             return true;
356         }
357 
IsConvertible(Type type)358         internal static bool IsConvertible(Type type) {
359             type = GetNonNullableType(type);
360             if (type.IsEnum) {
361                 return true;
362             }
363             switch (Type.GetTypeCode(type)) {
364                 case TypeCode.Boolean:
365                 case TypeCode.Byte:
366                 case TypeCode.SByte:
367                 case TypeCode.Int16:
368                 case TypeCode.Int32:
369                 case TypeCode.Int64:
370                 case TypeCode.UInt16:
371                 case TypeCode.UInt32:
372                 case TypeCode.UInt64:
373                 case TypeCode.Single:
374                 case TypeCode.Double:
375                 case TypeCode.Char:
376                     return true;
377                 default:
378                     return false;
379             }
380         }
381 
HasReferenceEquality(Type left, Type right)382         internal static bool HasReferenceEquality(Type left, Type right) {
383             if (left.IsValueType || right.IsValueType) {
384                 return false;
385             }
386 
387             // If we have an interface and a reference type then we can do
388             // reference equality.
389 
390             // If we have two reference types and one is assignable to the
391             // other then we can do reference equality.
392 
393             return left.IsInterface || right.IsInterface ||
394                 AreReferenceAssignable(left, right) ||
395                 AreReferenceAssignable(right, left);
396         }
397 
HasBuiltInEqualityOperator(Type left, Type right)398         internal static bool HasBuiltInEqualityOperator(Type left, Type right) {
399             // If we have an interface and a reference type then we can do
400             // reference equality.
401             if (left.IsInterface && !right.IsValueType) {
402                 return true;
403             }
404             if (right.IsInterface && !left.IsValueType) {
405                 return true;
406             }
407             // If we have two reference types and one is assignable to the
408             // other then we can do reference equality.
409             if (!left.IsValueType && !right.IsValueType) {
410                 if (AreReferenceAssignable(left, right) || AreReferenceAssignable(right, left)) {
411                     return true;
412                 }
413             }
414             // Otherwise, if the types are not the same then we definitely
415             // do not have a built-in equality operator.
416             if (!AreEquivalent(left, right)) {
417                 return false;
418             }
419             // We have two identical value types, modulo nullability.  (If they were both the
420             // same reference type then we would have returned true earlier.)
421             Debug.Assert(left.IsValueType);
422             // Equality between struct types is only defined for numerics, bools, enums,
423             // and their nullable equivalents.
424             Type nnType = GetNonNullableType(left);
425             if (nnType == typeof(bool) || IsNumeric(nnType) || nnType.IsEnum) {
426                 return true;
427             }
428             return false;
429         }
430 
IsImplicitlyConvertible(Type source, Type destination)431         internal static bool IsImplicitlyConvertible(Type source, Type destination) {
432             return AreEquivalent(source, destination) ||                // identity conversion
433                 IsImplicitNumericConversion(source, destination) ||
434                 IsImplicitReferenceConversion(source, destination) ||
435                 IsImplicitBoxingConversion(source, destination) ||
436                 IsImplicitNullableConversion(source, destination);
437         }
438 
439 
GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly)440         internal static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly) {
441             // check for implicit coercions first
442             Type nnExprType = TypeUtils.GetNonNullableType(convertFrom);
443             Type nnConvType = TypeUtils.GetNonNullableType(convertToType);
444             // try exact match on types
445             MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
446             MethodInfo method = FindConversionOperator(eMethods, convertFrom, convertToType, implicitOnly);
447             if (method != null) {
448                 return method;
449             }
450             MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
451             method = FindConversionOperator(cMethods, convertFrom, convertToType, implicitOnly);
452             if (method != null) {
453                 return method;
454             }
455             // try lifted conversion
456             if (!TypeUtils.AreEquivalent(nnExprType, convertFrom) ||
457                 !TypeUtils.AreEquivalent(nnConvType, convertToType)) {
458                 method = FindConversionOperator(eMethods, nnExprType, nnConvType, implicitOnly);
459                 if (method == null) {
460                     method = FindConversionOperator(cMethods, nnExprType, nnConvType, implicitOnly);
461                 }
462                 if (method != null) {
463                     return method;
464                 }
465             }
466             return null;
467         }
468 
FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo, bool implicitOnly)469         internal static MethodInfo FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo, bool implicitOnly) {
470             foreach (MethodInfo mi in methods) {
471                 if (mi.Name != "op_Implicit" && (implicitOnly || mi.Name != "op_Explicit")) {
472                     continue;
473                 }
474                 if (!TypeUtils.AreEquivalent(mi.ReturnType, typeTo)) {
475                     continue;
476                 }
477                 ParameterInfo[] pis = mi.GetParametersCached();
478                 if (!TypeUtils.AreEquivalent(pis[0].ParameterType, typeFrom)) {
479                     continue;
480                 }
481                 return mi;
482             }
483             return null;
484         }
485 
486         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
IsImplicitNumericConversion(Type source, Type destination)487         private static bool IsImplicitNumericConversion(Type source, Type destination) {
488             TypeCode tcSource = Type.GetTypeCode(source);
489             TypeCode tcDest = Type.GetTypeCode(destination);
490 
491             switch (tcSource) {
492                 case TypeCode.SByte:
493                     switch (tcDest) {
494                         case TypeCode.Int16:
495                         case TypeCode.Int32:
496                         case TypeCode.Int64:
497                         case TypeCode.Single:
498                         case TypeCode.Double:
499                         case TypeCode.Decimal:
500                             return true;
501                     }
502                     return false;
503                 case TypeCode.Byte:
504                     switch (tcDest) {
505                         case TypeCode.Int16:
506                         case TypeCode.UInt16:
507                         case TypeCode.Int32:
508                         case TypeCode.UInt32:
509                         case TypeCode.Int64:
510                         case TypeCode.UInt64:
511                         case TypeCode.Single:
512                         case TypeCode.Double:
513                         case TypeCode.Decimal:
514                             return true;
515                     }
516                     return false;
517                 case TypeCode.Int16:
518                     switch (tcDest) {
519                         case TypeCode.Int32:
520                         case TypeCode.Int64:
521                         case TypeCode.Single:
522                         case TypeCode.Double:
523                         case TypeCode.Decimal:
524                             return true;
525                     }
526                     return false;
527                 case TypeCode.UInt16:
528                     switch (tcDest) {
529                         case TypeCode.Int32:
530                         case TypeCode.UInt32:
531                         case TypeCode.Int64:
532                         case TypeCode.UInt64:
533                         case TypeCode.Single:
534                         case TypeCode.Double:
535                         case TypeCode.Decimal:
536                             return true;
537                     }
538                     return false;
539                 case TypeCode.Int32:
540                     switch (tcDest) {
541                         case TypeCode.Int64:
542                         case TypeCode.Single:
543                         case TypeCode.Double:
544                         case TypeCode.Decimal:
545                             return true;
546                     }
547                     return false;
548                 case TypeCode.UInt32:
549                     switch (tcDest) {
550                         case TypeCode.UInt32:
551                         case TypeCode.UInt64:
552                         case TypeCode.Single:
553                         case TypeCode.Double:
554                         case TypeCode.Decimal:
555                             return true;
556                     }
557                     return false;
558                 case TypeCode.Int64:
559                 case TypeCode.UInt64:
560                     switch (tcDest) {
561                         case TypeCode.Single:
562                         case TypeCode.Double:
563                         case TypeCode.Decimal:
564                             return true;
565                     }
566                     return false;
567                 case TypeCode.Char:
568                     switch (tcDest) {
569                         case TypeCode.UInt16:
570                         case TypeCode.Int32:
571                         case TypeCode.UInt32:
572                         case TypeCode.Int64:
573                         case TypeCode.UInt64:
574                         case TypeCode.Single:
575                         case TypeCode.Double:
576                         case TypeCode.Decimal:
577                             return true;
578                     }
579                     return false;
580                 case TypeCode.Single:
581                     return (tcDest == TypeCode.Double);
582             }
583             return false;
584         }
585 
IsImplicitReferenceConversion(Type source, Type destination)586         private static bool IsImplicitReferenceConversion(Type source, Type destination) {
587             return destination.IsAssignableFrom(source);
588         }
589 
IsImplicitBoxingConversion(Type source, Type destination)590         private static bool IsImplicitBoxingConversion(Type source, Type destination) {
591             if (source.IsValueType && (destination == typeof(object) || destination == typeof(System.ValueType)))
592                 return true;
593             if (source.IsEnum && destination == typeof(System.Enum))
594                 return true;
595             return false;
596         }
597 
IsImplicitNullableConversion(Type source, Type destination)598         private static bool IsImplicitNullableConversion(Type source, Type destination) {
599             if (IsNullableType(destination))
600                 return IsImplicitlyConvertible(GetNonNullableType(source), GetNonNullableType(destination));
601             return false;
602         }
603 
IsSameOrSubclass(Type type, Type subType)604         internal static bool IsSameOrSubclass(Type type, Type subType) {
605             return AreEquivalent(type, subType) || subType.IsSubclassOf(type);
606         }
607 
ValidateType(Type type)608         internal static void ValidateType(Type type) {
609             if (type.IsGenericTypeDefinition) {
610                 throw Error.TypeIsGeneric(type);
611             }
612             if (type.ContainsGenericParameters) {
613                 throw Error.TypeContainsGenericParameters(type);
614             }
615         }
616 
617         //from TypeHelper
FindGenericType(Type definition, Type type)618         internal static Type FindGenericType(Type definition, Type type) {
619             while (type != null && type != typeof(object)) {
620                 if (type.IsGenericType && AreEquivalent(type.GetGenericTypeDefinition(), definition)) {
621                     return type;
622                 }
623                 if (definition.IsInterface) {
624                     foreach (Type itype in type.GetInterfaces()) {
625                         Type found = FindGenericType(definition, itype);
626                         if (found != null)
627                             return found;
628                     }
629                 }
630                 type = type.BaseType;
631             }
632             return null;
633         }
634 
IsUnsigned(Type type)635         internal static bool IsUnsigned(Type type) {
636             type = GetNonNullableType(type);
637             switch (Type.GetTypeCode(type)) {
638                 case TypeCode.Byte:
639                 case TypeCode.UInt16:
640                 case TypeCode.Char:
641                 case TypeCode.UInt32:
642                 case TypeCode.UInt64:
643                     return true;
644                 default:
645                     return false;
646             }
647         }
648 
IsFloatingPoint(Type type)649         internal static bool IsFloatingPoint(Type type) {
650             type = GetNonNullableType(type);
651             switch (Type.GetTypeCode(type)) {
652                 case TypeCode.Single:
653                 case TypeCode.Double:
654                     return true;
655                 default:
656                     return false;
657             }
658         }
659 
660         /// <summary>
661         /// Searches for an operator method on the type. The method must have
662         /// the specified signature, no generic arguments, and have the
663         /// SpecialName bit set. Also searches inherited operator methods.
664         ///
665         /// NOTE: This was designed to satisfy the needs of op_True and
666         /// op_False, because we have to do runtime lookup for those. It may
667         /// not work right for unary operators in general.
668         /// </summary>
GetBooleanOperator(Type type, string name)669         internal static MethodInfo GetBooleanOperator(Type type, string name) {
670             do {
671                 MethodInfo result = type.GetMethodValidated(name, AnyStatic, null, new Type[] { type }, null);
672                 if (result != null && result.IsSpecialName && !result.ContainsGenericParameters) {
673                     return result;
674                 }
675                 type = type.BaseType;
676             } while (type != null);
677             return null;
678         }
679 
GetNonRefType(this Type type)680         internal static Type GetNonRefType(this Type type) {
681             return type.IsByRef ? type.GetElementType() : type;
682         }
683 
684         private static readonly Assembly _mscorlib = typeof(object).Assembly;
685         private static readonly Assembly _systemCore = typeof(Expression).Assembly;
686 
687         /// <summary>
688         /// We can cache references to types, as long as they aren't in
689         /// collectable assemblies. Unfortunately, we can't really distinguish
690         /// between different flavors of assemblies. But, we can at least
691         /// create a ---- for types in mscorlib (so we get the primitives)
692         /// and System.Core (so we find Func/Action overloads, etc).
693         /// </summary>
CanCache(this Type t)694         internal static bool CanCache(this Type t) {
695             // Note: we don't have to scan base or declaring types here.
696             // There's no way for a type in mscorlib to derive from or be
697             // contained in a type from another assembly. The only thing we
698             // need to look at is the generic arguments, which are the thing
699             // that allows mscorlib types to be specialized by types in other
700             // assemblies.
701 
702             var asm = t.Assembly;
703             if (asm != _mscorlib && asm != _systemCore) {
704                 // Not in mscorlib or our assembly
705                 return false;
706             }
707 
708             if (t.IsGenericType) {
709                 foreach (Type g in t.GetGenericArguments()) {
710                     if (!CanCache(g)) {
711                         return false;
712                     }
713                 }
714             }
715 
716             return true;
717         }
718     }
719 }
720