diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.TypeInit.cs b/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.TypeInit.cs index 8d96d30781c..76ebf89a7d0 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.TypeInit.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.TypeInit.cs @@ -41,56 +41,8 @@ public bool HasEagerStaticConstructor(TypeDesc type) private static bool HasEagerConstructorAttribute(TypeDesc type) { MetadataType mdType = type as MetadataType; - return mdType != null && ( - mdType.HasCustomAttribute("System.Runtime.CompilerServices", "EagerOrderedStaticConstructorAttribute") - || mdType.HasCustomAttribute("System.Runtime.CompilerServices", "EagerStaticClassConstructionAttribute")); - } - } - - public class EagerConstructorComparer : IComparer - { - private int GetConstructionOrder(MetadataType type) - { - // For EagerOrderedStaticConstructorAttribute, order is defined by an integer. - // For the other case (EagerStaticClassConstructionAttribute), order is defined - // implicitly. - - var decoded = ((EcmaType)type.GetTypeDefinition()).GetDecodedCustomAttribute( - "System.Runtime.CompilerServices", "EagerOrderedStaticConstructorAttribute"); - - if (decoded != null) - return (int)decoded.Value.FixedArguments[0].Value; - - Debug.Assert(type.HasCustomAttribute("System.Runtime.CompilerServices", "EagerStaticClassConstructionAttribute")); - // RhBind on .NET Native for UWP will sort these based on static dependencies of the .cctors. - // We could probably do the same, but this attribute is pretty much deprecated in favor of - // EagerOrderedStaticConstructorAttribute that has explicit order. The remaining uses of - // the unordered one don't appear to have dependencies, so sorting them all before the - // ordered ones should do. - return -1; - } - - public int Compare(DependencyAnalysis.IMethodNode x, DependencyAnalysis.IMethodNode y) - { - var typeX = (MetadataType)x.Method.OwningType; - var typeY = (MetadataType)y.Method.OwningType; - - int orderX = GetConstructionOrder(typeX); - int orderY = GetConstructionOrder(typeY); - - int result; - if (orderX != orderY) - { - result = Comparer.Default.Compare(orderX, orderY); - } - else - { - // Use type name as a tie breaker. We need this algorithm to produce stable - // ordering so that the sequence of eager cctors is deterministic. - result = String.Compare(typeX.GetFullName(), typeY.GetFullName(), StringComparison.Ordinal); - } - - return result; + return mdType != null && + mdType.HasCustomAttribute("System.Runtime.CompilerServices", "EagerStaticClassConstructionAttribute"); } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 86491b46433..07656053216 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -672,7 +672,7 @@ public string GetSymbolAlternateName(ISymbolNode node) public ArrayOfEmbeddedPointersNode EagerCctorTable = new ArrayOfEmbeddedPointersNode( "__EagerCctorStart", "__EagerCctorEnd", - new EagerConstructorComparer()); + null); public ArrayOfEmbeddedPointersNode DispatchMapTable = new ArrayOfEmbeddedPointersNode( "__DispatchMapTableStart", diff --git a/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs b/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs new file mode 100644 index 00000000000..eb3a3c07b58 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +using AssemblyName = System.Reflection.AssemblyName; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Encapsulates a list of class constructors that must be run in a prescribed order during start-up + /// + public sealed class LibraryInitializers + { + private const string ClassLibraryPlaceHolderString = "*ClassLibrary*"; + private const string LibraryInitializerContainerNamespaceName = "Internal.Runtime.CompilerHelpers"; + private const string LibraryInitializerContainerTypeName = "LibraryInitializer"; + private const string LibraryInitializerMethodName = "InitializeLibrary"; + + private static readonly LibraryInitializerInfo[] s_assembliesWithLibraryInitializers = + { + new LibraryInitializerInfo(ClassLibraryPlaceHolderString), + new LibraryInitializerInfo("System.Private.TypeLoader", false), + new LibraryInitializerInfo("System.Private.Reflection.Execution", false), + new LibraryInitializerInfo("System.Private.DeveloperExperience.Console"), + }; + + private List _libraryInitializerMethods; + + private readonly TypeSystemContext _context; + private readonly bool _isCppCodeGen; + + public LibraryInitializers(TypeSystemContext context, bool isCppCodeGen) + { + _context = context; + // + // We should not care which code-gen is being used, however currently CppCodeGen cannot + // handle code pulled in by all explicit cctors. + // + // See https://github.com/dotnet/corert/issues/2486 + // + _isCppCodeGen = isCppCodeGen; + } + + public IList LibraryInitializerMethods + { + get + { + if (_libraryInitializerMethods == null) + InitLibraryInitializers(); + + return _libraryInitializerMethods; + } + } + + private void InitLibraryInitializers() + { + Debug.Assert(_libraryInitializerMethods == null); + + _libraryInitializerMethods = new List(); + + foreach (var entry in s_assembliesWithLibraryInitializers) + { + if (_isCppCodeGen && !entry.UseWithCppCodeGen) + continue; + + ModuleDesc assembly = entry.Assembly == ClassLibraryPlaceHolderString + ? _context.SystemModule + : _context.ResolveAssembly(new AssemblyName(entry.Assembly), false); + + if (assembly == null) + continue; + + TypeDesc containingType = assembly.GetType(LibraryInitializerContainerNamespaceName, LibraryInitializerContainerTypeName, false); + if (containingType == null) + continue; + + MethodDesc initializerMethod = containingType.GetMethod(LibraryInitializerMethodName, null); + if (initializerMethod == null) + continue; + + _libraryInitializerMethods.Add(initializerMethod); + } + } + + private sealed class LibraryInitializerInfo + { + public string Assembly { get; } + public bool UseWithCppCodeGen { get; } + + public LibraryInitializerInfo(string assembly, bool useWithCppCodeGen = true) + { + Assembly = assembly; + UseWithCppCodeGen = useWithCppCodeGen; + } + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/MainMethodRootProvider.cs b/src/ILCompiler.Compiler/src/Compiler/MainMethodRootProvider.cs index ebcfc24e7e9..4df659ad832 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MainMethodRootProvider.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MainMethodRootProvider.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -21,10 +22,12 @@ public class MainMethodRootProvider : ICompilationRootProvider public const string ManagedEntryPointMethodName = "__managed__Main"; private EcmaModule _module; + private IList _libraryInitializers; - public MainMethodRootProvider(EcmaModule module) + public MainMethodRootProvider(EcmaModule module, IList libraryInitializers) { _module = module; + _libraryInitializers = libraryInitializers; } public void AddCompilationRoots(IRootingServiceProvider rootProvider) @@ -34,7 +37,7 @@ public void AddCompilationRoots(IRootingServiceProvider rootProvider) throw new Exception("No managed entrypoint defined for executable module"); TypeDesc owningType = _module.GetGlobalModuleType(); - var startupCodeMain = new StartupCodeMainMethod(owningType, mainMethod); + var startupCodeMain = new StartupCodeMainMethod(owningType, mainMethod, _libraryInitializers); rootProvider.AddCompilationRoot(startupCodeMain, "Startup Code Main Method", ManagedEntryPointMethodName); } diff --git a/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs b/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs index c9bdff5c973..f4fc041f711 100644 --- a/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs +++ b/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs @@ -378,20 +378,6 @@ public void CompileMethod(CppMethodCodeNode methodCodeNodeNeedingCode) if (methodIL == null) return; - // TODO: Remove this code once CppCodegen is able to generate code for the reflection startup path. - // The startup path runs before any user code is executed. - // For now we replace the startup path with a simple "ret". Reflection won't work, but - // programs not using reflection will. - if (method.Name == ".cctor") - { - MetadataType owningType = method.OwningType as MetadataType; - if (owningType != null && - owningType.Name == "ReflectionExecution" && owningType.Namespace == "Internal.Reflection.Execution") - { - methodIL = new Internal.IL.Stubs.ILStubMethodIL(method, new byte[] { (byte)ILOpcode.ret }, Array.Empty(), null); - } - } - try { // TODO: hacky special-case diff --git a/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs b/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs index afb38ad6c04..251dd3af3f2 100644 --- a/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs +++ b/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Internal.TypeSystem; @@ -20,11 +21,13 @@ public sealed class StartupCodeMainMethod : ILStubMethod private TypeDesc _owningType; private MainMethodWrapper _mainMethod; private MethodSignature _signature; + private IList _libraryInitializers; - public StartupCodeMainMethod(TypeDesc owningType, MethodDesc mainMethod) + public StartupCodeMainMethod(TypeDesc owningType, MethodDesc mainMethod, IList libraryInitializers) { _owningType = owningType; _mainMethod = new MainMethodWrapper(owningType, mainMethod); + _libraryInitializers = libraryInitializers; } public override TypeSystemContext Context @@ -56,14 +59,15 @@ public override MethodIL EmitIL() ILEmitter emitter = new ILEmitter(); ILCodeStream codeStream = emitter.NewCodeStream(); - ModuleDesc developerExperience = Context.ResolveAssembly(new AssemblyName("System.Private.DeveloperExperience.Console"), false); - if (developerExperience != null) + // Allow the class library to run explicitly ordered class constructors first thing in start-up. + if (_libraryInitializers != null) { - TypeDesc connectorType = developerExperience.GetKnownType("Internal.DeveloperExperience", "DeveloperExperienceConnectorConsole"); - MethodDesc initializeMethod = connectorType.GetKnownMethod("Initialize", null); - codeStream.Emit(ILOpcode.call, emitter.NewToken(initializeMethod)); + foreach (MethodDesc method in _libraryInitializers) + { + codeStream.Emit(ILOpcode.call, emitter.NewToken(method)); + } } - + MetadataType startup = Context.GetHelperType("StartupCodeHelpers"); // Initialize command line args if the class library supports this diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 10c68b33330..59c6ad2d6ff 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -90,6 +90,7 @@ JitInterface\JitConfigProvider.cs + diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs index 04e265623ef..16793a5ac85 100644 --- a/src/ILCompiler/src/Program.cs +++ b/src/ILCompiler/src/Program.cs @@ -209,7 +209,9 @@ private int Run(string[] args) if (entrypointModule != null) { - compilationRoots.Add(new MainMethodRootProvider(entrypointModule)); + LibraryInitializers libraryInitializers = + new LibraryInitializers(typeSystemContext, _isCppCodegen); + compilationRoots.Add(new MainMethodRootProvider(entrypointModule, libraryInitializers.LibraryInitializerMethods)); } if (_multiFile) @@ -240,20 +242,6 @@ private int Run(string[] args) compilationRoots.Add(new ExportedMethodsRootProvider((EcmaModule)typeSystemContext.SystemModule)); - // System.Private.Reflection.Execution needs to establish a communication channel with System.Private.CoreLib - // at process startup. This is done through an eager constructor that calls into CoreLib and passes it - // a callback object. - // - // Since CoreLib cannot reference anything, the type and it's eager constructor won't be added to the compilation - // unless we explictly add it. - - var refExec = typeSystemContext.GetModuleForSimpleName("System.Private.Reflection.Execution", false); - if (refExec != null) - { - var exec = refExec.GetType("Internal.Reflection.Execution", "ReflectionExecution"); - compilationRoots.Add(new SingleMethodRootProvider(exec.GetStaticConstructor())); - } - compilationGroup = new SingleFileCompilationModuleGroup(typeSystemContext); } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 00000000000..e2a2b318bc7 --- /dev/null +++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// Container class to run specific class constructors in a defined order. Since we can't + /// directly invoke class constructors in C#, they're renamed Initialize. + /// + internal class LibraryInitializer + { + public static void InitializeLibrary() + { + PreallocatedOutOfMemoryException.Initialize(); + ClassConstructorRunner.Initialize(); + TypeLoaderExports.Initialize(); + } + } +} diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index d5b115c32c6..15ea8ef1167 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -63,6 +63,7 @@ + @@ -1183,4 +1184,4 @@ - + \ No newline at end of file diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs index b0ec3c9f9b9..1b917fb946c 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs @@ -13,7 +13,7 @@ namespace System.Runtime.CompilerServices { - // Marked [EagerStaticClassConstruction] because Cctor.GetCctor + // Marked [EagerOrderedStaticConstructor] because Cctor.GetCctor // uses _cctorGlobalLock [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.CompilerServicesClassConstructorRunner)] internal static partial class ClassConstructorRunner @@ -262,10 +262,8 @@ private static bool DeadlockAwareAcquire(CctorHandle cctor, IntPtr pfnCctor) //============================================================================================================== // These structs are allocated on demand whenever the runtime tries to run a class constructor. Once the // the class constructor has been successfully initialized, we reclaim this structure. The structure is long- - // lived only if the class constructor threw an exception. This must be marked [EagerStaticClassConstruction] to - // avoid infinite mutual recursion in GetCctor. + // lived only if the class constructor threw an exception. //============================================================================================================== - [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.CompilerServicesClassConstructorRunnerCctor)] private unsafe struct Cctor { public Lock Lock; @@ -273,14 +271,7 @@ private unsafe struct Cctor public int HoldingThread; private int _refCount; private StaticClassConstructionContext* _pContext; - - // Because Cctor's are mutable structs, we have to give our callers raw references to the underlying arrays - // for this collection to be usable. This also means once we place a Cctor in an array, we can't grow or - // reallocate the array. - private static Cctor[][] s_cctorArrays = new Cctor[10][]; - private static int s_cctorArraysCount = 0; - private static int s_count; - + //========================================================================================================== // Gets the Cctor entry associated with a specific class constructor context (creating it if necessary.) //========================================================================================================== @@ -479,8 +470,35 @@ public static CctorHandle GetCctorThatThreadIsBlockedOn(int managedThreadId) private static int s_nextBlockingRecordIndex; } - private static Lock s_cctorGlobalLock = new Lock(); + private static Lock s_cctorGlobalLock; + + // These three statics are used by ClassConstructorRunner.Cctor but moved out to avoid an unnecessary + // extra class constructor call. + // + // Because Cctor's are mutable structs, we have to give our callers raw references to the underlying arrays + // for this collection to be usable. This also means once we place a Cctor in an array, we can't grow or + // reallocate the array. + private static Cctor[][] s_cctorArrays; + private static int s_cctorArraysCount; + private static int s_count; + // + // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor + // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead + // added to Initialize. + // +#if !CORERT + static ClassConstructorRunner() + { + Initialize(); + } +#endif + internal static void Initialize() + { + s_cctorArrays = new Cctor[10][]; + s_cctorGlobalLock = new Lock(); + } + [Conditional("ENABLE_NOISY_CCTOR_LOG")] private static void NoisyLog(string format, IntPtr cctorMethod, int threadId) { diff --git a/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs b/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs index 7f8cf556e26..57e8efd7992 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs @@ -73,12 +73,31 @@ private class Entry // Initialize the cache eagerly to avoid null checks. // Use array with just single element to make this pay-for-play. The actual cache will be allocated only // once the lazy lookups are actually needed. - private static Entry[] s_cache = new Entry[1]; + private static Entry[] s_cache; private static Lock s_lock; private static GCHandle s_previousCache; - private volatile static IntPtr[] s_resolutionFunctionPointers = new IntPtr[4]; - private static int s_nextResolutionFunctionPointerIndex = (int)SignatureKind.Count; + private volatile static IntPtr[] s_resolutionFunctionPointers; + private static int s_nextResolutionFunctionPointerIndex; + + // + // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor + // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead + // added to Initialize. + // +#if !CORERT + static TypeLoaderExports() + { + Initialize(); + } +#endif + + internal static void Initialize() + { + s_cache = new Entry[1]; + s_resolutionFunctionPointers = new IntPtr[4]; + s_nextResolutionFunctionPointerIndex = (int)SignatureKind.Count; + } [RuntimeExport("GenericLookup")] public static IntPtr GenericLookup(IntPtr context, IntPtr signature) diff --git a/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs index 5c3945636ee..c53f1a2512d 100644 --- a/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -17,7 +17,24 @@ namespace System [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.SystemPreallocatedOutOfMemoryException)] internal class PreallocatedOutOfMemoryException { - public static readonly OutOfMemoryException Instance = new OutOfMemoryException(message: null); // Cannot call the nullary constructor as that triggers non-trivial resource manager logic. + public static OutOfMemoryException Instance { get; private set; } + + // + // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor + // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead + // added to Initialize. + // +#if !CORERT + static PreallocatedOutOfMemoryException() + { + Initialize(); + } +#endif + + internal static void Initialize() + { + Instance = new OutOfMemoryException(message: null); // Cannot call the nullary constructor as that triggers non-trivial resource manager logic. + } } public class RuntimeExceptionHelpers diff --git a/src/System.Private.DeveloperExperience.Console/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/System.Private.DeveloperExperience.Console/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 00000000000..68af06c7fc6 --- /dev/null +++ b/src/System.Private.DeveloperExperience.Console/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using Internal.DeveloperExperience; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + DeveloperExperienceConnectorConsole.Initialize(); + } + } +} diff --git a/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj b/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj index 5cf51966529..5a748e1b428 100644 --- a/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj +++ b/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj @@ -33,6 +33,7 @@ + diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs index 453dc7637d6..5d287ca1c6b 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs @@ -38,12 +38,24 @@ namespace Internal.Reflection.Execution [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.ReflectionExecution)] public static class ReflectionExecution { + // + // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor + // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead + // added to Initialize. + // +#if !CORERT /// /// This eager constructor initializes runtime reflection support. As part of ExecutionEnvironmentImplementation /// initialization it enumerates the modules and registers the ones containing EmbeddedMetadata reflection blobs /// in its _moduleToMetadataReader map. /// static ReflectionExecution() + { + Initialize(); + } +#endif + + internal static void Initialize() { // Initialize Reflection.Core's one and only ExecutionDomain. ExecutionEnvironmentImplementation executionEnvironment = new ExecutionEnvironmentImplementation(); @@ -85,7 +97,7 @@ public static Type ExtensibleGetType(string typeName, string callingAssemblyName return ReflectionCoreExecution.ExecutionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, defaultAssemblies); } - internal static ExecutionEnvironmentImplementation ExecutionEnvironment { get; } + internal static ExecutionEnvironmentImplementation ExecutionEnvironment { get; private set; } internal static IList DefaultAssemblyNamesForGetType; } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/System.Private.Reflection.Execution/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 00000000000..9932db2d191 --- /dev/null +++ b/src/System.Private.Reflection.Execution/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using Internal.Reflection.Execution; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + ReflectionExecution.Initialize(); + } + } +} diff --git a/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj b/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj index 4939f385392..20a4da52049 100644 --- a/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj +++ b/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj @@ -90,6 +90,7 @@ + diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 00000000000..5b7964f8a0b --- /dev/null +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using Internal.Runtime.TypeLoader; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + TypeLoaderEnvironment.Initialize(); + } + } +} diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs index c1acd5517db..777352910b6 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs @@ -83,7 +83,7 @@ private static unsafe string GetStringFromMemoryInNativeFormat(IntPtr pointerToD return Encoding.UTF8.GetString(dataStream, checked((int)stringLen)); } - private static LowLevelDictionary s_nativeFormatStrings = new LowLevelDictionary(); + private static LowLevelDictionary s_nativeFormatStrings; /// /// From a string, get a pointer to an allocated memory location that holds a NativeFormat encoded string. diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs index 64bdbed2c64..9f1b828be08 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs @@ -37,7 +37,7 @@ private class NamedTypeLookupResult private NamedTypeRuntimeTypeHandleToMetadataHashtable _runtimeTypeHandleToMetadataHashtable = new NamedTypeRuntimeTypeHandleToMetadataHashtable(); - public static readonly IntPtr NoStaticsData = (IntPtr)1; + public static IntPtr NoStaticsData { get; private set; } private class NamedTypeRuntimeTypeHandleToMetadataHashtable : LockFreeReaderHashtable { diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs index b8d8deb11fe..b6309bd221b 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs @@ -92,7 +92,7 @@ public sealed partial class TypeLoaderEnvironment [ThreadStatic] private static bool t_isReentrant; - public static readonly TypeLoaderEnvironment Instance; + public static TypeLoaderEnvironment Instance { get; private set; } /// /// List of loaded binary modules is typically used to locate / process various metadata blobs @@ -107,10 +107,24 @@ public sealed partial class TypeLoaderEnvironment [ThreadStatic] private static LowLevelDictionary t_moduleNativeReaders; + // + // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor + // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead + // added to Initialize. + // +#if !CORERT static TypeLoaderEnvironment() + { + Initialize(); + } +#endif + + internal static void Initialize() { Instance = new TypeLoaderEnvironment(); RuntimeAugments.InitializeLookups(new Callbacks()); + s_nativeFormatStrings = new LowLevelDictionary(); + NoStaticsData = (IntPtr)1; } public TypeLoaderEnvironment() diff --git a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index d1c371050b3..58f967904ad 100644 --- a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -27,7 +27,6 @@ - @@ -264,6 +263,7 @@ Internal\TypeSystem\WellKnownType.cs +