diff options
author | Michal Strehovský <MichalStrehovsky@users.noreply.github.com> | 2017-08-31 04:05:28 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-31 04:05:28 +0300 |
commit | 2b40a707239a9974a63cf992ce6a6f8303f5b362 (patch) | |
tree | f2499c94260c3c02c368aaade7885db361a89d1c /src/System.Private.TypeLoader | |
parent | 47f76ea83121b629abeccf152841516d66ce9b42 (diff) | |
parent | 81a8e55b2e32c0a27a17e1ba67664c593a14810b (diff) |
Merge pull request #4370 from dotnet/nmirror
Merge nmirror to master
Diffstat (limited to 'src/System.Private.TypeLoader')
6 files changed, 307 insertions, 14 deletions
diff --git a/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs b/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs index 6f926d7a2..c6342da01 100644 --- a/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs +++ b/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs @@ -86,14 +86,14 @@ namespace Internal.Reflection.Execution } } - partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, ref AssemblyBindResult result, ref Exception exception, ref bool foundMatch) + partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, ref AssemblyBindResult result, ref Exception exception, ref Exception preferredException, ref bool foundMatch) { lock(s_ecmaLoadedAssemblies) { for (int i = 0; i < s_ecmaLoadedAssemblies.Count; i++) { PEInfo info = s_ecmaLoadedAssemblies[i]; - if (AssemblyNameMatches(refName, info.Name)) + if (AssemblyNameMatches(refName, info.Name, ref preferredException)) { if (foundMatch) { @@ -146,7 +146,7 @@ namespace Internal.Reflection.Execution RuntimeAssemblyName runtimeAssemblyName = reader.GetAssemblyDefinition().ToRuntimeAssemblyName(reader).CanonicalizePublicKeyToken(); // If assembly name doesn't match, it isn't the one we're looking for. Continue to look for more assemblies - if (!AssemblyNameMatches(refName, runtimeAssemblyName)) + if (!AssemblyNameMatches(refName, runtimeAssemblyName, ref preferredException)) continue; // This is the one we are looking for, add it to the list of loaded assemblies diff --git a/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs b/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs index e17a26c13..701160534 100644 --- a/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs +++ b/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs @@ -35,7 +35,7 @@ namespace Internal.Reflection.Execution public static AssemblyBinderImplementation Instance { get; } = new AssemblyBinderImplementation(); partial void BindEcmaByteArray(byte[] rawAssembly, byte[] rawSymbolStore, ref AssemblyBindResult bindResult, ref Exception exception, ref bool? result); - partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, ref AssemblyBindResult result, ref Exception exception, ref bool resultBoolean); + partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, ref AssemblyBindResult result, ref Exception exception, ref Exception preferredException, ref bool resultBoolean); partial void InsertEcmaLoadedAssemblies(List<AssemblyBindResult> loadedAssemblies); public sealed override bool Bind(byte[] rawAssembly, byte[] rawSymbolStore, out AssemblyBindResult bindResult, out Exception exception) @@ -59,6 +59,8 @@ namespace Internal.Reflection.Execution result = default(AssemblyBindResult); exception = null; + Exception preferredException = null; + refName = refName.CanonicalizePublicKeyToken(); // At least one real-world app calls Type.GetType() for "char" using the assembly name "mscorlib". To accomodate this, @@ -80,7 +82,7 @@ namespace Internal.Reflection.Execution } else { - nameMatches = AssemblyNameMatches(refName, group.Key); + nameMatches = AssemblyNameMatches(refName, group.Key, ref preferredException); } if (nameMatches) @@ -100,13 +102,13 @@ namespace Internal.Reflection.Execution } } - BindEcmaAssemblyName(refName, ref result, ref exception, ref foundMatch); + BindEcmaAssemblyName(refName, ref result, ref exception, ref preferredException, ref foundMatch); if (exception != null) return false; if (!foundMatch) { - exception = new IOException(SR.Format(SR.FileNotFound_AssemblyNotFound, refName.FullName)); + exception = preferredException ?? new FileNotFoundException(SR.Format(SR.FileNotFound_AssemblyNotFound, refName.FullName)); return false; } @@ -159,7 +161,7 @@ namespace Internal.Reflection.Execution // // Encapsulates the assembly ref->def matching policy. // - private bool AssemblyNameMatches(RuntimeAssemblyName refName, RuntimeAssemblyName defName) + private bool AssemblyNameMatches(RuntimeAssemblyName refName, RuntimeAssemblyName defName, ref Exception preferredException) { // // The defName came from trusted metadata so it should be fully specified. @@ -175,9 +177,11 @@ namespace Internal.Reflection.Execution if (refName.Version != null) { - int compareResult = refName.Version.CompareTo(defName.Version); - if (compareResult > 0) + if (!AssemblyVersionMatches(refVersion: refName.Version, defVersion: defName.Version)) + { + preferredException = new FileLoadException(SR.Format(SR.FileLoadException_RefDefMismatch, refName.FullName, defName.Version, refName.Version)); return false; + } } if (refName.CultureName != null) @@ -206,6 +210,33 @@ namespace Internal.Reflection.Execution return true; } + private static bool AssemblyVersionMatches(Version refVersion, Version defVersion) + { + if (defVersion.Major < refVersion.Major) + return false; + if (defVersion.Major > refVersion.Major) + return true; + + if (defVersion.Minor < refVersion.Minor) + return false; + if (defVersion.Minor > refVersion.Minor) + return true; + + if (refVersion.Build == -1) + return true; + if (defVersion.Build < refVersion.Build) + return false; + if (defVersion.Build > refVersion.Build) + return true; + + if (refVersion.Revision == -1) + return true; + if (defVersion.Revision < refVersion.Revision) + return false; + + return true; + } + /// <summary> /// This callback gets called whenever a module gets registered. It adds the metadata reader /// for the new module to the available scopes. The lock in ExecutionEnvironmentImplementation ensures diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs index a1da52d2c..a23b0453b 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs @@ -417,7 +417,7 @@ namespace Internal.Runtime.TypeLoader internal void ParseNativeLayoutInfo(InstantiatedMethod method) { - TypeLoaderLogger.WriteLine("Parsing NativeLayoutInfo for method " + ToString() + " ..."); + TypeLoaderLogger.WriteLine("Parsing NativeLayoutInfo for method " + method.ToString() + " ..."); Debug.Assert(method.Dictionary == null); @@ -1867,6 +1867,103 @@ namespace Internal.Runtime.TypeLoader } } + // + // This method is used to build the floating portion of a generic dictionary. + // + private unsafe IntPtr BuildFloatingDictionary(TypeSystemContext typeSystemContext, IntPtr context, bool isTypeContext, IntPtr fixedDictionary, out bool isNewlyAllocatedDictionary) + { + isNewlyAllocatedDictionary = true; + + NativeParser nativeLayoutParser; + NativeLayoutInfoLoadContext nlilContext; + + if (isTypeContext) + { + TypeDesc typeContext = typeSystemContext.ResolveRuntimeTypeHandle(*(RuntimeTypeHandle*)&context); + + TypeLoaderLogger.WriteLine("Building floating dictionary layout for type " + typeContext.ToString() + "..."); + + // We should only perform updates to floating dictionaries for types that share normal canonical code + Debug.Assert(typeContext.CanShareNormalGenericCode()); + + // Computing the template will throw if no template is found. + typeContext.ComputeTemplate(); + + TypeBuilderState state = typeContext.GetOrCreateTypeBuilderState(); + nativeLayoutParser = state.GetParserForNativeLayoutInfo(); + nlilContext = state.NativeLayoutInfo.LoadContext; + } + else + { + RuntimeTypeHandle declaringTypeHandle; + MethodNameAndSignature nameAndSignature; + RuntimeTypeHandle[] genericMethodArgHandles; + bool success = TypeLoaderEnvironment.Instance.TryGetGenericMethodComponents(context, out declaringTypeHandle, out nameAndSignature, out genericMethodArgHandles); + Debug.Assert(success); + + DefType declaringType = (DefType)typeSystemContext.ResolveRuntimeTypeHandle(declaringTypeHandle); + InstantiatedMethod methodContext = (InstantiatedMethod)typeSystemContext.ResolveGenericMethodInstantiation( + false, + declaringType, + nameAndSignature, + typeSystemContext.ResolveRuntimeTypeHandles(genericMethodArgHandles), + IntPtr.Zero, + false); + + TypeLoaderLogger.WriteLine("Building floating dictionary layout for method " + methodContext.ToString() + "..."); + + // We should only perform updates to floating dictionaries for gemeric methods that share normal canonical code + Debug.Assert(!methodContext.IsNonSharableMethod); + + uint nativeLayoutInfoToken; + NativeFormatModuleInfo nativeLayoutModule; + MethodDesc templateMethod = (new TemplateLocator()).TryGetGenericMethodTemplate(methodContext, out nativeLayoutModule, out nativeLayoutInfoToken); + if (templateMethod == null) + throw new TypeBuilder.MissingTemplateException(); + + NativeReader nativeLayoutInfoReader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(nativeLayoutModule.Handle); + + nativeLayoutParser = new NativeParser(nativeLayoutInfoReader, nativeLayoutInfoToken); + nlilContext = new NativeLayoutInfoLoadContext + { + _typeSystemContext = methodContext.Context, + _typeArgumentHandles = methodContext.OwningType.Instantiation, + _methodArgumentHandles = methodContext.Instantiation, + _module = nativeLayoutModule + }; + } + + NativeParser dictionaryLayoutParser = nativeLayoutParser.GetParserForBagElementKind(BagElementKind.DictionaryLayout); + if (dictionaryLayoutParser.IsNull) + return IntPtr.Zero; + + int floatingVersionCellIndex, floatingVersionInLayout; + GenericDictionaryCell[] floatingCells = GenericDictionaryCell.BuildFloatingDictionary(this, nlilContext, dictionaryLayoutParser, out floatingVersionCellIndex, out floatingVersionInLayout); + if (floatingCells == null) + return IntPtr.Zero; + + // First, check if the current version of the existing floating dictionary matches the version in the native layout. If so, there + // is no need to allocate anything new, and we can just use the existing statically compiled floating portion of the input dictionary. + int currentFloatingVersion = (int)(((IntPtr*)fixedDictionary)[floatingVersionCellIndex]); + if(currentFloatingVersion == floatingVersionInLayout) + { + isNewlyAllocatedDictionary = false; + return fixedDictionary + IntPtr.Size * floatingVersionCellIndex; + } + + GenericTypeDictionary floatingDict = new GenericTypeDictionary(floatingCells); + + IntPtr result = floatingDict.Allocate(); + + ProcessTypesNeedingPreparation(); + + FinishTypeAndMethodBuilding(); + + floatingDict.Finish(this); + + return result; + } + public static bool TryBuildGenericType(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle) { Debug.Assert(!genericTypeDefinitionHandle.IsNull() && genericTypeArgumentHandles != null && genericTypeArgumentHandles.Length > 0); @@ -2142,5 +2239,32 @@ namespace Internal.Runtime.TypeLoader return success; } + + // + // This method is used to build the floating portion of a generic dictionary. + // + internal static IntPtr TryBuildFloatingDictionary(IntPtr context, bool isTypeContext, IntPtr fixedDictionary, out bool isNewlyAllocatedDictionary) + { + isNewlyAllocatedDictionary = true; + + try + { + TypeSystemContext typeSystemContext = TypeSystemContextFactory.Create(); + + IntPtr ret = new TypeBuilder().BuildFloatingDictionary(typeSystemContext, context, isTypeContext, fixedDictionary, out isNewlyAllocatedDictionary); + + TypeSystemContextFactory.Recycle(typeSystemContext); + + return ret; + } + catch (MissingTemplateException e) + { + // This should not ever happen. The static compiler should ensure that the templates are always + // available for types and methods that have floating dictionaries + Environment.FailFast("MissingTemplateException thrown during dictionary update", e); + + return IntPtr.Zero; + } + } } } 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 e4a05ff8b..b3b39230e 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs @@ -86,6 +86,11 @@ namespace Internal.Runtime.TypeLoader return TypeLoaderEnvironment.Instance.TryGetArrayTypeForElementType(elementTypeHandle, isMdArray, rank, out arrayTypeHandle); } + public override IntPtr UpdateFloatingDictionary(IntPtr context, IntPtr dictionaryPtr) + { + return TypeLoaderEnvironment.Instance.UpdateFloatingDictionary(context, dictionaryPtr); + } + /// <summary> /// Register a new runtime-allocated code thunk in the diagnostic stream. /// </summary> @@ -513,6 +518,77 @@ namespace Internal.Runtime.TypeLoader } } + public unsafe IntPtr UpdateFloatingDictionary(IntPtr context, IntPtr dictionaryPtr) + { + IntPtr newFloatingDictionary; + bool isNewlyAllocatedDictionary; + bool isTypeContext = context != dictionaryPtr; + + if (isTypeContext) + { + EEType* pEEType = (EEType*)context.ToPointer(); + IntPtr curDictPtr = EETypeCreator.GetDictionary(pEEType); + + // Look for the exact base type that owns the dictionary. We may be having + // a virtual method run on a derived type and the generic lookup are performed + // on the base type's dictionary. + while (curDictPtr != dictionaryPtr) + { + pEEType = pEEType->BaseType; + Debug.Assert(pEEType != null); + curDictPtr = EETypeCreator.GetDictionary(pEEType); + Debug.Assert(curDictPtr != IntPtr.Zero); + } + + context = (IntPtr)pEEType; + } + + using (LockHolder.Hold(_typeLoaderLock)) + { + // Check if some other thread already allocated a floating dictionary and updated the fixed portion + if(*(IntPtr*)dictionaryPtr != IntPtr.Zero) + return *(IntPtr*)dictionaryPtr; + + try + { + if (t_isReentrant) + Environment.FailFast("Reentrant update to floating dictionary"); + t_isReentrant = true; + + newFloatingDictionary = TypeBuilder.TryBuildFloatingDictionary(context, isTypeContext, dictionaryPtr, out isNewlyAllocatedDictionary); + + t_isReentrant = false; + } + catch + { + // Catch and rethrow any exceptions instead of using finally block. Otherwise, filters that are run during + // the first pass of exception unwind may hit the re-entrancy fail fast above. + + // TODO: Convert this to filter for better diagnostics once we switch to Roslyn + + t_isReentrant = false; + throw; + } + } + + if (newFloatingDictionary == IntPtr.Zero) + { + Environment.FailFast("Unable to update floating dictionary"); + return IntPtr.Zero; + } + + // The pointer to the floating dictionary is the first slot of the fixed dictionary. + if (Interlocked.CompareExchange(ref *(IntPtr*)dictionaryPtr, newFloatingDictionary, IntPtr.Zero) != IntPtr.Zero) + { + // Some other thread beat us and updated the pointer to the floating dictionary. + // Free the one allocated by the current thread + if (isNewlyAllocatedDictionary) + MemoryHelpers.FreeMemory(newFloatingDictionary); + } + + return *(IntPtr*)dictionaryPtr; + } + public bool CanInstantiationsShareCode(RuntimeTypeHandle[] genericArgHandles1, RuntimeTypeHandle[] genericArgHandles2, CanonicalFormKind kind) { if (genericArgHandles1.Length != genericArgHandles2.Length) diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/genericdictionarycell.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/genericdictionarycell.cs index a07d6dad8..2ddeff53c 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/genericdictionarycell.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/genericdictionarycell.cs @@ -1073,8 +1073,7 @@ namespace Internal.Runtime.TypeLoader { TypeLoaderLogger.WriteLine(" -> DictionaryCell[" + i.LowLevelToString() + "] = "); - GenericDictionaryCell cell = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); - dictionary[i] = cell; + dictionary[i] = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); } for (uint i = 0; i < count; i++) @@ -1083,6 +1082,66 @@ namespace Internal.Runtime.TypeLoader return dictionary; } + internal static unsafe GenericDictionaryCell[] BuildFloatingDictionary(TypeBuilder typeBuilder, NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, NativeParser parser, out int floatingVersionCellIndex, out int floatingVersionInLayout) + { + // + // The format of a dictionary that has a floating portion is as follows: + // + // "Fixed" portion: + // - First slot is a pointer to the first cell of the "floating" portion + // - Followed by N various dictionary lookup cells + // "Floating" portion: + // - Cell containing the version number of the floating portion + // - Followed by N various dictionary lookup cells + // + + floatingVersionCellIndex = floatingVersionInLayout = -1; + + uint count = parser.GetSequenceCount(); + Debug.Assert(count > 1); + + GenericDictionaryCell cell = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); + if (!(cell is PointerToOtherDictionarySlotCell)) + { + // This is not a dictionary layout that has a floating portion + Debug.Assert(false, "Unreachable: we should never reach here if the target dictionary does not have a floating layout"); + return null; + } + + PointerToOtherDictionarySlotCell pointerToCell = (PointerToOtherDictionarySlotCell)cell; + floatingVersionCellIndex = (int)pointerToCell.OtherDictionarySlot; + Debug.Assert(count > pointerToCell.OtherDictionarySlot); + + GenericDictionaryCell[] dictionary = new GenericDictionaryCell[count - pointerToCell.OtherDictionarySlot]; + + for (uint i = 1; i < pointerToCell.OtherDictionarySlot; i++) + { + // Parse and discard the fixed dictionary cells. We only need to build the cells of the floating portion + ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); + } + + for (uint i = pointerToCell.OtherDictionarySlot; i < count; i++) + { + TypeLoaderLogger.WriteLine(" -> FloatingDictionaryCell[" + (i - pointerToCell.OtherDictionarySlot).LowLevelToString() + "] (" + i.LowLevelToString() + " in all) = "); + + cell = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); + + if (i == pointerToCell.OtherDictionarySlot) + { + // The first cell in the floating portion should always be the version number + Debug.Assert(cell is IntPtrCell); + floatingVersionInLayout = (int)((IntPtrCell)cell).Value; + } + + dictionary[i - pointerToCell.OtherDictionarySlot] = cell; + } + + for (uint i = pointerToCell.OtherDictionarySlot; i < count; i++) + dictionary[i - pointerToCell.OtherDictionarySlot].Prepare(typeBuilder); + + return dictionary; + } + #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING /// <summary> /// Build an array of GenericDictionaryCell from a NativeParser stream that has the appropriate metadata diff --git a/src/System.Private.TypeLoader/src/Resources/Strings.resx b/src/System.Private.TypeLoader/src/Resources/Strings.resx index 02df9452b..949e8adc2 100644 --- a/src/System.Private.TypeLoader/src/Resources/Strings.resx +++ b/src/System.Private.TypeLoader/src/Resources/Strings.resx @@ -126,4 +126,7 @@ <data name="FileNotFound_AssemblyNotFound" xml:space="preserve"> <value>Cannot load assembly '{0}'. No metadata found for this assembly.</value> </data> -</root>
\ No newline at end of file + <data name="FileLoadException_RefDefMismatch" xml:space="preserve"> + <value>Cannot load assembly '{0}'. The assembly exists but its version {1} is lower than the requested version {2}.</value> + </data> +</root> |