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