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 using System;
6 
7 using Internal.TypeSystem;
8 using Internal.TypeSystem.Ecma;
9 
10 using Debug = System.Diagnostics.Debug;
11 using TypeAttributes = System.Reflection.TypeAttributes;
12 using MethodAttributes = System.Reflection.MethodAttributes;
13 using FieldAttributes = System.Reflection.FieldAttributes;
14 
15 namespace ILCompiler
16 {
17     /// <summary>
18     /// Represents a metadata policy that blocks implementations details.
19     /// </summary>
20     public sealed class BlockedInternalsBlockingPolicy : MetadataBlockingPolicy
21     {
22         private enum ModuleBlockingMode
23         {
24             None,
25             BlockedInternals,
26             FullyBlocked,
27         }
28 
29         private class ModuleBlockingState
30         {
31             public ModuleDesc Module { get; }
32             public ModuleBlockingMode BlockingMode { get; }
ModuleBlockingState(ModuleDesc module, ModuleBlockingMode mode)33             public ModuleBlockingState(ModuleDesc module, ModuleBlockingMode mode)
34             {
35                 Module = module;
36                 BlockingMode = mode;
37             }
38         }
39 
40         private class BlockedModulesHashtable : LockFreeReaderHashtable<ModuleDesc, ModuleBlockingState>
41         {
42             protected override int GetKeyHashCode(ModuleDesc key) => key.GetHashCode();
43             protected override int GetValueHashCode(ModuleBlockingState value) => value.Module.GetHashCode();
CompareKeyToValue(ModuleDesc key, ModuleBlockingState value)44             protected override bool CompareKeyToValue(ModuleDesc key, ModuleBlockingState value) => Object.ReferenceEquals(key, value.Module);
CompareValueToValue(ModuleBlockingState value1, ModuleBlockingState value2)45             protected override bool CompareValueToValue(ModuleBlockingState value1, ModuleBlockingState value2) => Object.ReferenceEquals(value1.Module, value2.Module);
CreateValueFromKey(ModuleDesc module)46             protected override ModuleBlockingState CreateValueFromKey(ModuleDesc module)
47             {
48                 ModuleBlockingMode blockingMode = ModuleBlockingMode.None;
49 
50                 if (module.GetType("System.Runtime.CompilerServices", "__BlockAllReflectionAttribute", false) != null)
51                 {
52                     blockingMode = ModuleBlockingMode.FullyBlocked;
53                 }
54                 else if (module.GetType("System.Runtime.CompilerServices", "__BlockReflectionAttribute", false) != null)
55                 {
56                     blockingMode = ModuleBlockingMode.BlockedInternals;
57                 }
58 
59                 return new ModuleBlockingState(module, blockingMode);
60             }
61         }
62         private BlockedModulesHashtable _blockedModules = new BlockedModulesHashtable();
63 
64         private class BlockingState
65         {
66             public EcmaType Type { get; }
67             public bool IsBlocked { get; }
BlockingState(EcmaType type, bool isBlocked)68             public BlockingState(EcmaType type, bool isBlocked)
69             {
70                 Type = type;
71                 IsBlocked = isBlocked;
72             }
73         }
74 
75         private class BlockedTypeHashtable : LockFreeReaderHashtable<EcmaType, BlockingState>
76         {
77             private readonly BlockedModulesHashtable _blockedModules;
78 
BlockedTypeHashtable(BlockedModulesHashtable blockedModules)79             public BlockedTypeHashtable(BlockedModulesHashtable blockedModules)
80             {
81                 _blockedModules = blockedModules;
82             }
83 
84             protected override int GetKeyHashCode(EcmaType key) => key.GetHashCode();
85             protected override int GetValueHashCode(BlockingState value) => value.Type.GetHashCode();
CompareKeyToValue(EcmaType key, BlockingState value)86             protected override bool CompareKeyToValue(EcmaType key, BlockingState value) => Object.ReferenceEquals(key, value.Type);
CompareValueToValue(BlockingState value1, BlockingState value2)87             protected override bool CompareValueToValue(BlockingState value1, BlockingState value2) => Object.ReferenceEquals(value1.Type, value2.Type);
CreateValueFromKey(EcmaType type)88             protected override BlockingState CreateValueFromKey(EcmaType type)
89             {
90                 ModuleBlockingMode moduleBlockingMode = _blockedModules.GetOrCreateValue(type.EcmaModule).BlockingMode;
91                 bool isBlocked = ComputeIsBlocked(type, moduleBlockingMode);
92                 return new BlockingState(type, isBlocked);
93             }
94 
ComputeIsBlocked(EcmaType type, ModuleBlockingMode blockingMode)95             private bool ComputeIsBlocked(EcmaType type, ModuleBlockingMode blockingMode)
96             {
97                 // If no blocking is applied to the module, the type is not blocked
98                 if (blockingMode == ModuleBlockingMode.None)
99                     return false;
100 
101                 // <Module> type always gets metadata
102                 if (type.IsModuleType)
103                     return false;
104 
105                 // The various SR types used in Resource Manager always get metadata
106                 if (type.Name == "SR")
107                     return false;
108 
109                 // We block everything else if the module is blocked
110                 if (blockingMode == ModuleBlockingMode.FullyBlocked)
111                     return true;
112 
113                 var typeDefinition = type.MetadataReader.GetTypeDefinition(type.Handle);
114                 DefType containingType = type.ContainingType;
115                 if (containingType == null)
116                 {
117                     if ((typeDefinition.Attributes & TypeAttributes.Public) == 0)
118                     {
119                         return true;
120                     }
121                 }
122                 else
123                 {
124                     if ((typeDefinition.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic)
125                     {
126                         return ComputeIsBlocked((EcmaType)containingType, blockingMode);
127                     }
128                     else
129                     {
130                         return true;
131                     }
132                 }
133 
134                 return false;
135             }
136         }
137         private BlockedTypeHashtable _blockedTypes;
138 
139         private MetadataType _arrayOfTType;
InitializeArrayOfTType(TypeSystemEntity contextEntity)140         private MetadataType InitializeArrayOfTType(TypeSystemEntity contextEntity)
141         {
142             _arrayOfTType = contextEntity.Context.SystemModule.GetType("System", "Array`1");
143             return _arrayOfTType;
144         }
GetArrayOfTType(TypeSystemEntity contextEntity)145         private MetadataType GetArrayOfTType(TypeSystemEntity contextEntity)
146         {
147             if (_arrayOfTType != null)
148             {
149                 return _arrayOfTType;
150             }
151             return InitializeArrayOfTType(contextEntity);
152         }
153 
BlockedInternalsBlockingPolicy()154         public BlockedInternalsBlockingPolicy()
155         {
156             _blockedTypes = new BlockedTypeHashtable(_blockedModules);
157         }
158 
IsBlocked(MetadataType type)159         public override bool IsBlocked(MetadataType type)
160         {
161             Debug.Assert(type.IsTypeDefinition);
162 
163             var ecmaType = type as EcmaType;
164             if (ecmaType == null)
165                 return true;
166 
167             return _blockedTypes.GetOrCreateValue(ecmaType).IsBlocked;
168         }
169 
IsBlocked(MethodDesc method)170         public override bool IsBlocked(MethodDesc method)
171         {
172             Debug.Assert(method.IsTypicalMethodDefinition);
173 
174             var ecmaMethod = method as EcmaMethod;
175             if (ecmaMethod == null)
176                 return true;
177 
178             ModuleBlockingMode moduleBlockingMode = _blockedModules.GetOrCreateValue(ecmaMethod.Module).BlockingMode;
179             if (moduleBlockingMode == ModuleBlockingMode.None)
180                 return false;
181             else if (moduleBlockingMode == ModuleBlockingMode.FullyBlocked)
182                 return true;
183 
184             // We are blocking internal implementation details
185             Debug.Assert(moduleBlockingMode == ModuleBlockingMode.BlockedInternals);
186 
187             if (_blockedTypes.GetOrCreateValue((EcmaType)ecmaMethod.OwningType).IsBlocked)
188                 return true;
189 
190             if ((ecmaMethod.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
191                 return true;
192 
193             // Methods on Array`1<T> are implementation details that implement the generic interfaces on
194             // arrays. They should not generate metadata or be reflection invokable.
195             // We could get rid of this special casing two ways:
196             // * Make these method stop being regular EcmaMethods with Array<T> as their owning type, or
197             // * Make these methods implement the interfaces explicitly (they would become private and naturally blocked)
198             if (ecmaMethod.OwningType == GetArrayOfTType(ecmaMethod))
199                 return true;
200 
201             return false;
202         }
203 
IsBlocked(FieldDesc field)204         public override bool IsBlocked(FieldDesc field)
205         {
206             Debug.Assert(field.IsTypicalFieldDefinition);
207 
208             var ecmaField = field as EcmaField;
209             if (ecmaField == null)
210                 return true;
211 
212             ModuleBlockingMode moduleBlockingMode = _blockedModules.GetOrCreateValue(ecmaField.Module).BlockingMode;
213             if (moduleBlockingMode == ModuleBlockingMode.None)
214                 return false;
215             else if (moduleBlockingMode == ModuleBlockingMode.FullyBlocked)
216                 return true;
217 
218             // We are blocking internal implementation details
219             Debug.Assert(moduleBlockingMode == ModuleBlockingMode.BlockedInternals);
220 
221             if (_blockedTypes.GetOrCreateValue((EcmaType)ecmaField.OwningType).IsBlocked)
222                 return true;
223 
224             if ((ecmaField.Attributes & FieldAttributes.Public) != FieldAttributes.Public)
225                 return true;
226 
227             return false;
228         }
229     }
230 }
231