diff --git a/Mono.Cecil.Cil/CodeReader.cs b/Mono.Cecil.Cil/CodeReader.cs index ac5b31be0..89515d688 100644 --- a/Mono.Cecil.Cil/CodeReader.cs +++ b/Mono.Cecil.Cil/CodeReader.cs @@ -132,7 +132,7 @@ void ReadFatMethod () public VariableDefinitionCollection ReadVariables (MetadataToken local_var_token) { var position = reader.position; - var variables = reader.ReadVariables (local_var_token); + var variables = reader.ReadVariables (local_var_token, method); reader.position = position; return variables; diff --git a/Mono.Cecil.Cil/ILProcessor.cs b/Mono.Cecil.Cil/ILProcessor.cs index a3fb8f10a..86e6ed754 100644 --- a/Mono.Cecil.Cil/ILProcessor.cs +++ b/Mono.Cecil.Cil/ILProcessor.cs @@ -283,5 +283,10 @@ public void RemoveAt (int index) instructions.RemoveAt (index); } + + public void Clear () + { + instructions.Clear (); + } } } diff --git a/Mono.Cecil.Cil/MethodBody.cs b/Mono.Cecil.Cil/MethodBody.cs index ba3024d3e..0b5692d8d 100644 --- a/Mono.Cecil.Cil/MethodBody.cs +++ b/Mono.Cecil.Cil/MethodBody.cs @@ -81,7 +81,7 @@ public bool HasVariables { public Collection Variables { get { if (variables == null) - Interlocked.CompareExchange (ref variables, new VariableDefinitionCollection (), null); + Interlocked.CompareExchange (ref variables, new VariableDefinitionCollection (this.method), null); return variables; } @@ -134,13 +134,17 @@ public ILProcessor GetILProcessor () sealed class VariableDefinitionCollection : Collection { - internal VariableDefinitionCollection () + readonly MethodDefinition method; + + internal VariableDefinitionCollection (MethodDefinition method) { + this.method = method; } - internal VariableDefinitionCollection (int capacity) + internal VariableDefinitionCollection (MethodDefinition method, int capacity) : base (capacity) { + this.method = method; } protected override void OnAdd (VariableDefinition item, int index) @@ -151,9 +155,7 @@ protected override void OnAdd (VariableDefinition item, int index) protected override void OnInsert (VariableDefinition item, int index) { item.index = index; - - for (int i = index; i < size; i++) - items [i].index = i + 1; + UpdateVariableIndices (index, 1); } protected override void OnSet (VariableDefinition item, int index) @@ -163,10 +165,47 @@ protected override void OnSet (VariableDefinition item, int index) protected override void OnRemove (VariableDefinition item, int index) { + UpdateVariableIndices (index + 1, -1, item); item.index = -1; + } + + void UpdateVariableIndices (int startIndex, int offset, VariableDefinition variableToRemove = null) + { + for (int i = startIndex; i < size; i++) + items [i].index = i + offset; - for (int i = index + 1; i < size; i++) - items [i].index = i - 1; + var debug_info = method == null ? null : method.debug_info; + if (debug_info == null || debug_info.Scope == null) + return; + + foreach (var scope in debug_info.GetScopes ()) { + if (!scope.HasVariables) + continue; + + var variables = scope.Variables; + int variableDebugInfoIndexToRemove = -1; + for (int i = 0; i < variables.Count; i++) { + var variable = variables [i]; + + // If a variable is being removed detect if it has debug info counterpart, if so remove that as well. + // Note that the debug info can be either resolved (has direct reference to the VariableDefinition) + // or unresolved (has only the number index of the variable) - this needs to handle both cases. + if (variableToRemove != null && + ((variable.index.IsResolved && variable.index.ResolvedVariable == variableToRemove) || + (!variable.index.IsResolved && variable.Index == variableToRemove.Index))) { + variableDebugInfoIndexToRemove = i; + continue; + } + + // For unresolved debug info updates indeces to keep them pointing to the same variable. + if (!variable.index.IsResolved && variable.Index >= startIndex) { + variable.index = new VariableIndex (variable.Index + offset); + } + } + + if (variableDebugInfoIndexToRemove >= 0) + variables.RemoveAt (variableDebugInfoIndexToRemove); + } } } @@ -197,25 +236,31 @@ protected override void OnAdd (Instruction item, int index) protected override void OnInsert (Instruction item, int index) { - if (size == 0) - return; + int startOffset = 0; + if (size != 0) { + var current = items [index]; + if (current == null) { + var last = items [index - 1]; + last.next = item; + item.previous = last; + return; + } - var current = items [index]; - if (current == null) { - var last = items [index - 1]; - last.next = item; - item.previous = last; - return; - } + startOffset = current.Offset; - var previous = current.previous; - if (previous != null) { - previous.next = item; - item.previous = previous; + var previous = current.previous; + if (previous != null) { + previous.next = item; + item.previous = previous; + } + + current.previous = item; + item.next = current; } - current.previous = item; - item.next = current; + var scope = GetLocalScope (); + if (scope != null) + UpdateLocalScope (scope, startOffset, item.GetSize (), instructionRemoved: null); } protected override void OnSet (Instruction item, int index) @@ -227,6 +272,12 @@ protected override void OnSet (Instruction item, int index) current.previous = null; current.next = null; + + var scope = GetLocalScope (); + if (scope != null) { + var sizeOfCurrent = current.GetSize (); + UpdateLocalScope (scope, current.Offset + sizeOfCurrent, item.GetSize () - sizeOfCurrent, current); + } } protected override void OnRemove (Instruction item, int index) @@ -241,6 +292,12 @@ protected override void OnRemove (Instruction item, int index) RemoveSequencePoint (item); + var scope = GetLocalScope (); + if (scope != null) { + var size = item.GetSize (); + UpdateLocalScope (scope, item.Offset + size, -size, item); + } + item.previous = null; item.next = null; } @@ -259,5 +316,38 @@ void RemoveSequencePoint (Instruction instruction) } } } + + ScopeDebugInformation GetLocalScope () + { + var debug_info = method.debug_info; + if (debug_info == null) + return null; + + return debug_info.Scope; + } + + static void UpdateLocalScope (ScopeDebugInformation scope, int startFromOffset, int offset, Instruction instructionRemoved) + { + // For both start and enf offsets on the scope: + // * If the offset is resolved (points to instruction by reference) only update it if the instruction it points to is being removed. + // For non-removed instructions it remains correct regardless of any updates to the instructions. + // * If the offset is not resolved (stores the instruction offset number itself) + // update the number accordingly to keep it pointing to the correct instruction (by offset). + + if ((!scope.Start.IsResolved && scope.Start.Offset >= startFromOffset) || + (instructionRemoved != null && scope.Start.ResolvedInstruction == instructionRemoved)) + scope.Start = new InstructionOffset (scope.Start.Offset + offset); + + // For end offset only update it if it's not the special sentinel value "EndOfMethod"; that should remain as-is. + if (!scope.End.IsEndOfMethod && + ((!scope.End.IsResolved && scope.End.Offset >= startFromOffset) || + (instructionRemoved != null && scope.End.ResolvedInstruction == instructionRemoved))) + scope.End = new InstructionOffset (scope.End.Offset + offset); + + if (scope.HasScopes) { + foreach (var subScope in scope.Scopes) + UpdateLocalScope (subScope, startFromOffset, offset, instructionRemoved); + } + } } } diff --git a/Mono.Cecil.Cil/Symbols.cs b/Mono.Cecil.Cil/Symbols.cs index 9c095af52..38b47a42e 100644 --- a/Mono.Cecil.Cil/Symbols.cs +++ b/Mono.Cecil.Cil/Symbols.cs @@ -207,6 +207,10 @@ public bool IsEndOfMethod { get { return instruction == null && !offset.HasValue; } } + internal bool IsResolved => instruction != null; + + internal Instruction ResolvedInstruction => instruction; + public InstructionOffset (Instruction instruction) { if (instruction == null) @@ -244,6 +248,10 @@ public int Index { } } + internal bool IsResolved => variable != null; + + internal VariableDefinition ResolvedVariable => variable; + public VariableIndex (VariableDefinition variable) { if (variable == null) diff --git a/Mono.Cecil.PE/ImageReader.cs b/Mono.Cecil.PE/ImageReader.cs index 9731ba90b..84fdeb660 100644 --- a/Mono.Cecil.PE/ImageReader.cs +++ b/Mono.Cecil.PE/ImageReader.cs @@ -481,7 +481,7 @@ void ComputeTableInformations () { uint offset = (uint) BaseStream.Position - table_heap_offset - image.MetadataSection.PointerToRawData; // header - int stridx_size = image.StringHeap.IndexSize; + int stridx_size = image.StringHeap != null ? image.StringHeap.IndexSize : 2; int guididx_size = image.GuidHeap != null ? image.GuidHeap.IndexSize : 2; int blobidx_size = image.BlobHeap != null ? image.BlobHeap.IndexSize : 2; diff --git a/Mono.Cecil/AssemblyHashAlgorithm.cs b/Mono.Cecil/AssemblyHashAlgorithm.cs index 66ef4cbba..6684c2ceb 100644 --- a/Mono.Cecil/AssemblyHashAlgorithm.cs +++ b/Mono.Cecil/AssemblyHashAlgorithm.cs @@ -12,7 +12,11 @@ namespace Mono.Cecil { public enum AssemblyHashAlgorithm : uint { None = 0x0000, - Reserved = 0x8003, // MD5 - SHA1 = 0x8004 + MD5 = 0x8003, + SHA1 = 0x8004, + SHA256 = 0x800C, + SHA384 = 0x800D, + SHA512 = 0x800E, + Reserved = 0x8003, // MD5 } } diff --git a/Mono.Cecil/AssemblyReader.cs b/Mono.Cecil/AssemblyReader.cs index 224d8a869..d18a51ed0 100644 --- a/Mono.Cecil/AssemblyReader.cs +++ b/Mono.Cecil/AssemblyReader.cs @@ -2141,7 +2141,7 @@ public CallSite ReadCallSite (MetadataToken token) return call_site; } - public VariableDefinitionCollection ReadVariables (MetadataToken local_var_token) + public VariableDefinitionCollection ReadVariables (MetadataToken local_var_token, MethodDefinition method = null) { if (!MoveTo (Table.StandAloneSig, local_var_token.RID)) return null; @@ -2156,7 +2156,7 @@ public VariableDefinitionCollection ReadVariables (MetadataToken local_var_token if (count == 0) return null; - var variables = new VariableDefinitionCollection ((int) count); + var variables = new VariableDefinitionCollection (method, (int) count); for (int i = 0; i < count; i++) variables.Add (new VariableDefinition (reader.ReadTypeSignature ())); @@ -2326,10 +2326,6 @@ MemberReference ReadMemberReference (uint rid) } member.token = new MetadataToken (TokenType.MemberRef, rid); - - if (module.IsWindowsMetadata ()) - WindowsRuntimeProjections.Project (member); - return member; } diff --git a/Mono.Cecil/AssemblyWriter.cs b/Mono.Cecil/AssemblyWriter.cs index 3ea1088b3..4fba7865e 100644 --- a/Mono.Cecil/AssemblyWriter.cs +++ b/Mono.Cecil/AssemblyWriter.cs @@ -1277,6 +1277,8 @@ void AttachTokens () void AttachTypeToken (TypeDefinition type) { + var treatment = WindowsRuntimeProjections.RemoveProjection (type); + type.token = new MetadataToken (TokenType.TypeDef, type_rid++); type.fields_range.Start = field_rid; type.methods_range.Start = method_rid; @@ -1289,6 +1291,8 @@ void AttachTypeToken (TypeDefinition type) if (type.HasNestedTypes) AttachNestedTypesToken (type); + + WindowsRuntimeProjections.ApplyProjection (type, treatment); } void AttachNestedTypesToken (TypeDefinition type) @@ -1990,16 +1994,12 @@ void AddSecurityDeclarations (ISecurityDeclarationProvider owner) MetadataToken GetMemberRefToken (MemberReference member) { - var projection = WindowsRuntimeProjections.RemoveProjection (member); - var row = CreateMemberRefRow (member); MetadataToken token; if (!member_ref_map.TryGetValue (row, out token)) token = AddMemberReference (member, row); - WindowsRuntimeProjections.ApplyProjection (member, projection); - return token; } diff --git a/Mono.Cecil/BaseAssemblyResolver.cs b/Mono.Cecil/BaseAssemblyResolver.cs index b64ba4c8b..fd9e5c968 100644 --- a/Mono.Cecil/BaseAssemblyResolver.cs +++ b/Mono.Cecil/BaseAssemblyResolver.cs @@ -366,7 +366,7 @@ AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderP var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" }; var prefixes = new [] { string.Empty, "v4.0_" }; - for (int i = 0; i < 2; i++) { + for (int i = 0; i < gac_paths.Count; i++) { for (int j = 0; j < gacs.Length; j++) { var gac = Path.Combine (gac_paths [i], gacs [j]); var file = GetAssemblyFile (reference, prefixes [i], gac); diff --git a/Mono.Cecil/FieldDefinition.cs b/Mono.Cecil/FieldDefinition.cs index 242e04ef2..15b7720c3 100644 --- a/Mono.Cecil/FieldDefinition.cs +++ b/Mono.Cecil/FieldDefinition.cs @@ -68,7 +68,7 @@ public int Offset { set { offset = value; } } - internal new FieldDefinitionProjection WindowsRuntimeProjection { + internal FieldDefinitionProjection WindowsRuntimeProjection { get { return (FieldDefinitionProjection) projection; } set { projection = value; } } diff --git a/Mono.Cecil/GenericParameterResolver.cs b/Mono.Cecil/GenericParameterResolver.cs new file mode 100644 index 000000000..86525c59e --- /dev/null +++ b/Mono.Cecil/GenericParameterResolver.cs @@ -0,0 +1,175 @@ +using Mono.Cecil.Cil; +using System; + +namespace Mono.Cecil { + internal sealed class GenericParameterResolver { + internal static TypeReference ResolveReturnTypeIfNeeded (MethodReference methodReference) + { + if (methodReference.DeclaringType.IsArray && methodReference.Name == "Get") + return methodReference.ReturnType; + + var genericInstanceMethod = methodReference as GenericInstanceMethod; + var declaringGenericInstanceType = methodReference.DeclaringType as GenericInstanceType; + + if (genericInstanceMethod == null && declaringGenericInstanceType == null) + return methodReference.ReturnType; + + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, methodReference.ReturnType); + } + + internal static TypeReference ResolveFieldTypeIfNeeded (FieldReference fieldReference) + { + return ResolveIfNeeded (null, fieldReference.DeclaringType as GenericInstanceType, fieldReference.FieldType); + } + + internal static TypeReference ResolveParameterTypeIfNeeded (MethodReference method, ParameterReference parameter) + { + var genericInstanceMethod = method as GenericInstanceMethod; + var declaringGenericInstanceType = method.DeclaringType as GenericInstanceType; + + if (genericInstanceMethod == null && declaringGenericInstanceType == null) + return parameter.ParameterType; + + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, parameter.ParameterType); + } + + internal static TypeReference ResolveVariableTypeIfNeeded (MethodReference method, VariableReference variable) + { + var genericInstanceMethod = method as GenericInstanceMethod; + var declaringGenericInstanceType = method.DeclaringType as GenericInstanceType; + + if (genericInstanceMethod == null && declaringGenericInstanceType == null) + return variable.VariableType; + + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, variable.VariableType); + } + + private static TypeReference ResolveIfNeeded (IGenericInstance genericInstanceMethod, IGenericInstance declaringGenericInstanceType, TypeReference parameterType) + { + var byRefType = parameterType as ByReferenceType; + if (byRefType != null) + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, byRefType); + + var arrayType = parameterType as ArrayType; + if (arrayType != null) + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, arrayType); + + var genericInstanceType = parameterType as GenericInstanceType; + if (genericInstanceType != null) + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, genericInstanceType); + + var genericParameter = parameterType as GenericParameter; + if (genericParameter != null) + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, genericParameter); + + var requiredModifierType = parameterType as RequiredModifierType; + if (requiredModifierType != null && ContainsGenericParameters (requiredModifierType)) + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, requiredModifierType.ElementType); + + if (ContainsGenericParameters (parameterType)) + throw new Exception ("Unexpected generic parameter."); + + return parameterType; + } + + private static TypeReference ResolveIfNeeded (IGenericInstance genericInstanceMethod, IGenericInstance genericInstanceType, GenericParameter genericParameterElement) + { + return (genericParameterElement.MetadataType == MetadataType.MVar) + ? (genericInstanceMethod != null ? genericInstanceMethod.GenericArguments[genericParameterElement.Position] : genericParameterElement) + : genericInstanceType.GenericArguments[genericParameterElement.Position]; + } + + private static ArrayType ResolveIfNeeded (IGenericInstance genericInstanceMethod, IGenericInstance genericInstanceType, ArrayType arrayType) + { + return new ArrayType (ResolveIfNeeded (genericInstanceMethod, genericInstanceType, arrayType.ElementType), arrayType.Rank); + } + + private static ByReferenceType ResolveIfNeeded (IGenericInstance genericInstanceMethod, IGenericInstance genericInstanceType, ByReferenceType byReferenceType) + { + return new ByReferenceType (ResolveIfNeeded (genericInstanceMethod, genericInstanceType, byReferenceType.ElementType)); + } + + private static GenericInstanceType ResolveIfNeeded (IGenericInstance genericInstanceMethod, IGenericInstance genericInstanceType, GenericInstanceType genericInstanceType1) + { + if (!ContainsGenericParameters (genericInstanceType1)) + return genericInstanceType1; + + var newGenericInstance = new GenericInstanceType (genericInstanceType1.ElementType); + + foreach (var genericArgument in genericInstanceType1.GenericArguments) { + if (!genericArgument.IsGenericParameter) { + newGenericInstance.GenericArguments.Add (ResolveIfNeeded (genericInstanceMethod, genericInstanceType, genericArgument)); + continue; + } + + var genParam = (GenericParameter)genericArgument; + + switch (genParam.Type) { + case GenericParameterType.Type: { + if (genericInstanceType == null) + throw new NotSupportedException (); + + newGenericInstance.GenericArguments.Add (genericInstanceType.GenericArguments[genParam.Position]); + } + break; + + case GenericParameterType.Method: { + if (genericInstanceMethod == null) + newGenericInstance.GenericArguments.Add (genParam); + else + newGenericInstance.GenericArguments.Add (genericInstanceMethod.GenericArguments[genParam.Position]); + } + break; + } + } + + return newGenericInstance; + } + + private static bool ContainsGenericParameters (TypeReference typeReference) + { + var genericParameter = typeReference as GenericParameter; + if (genericParameter != null) + return true; + + var arrayType = typeReference as ArrayType; + if (arrayType != null) + return ContainsGenericParameters (arrayType.ElementType); + + var pointerType = typeReference as PointerType; + if (pointerType != null) + return ContainsGenericParameters (pointerType.ElementType); + + var byRefType = typeReference as ByReferenceType; + if (byRefType != null) + return ContainsGenericParameters (byRefType.ElementType); + + var sentinelType = typeReference as SentinelType; + if (sentinelType != null) + return ContainsGenericParameters (sentinelType.ElementType); + + var pinnedType = typeReference as PinnedType; + if (pinnedType != null) + return ContainsGenericParameters (pinnedType.ElementType); + + var requiredModifierType = typeReference as RequiredModifierType; + if (requiredModifierType != null) + return ContainsGenericParameters (requiredModifierType.ElementType); + + var genericInstance = typeReference as GenericInstanceType; + if (genericInstance != null) { + foreach (var genericArgument in genericInstance.GenericArguments) { + if (ContainsGenericParameters (genericArgument)) + return true; + } + + return false; + } + + if (typeReference is TypeSpecification) + throw new NotSupportedException (); + + return false; + } + } +} diff --git a/Mono.Cecil/MemberReference.cs b/Mono.Cecil/MemberReference.cs index 84fd38c54..8276a66f1 100644 --- a/Mono.Cecil/MemberReference.cs +++ b/Mono.Cecil/MemberReference.cs @@ -48,11 +48,6 @@ public bool IsWindowsRuntimeProjection { get { return projection != null; } } - internal MemberReferenceProjection WindowsRuntimeProjection { - get { return (MemberReferenceProjection) projection; } - set { projection = value; } - } - internal bool HasImage { get { var module = Module; diff --git a/Mono.Cecil/MethodDefinition.cs b/Mono.Cecil/MethodDefinition.cs index 42bd8f3ea..b4b88d997 100644 --- a/Mono.Cecil/MethodDefinition.cs +++ b/Mono.Cecil/MethodDefinition.cs @@ -81,7 +81,7 @@ public MethodSemanticsAttributes SemanticsAttributes { set { sem_attrs = value; } } - internal new MethodDefinitionProjection WindowsRuntimeProjection { + internal MethodDefinitionProjection WindowsRuntimeProjection { get { return (MethodDefinitionProjection) projection; } set { projection = value; } } diff --git a/Mono.Cecil/MethodReferenceComparer.cs b/Mono.Cecil/MethodReferenceComparer.cs new file mode 100644 index 000000000..3bf2c6eca --- /dev/null +++ b/Mono.Cecil/MethodReferenceComparer.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; + +namespace Mono.Cecil { + internal sealed class MethodReferenceComparer : EqualityComparer { + // Initialized lazily for each thread + [ThreadStatic] + static List xComparisonStack = null; + + [ThreadStatic] + static List yComparisonStack = null; + + public override bool Equals (MethodReference x, MethodReference y) + { + return AreEqual (x, y); + } + + public override int GetHashCode (MethodReference obj) + { + return GetHashCodeFor (obj); + } + + public static bool AreEqual (MethodReference x, MethodReference y) + { + if (ReferenceEquals (x, y)) + return true; + + if (x.HasThis != y.HasThis) + return false; + + if (x.HasParameters != y.HasParameters) + return false; + + if (x.HasGenericParameters != y.HasGenericParameters) + return false; + + if (x.Parameters.Count != y.Parameters.Count) + return false; + + if (x.Name != y.Name) + return false; + + if (!TypeReferenceEqualityComparer.AreEqual (x.DeclaringType, y.DeclaringType)) + return false; + + var xGeneric = x as GenericInstanceMethod; + var yGeneric = y as GenericInstanceMethod; + if (xGeneric != null || yGeneric != null) { + if (xGeneric == null || yGeneric == null) + return false; + + if (xGeneric.GenericArguments.Count != yGeneric.GenericArguments.Count) + return false; + + for (int i = 0; i < xGeneric.GenericArguments.Count; i++) + if (!TypeReferenceEqualityComparer.AreEqual (xGeneric.GenericArguments[i], yGeneric.GenericArguments[i])) + return false; + } + + var xResolved = x.Resolve (); + var yResolved = y.Resolve (); + + if (xResolved != yResolved) + return false; + + if (xResolved == null) + { + // We couldn't resolve either method. In order for them to be equal, their parameter types _must_ match. But wait, there's a twist! + // There exists a situation where we might get into a recursive state: parameter type comparison might lead to comparing the same + // methods again if the parameter types are generic parameters whose owners are these methods. We guard against these by using a + // thread static list of all our comparisons carried out in the stack so far, and if we're in progress of comparing them already, + // we'll just say that they match. + + if (xComparisonStack == null) + xComparisonStack = new List (); + + if (yComparisonStack == null) + yComparisonStack = new List (); + + for (int i = 0; i < xComparisonStack.Count; i++) { + if (xComparisonStack[i] == x && yComparisonStack[i] == y) + return true; + } + + xComparisonStack.Add (x); + + try { + yComparisonStack.Add (y); + + try { + for (int i = 0; i < x.Parameters.Count; i++) { + if (!TypeReferenceEqualityComparer.AreEqual (x.Parameters[i].ParameterType, y.Parameters[i].ParameterType)) + return false; + } + } finally { + yComparisonStack.RemoveAt (yComparisonStack.Count - 1); + } + } finally { + xComparisonStack.RemoveAt (xComparisonStack.Count - 1); + } + } + + return true; + } + + public static bool AreSignaturesEqual (MethodReference x, MethodReference y, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (x.HasThis != y.HasThis) + return false; + + if (x.Parameters.Count != y.Parameters.Count) + return false; + + if (x.GenericParameters.Count != y.GenericParameters.Count) + return false; + + for (var i = 0; i < x.Parameters.Count; i++) + if (!TypeReferenceEqualityComparer.AreEqual (x.Parameters[i].ParameterType, y.Parameters[i].ParameterType, comparisonMode)) + return false; + + if (!TypeReferenceEqualityComparer.AreEqual (x.ReturnType, y.ReturnType, comparisonMode)) + return false; + + return true; + } + + public static int GetHashCodeFor (MethodReference obj) + { + // a very good prime number + const int hashCodeMultiplier = 486187739; + + var genericInstanceMethod = obj as GenericInstanceMethod; + if (genericInstanceMethod != null) { + var hashCode = GetHashCodeFor (genericInstanceMethod.ElementMethod); + for (var i = 0; i < genericInstanceMethod.GenericArguments.Count; i++) + hashCode = hashCode * hashCodeMultiplier + TypeReferenceEqualityComparer.GetHashCodeFor (genericInstanceMethod.GenericArguments[i]); + return hashCode; + } + + return TypeReferenceEqualityComparer.GetHashCodeFor (obj.DeclaringType) * hashCodeMultiplier + obj.Name.GetHashCode (); + } + } +} diff --git a/Mono.Cecil/Treatments.cs b/Mono.Cecil/Treatments.cs index 4ceaebf7c..21c176842 100644 --- a/Mono.Cecil/Treatments.cs +++ b/Mono.Cecil/Treatments.cs @@ -23,6 +23,7 @@ enum TypeDefinitionTreatment { PrefixWindowsRuntimeName = 0x4, RedirectToClrType = 0x5, RedirectToClrAttribute = 0x6, + RedirectImplementedMethods = 0x7, Abstract = 0x10, Internal = 0x20, @@ -38,7 +39,6 @@ enum TypeReferenceTreatment { [Flags] enum MethodDefinitionTreatment { None = 0x0, - Dispose = 0x1, Abstract = 0x2, Private = 0x4, Public = 0x8, @@ -51,11 +51,6 @@ enum FieldDefinitionTreatment { Public = 0x1, } - enum MemberReferenceTreatment { - None = 0x0, - Dispose = 0x1, - } - enum CustomAttributeValueTreatment { None = 0x0, AllowSingle = 0x1, diff --git a/Mono.Cecil/TypeComparisonMode.cs b/Mono.Cecil/TypeComparisonMode.cs new file mode 100644 index 000000000..f69d98a99 --- /dev/null +++ b/Mono.Cecil/TypeComparisonMode.cs @@ -0,0 +1,12 @@ +namespace Mono.Cecil +{ + internal enum TypeComparisonMode { + Exact, + SignatureOnly, + + /// + /// Types can be in different assemblies, as long as the module, assembly, and type names match they will be considered equal + /// + SignatureOnlyLoose + } +} diff --git a/Mono.Cecil/TypeReference.cs b/Mono.Cecil/TypeReference.cs index e3fb5c58c..701f83b8f 100644 --- a/Mono.Cecil/TypeReference.cs +++ b/Mono.Cecil/TypeReference.cs @@ -100,7 +100,7 @@ public override ModuleDefinition Module { } } - internal new TypeReferenceProjection WindowsRuntimeProjection { + internal TypeReferenceProjection WindowsRuntimeProjection { get { return (TypeReferenceProjection) projection; } set { projection = value; } } diff --git a/Mono.Cecil/TypeReferenceEqualityComparer.cs b/Mono.Cecil/TypeReferenceEqualityComparer.cs new file mode 100644 index 000000000..29c2ee2e9 --- /dev/null +++ b/Mono.Cecil/TypeReferenceEqualityComparer.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; + +namespace Mono.Cecil { + internal sealed class TypeReferenceEqualityComparer : EqualityComparer { + public override bool Equals (TypeReference x, TypeReference y) + { + return AreEqual (x, y); + } + + public override int GetHashCode (TypeReference obj) + { + return GetHashCodeFor (obj); + } + + public static bool AreEqual (TypeReference a, TypeReference b, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + if (a == null || b == null) + return false; + + var aMetadataType = a.MetadataType; + var bMetadataType = b.MetadataType; + + if (aMetadataType == MetadataType.GenericInstance || bMetadataType == MetadataType.GenericInstance) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericInstanceType)a, (GenericInstanceType)b, comparisonMode); + } + + if (aMetadataType == MetadataType.Array || bMetadataType == MetadataType.Array) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (ArrayType)a; + var b1 = (ArrayType)b; + if (a1.Rank != b1.Rank) + return false; + + return AreEqual (a1.ElementType, b1.ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.Var || bMetadataType == MetadataType.Var) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericParameter)a, (GenericParameter)b, comparisonMode); + } + + if (aMetadataType == MetadataType.MVar || bMetadataType == MetadataType.MVar) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericParameter)a, (GenericParameter)b, comparisonMode); + } + + if (aMetadataType == MetadataType.ByReference || bMetadataType == MetadataType.ByReference) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((ByReferenceType)a).ElementType, ((ByReferenceType)b).ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.Pointer || bMetadataType == MetadataType.Pointer) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((PointerType)a).ElementType, ((PointerType)b).ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.RequiredModifier || bMetadataType == MetadataType.RequiredModifier) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (RequiredModifierType)a; + var b1 = (RequiredModifierType)b; + + return AreEqual (a1.ModifierType, b1.ModifierType, comparisonMode) && AreEqual (a1.ElementType, b1.ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.OptionalModifier || bMetadataType == MetadataType.OptionalModifier) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (OptionalModifierType)a; + var b1 = (OptionalModifierType)b; + + return AreEqual (a1.ModifierType, b1.ModifierType, comparisonMode) && AreEqual (a1.ElementType, b1.ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.Pinned || bMetadataType == MetadataType.Pinned) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((PinnedType)a).ElementType, ((PinnedType)b).ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.Sentinel || bMetadataType == MetadataType.Sentinel) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((SentinelType)a).ElementType, ((SentinelType)b).ElementType, comparisonMode); + } + + if (!a.Name.Equals (b.Name) || !a.Namespace.Equals (b.Namespace)) + return false; + + var xDefinition = a.Resolve (); + var yDefinition = b.Resolve (); + + // For loose signature the types could be in different assemblies, as long as the type names match we will consider them equal + if (comparisonMode == TypeComparisonMode.SignatureOnlyLoose) { + if (xDefinition.Module.Name != yDefinition.Module.Name) + return false; + + if (xDefinition.Module.Assembly.Name.Name != yDefinition.Module.Assembly.Name.Name) + return false; + + return xDefinition.FullName == yDefinition.FullName; + } + + return xDefinition == yDefinition; + } + + static bool AreEqual (GenericParameter a, GenericParameter b, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + if (a.Position != b.Position) + return false; + + if (a.Type != b.Type) + return false; + + var aOwnerType = a.Owner as TypeReference; + if (aOwnerType != null && AreEqual (aOwnerType, b.Owner as TypeReference, comparisonMode)) + return true; + + var aOwnerMethod = a.Owner as MethodReference; + if (aOwnerMethod != null && comparisonMode != TypeComparisonMode.SignatureOnlyLoose && MethodReferenceComparer.AreEqual (aOwnerMethod, b.Owner as MethodReference)) + return true; + + return comparisonMode == TypeComparisonMode.SignatureOnly || comparisonMode == TypeComparisonMode.SignatureOnlyLoose; + } + + static bool AreEqual (GenericInstanceType a, GenericInstanceType b, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + var aGenericArgumentsCount = a.GenericArguments.Count; + if (aGenericArgumentsCount != b.GenericArguments.Count) + return false; + + if (!AreEqual (a.ElementType, b.ElementType, comparisonMode)) + return false; + + for (int i = 0; i < aGenericArgumentsCount; i++) + if (!AreEqual (a.GenericArguments[i], b.GenericArguments[i], comparisonMode)) + return false; + + return true; + } + + public static int GetHashCodeFor (TypeReference obj) + { + // a very good prime number + const int hashCodeMultiplier = 486187739; + // prime numbers + const int genericInstanceTypeMultiplier = 31; + const int byReferenceMultiplier = 37; + const int pointerMultiplier = 41; + const int requiredModifierMultiplier = 43; + const int optionalModifierMultiplier = 47; + const int pinnedMultiplier = 53; + const int sentinelMultiplier = 59; + + var metadataType = obj.MetadataType; + + if (metadataType == MetadataType.GenericInstance) { + var genericInstanceType = (GenericInstanceType)obj; + var hashCode = GetHashCodeFor (genericInstanceType.ElementType) * hashCodeMultiplier + genericInstanceTypeMultiplier; + for (var i = 0; i < genericInstanceType.GenericArguments.Count; i++) + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (genericInstanceType.GenericArguments[i]); + return hashCode; + } + + if (metadataType == MetadataType.Array) { + var arrayType = (ArrayType)obj; + return GetHashCodeFor (arrayType.ElementType) * hashCodeMultiplier + arrayType.Rank.GetHashCode (); + } + + if (metadataType == MetadataType.Var || metadataType == MetadataType.MVar) { + var genericParameter = (GenericParameter)obj; + var hashCode = genericParameter.Position.GetHashCode () * hashCodeMultiplier + ((int)metadataType).GetHashCode (); + + var ownerTypeReference = genericParameter.Owner as TypeReference; + if (ownerTypeReference != null) + return hashCode * hashCodeMultiplier + GetHashCodeFor (ownerTypeReference); + + var ownerMethodReference = genericParameter.Owner as MethodReference; + if (ownerMethodReference != null) + return hashCode * hashCodeMultiplier + MethodReferenceComparer.GetHashCodeFor (ownerMethodReference); + + throw new InvalidOperationException ("Generic parameter encountered with invalid owner"); + } + + if (metadataType == MetadataType.ByReference) { + var byReferenceType = (ByReferenceType)obj; + return GetHashCodeFor (byReferenceType.ElementType) * hashCodeMultiplier * byReferenceMultiplier; + } + + if (metadataType == MetadataType.Pointer) { + var pointerType = (PointerType)obj; + return GetHashCodeFor (pointerType.ElementType) * hashCodeMultiplier * pointerMultiplier; + } + + if (metadataType == MetadataType.RequiredModifier) { + var requiredModifierType = (RequiredModifierType)obj; + var hashCode = GetHashCodeFor (requiredModifierType.ElementType) * requiredModifierMultiplier; + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (requiredModifierType.ModifierType); + return hashCode; + } + + if (metadataType == MetadataType.OptionalModifier) { + var optionalModifierType = (OptionalModifierType)obj; + var hashCode = GetHashCodeFor (optionalModifierType.ElementType) * optionalModifierMultiplier; + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (optionalModifierType.ModifierType); + return hashCode; + } + + if (metadataType == MetadataType.Pinned) { + var pinnedType = (PinnedType)obj; + return GetHashCodeFor (pinnedType.ElementType) * hashCodeMultiplier * pinnedMultiplier; + } + + if (metadataType == MetadataType.Sentinel) { + var sentinelType = (SentinelType)obj; + return GetHashCodeFor (sentinelType.ElementType) * hashCodeMultiplier * sentinelMultiplier; + } + + if (metadataType == MetadataType.FunctionPointer) { + throw new NotImplementedException ("We currently don't handle function pointer types."); + } + + return obj.Namespace.GetHashCode () * hashCodeMultiplier + obj.FullName.GetHashCode (); + } + } +} diff --git a/Mono.Cecil/TypeResolver.cs b/Mono.Cecil/TypeResolver.cs new file mode 100644 index 000000000..95b0f3a71 --- /dev/null +++ b/Mono.Cecil/TypeResolver.cs @@ -0,0 +1,220 @@ +using Mono.Cecil.Cil; +using System; + +namespace Mono.Cecil { + internal sealed class TypeResolver { + private readonly IGenericInstance _typeDefinitionContext; + private readonly IGenericInstance _methodDefinitionContext; + + public static TypeResolver For (TypeReference typeReference) + { + return typeReference.IsGenericInstance ? new TypeResolver ((GenericInstanceType)typeReference) : new TypeResolver (); + } + + public static TypeResolver For (TypeReference typeReference, MethodReference methodReference) + { + return new TypeResolver (typeReference as GenericInstanceType, methodReference as GenericInstanceMethod); + } + + public TypeResolver () + { + + } + + public TypeResolver (GenericInstanceType typeDefinitionContext) + { + _typeDefinitionContext = typeDefinitionContext; + } + + public TypeResolver (GenericInstanceMethod methodDefinitionContext) + { + _methodDefinitionContext = methodDefinitionContext; + } + + public TypeResolver (GenericInstanceType typeDefinitionContext, GenericInstanceMethod methodDefinitionContext) + { + _typeDefinitionContext = typeDefinitionContext; + _methodDefinitionContext = methodDefinitionContext; + } + + public MethodReference Resolve (MethodReference method) + { + var methodReference = method; + if (IsDummy ()) + return methodReference; + + var declaringType = Resolve (method.DeclaringType); + + var genericInstanceMethod = method as GenericInstanceMethod; + if (genericInstanceMethod != null) { + methodReference = new MethodReference (method.Name, method.ReturnType, declaringType); + + foreach (var p in method.Parameters) + methodReference.Parameters.Add (new ParameterDefinition (p.Name, p.Attributes, p.ParameterType)); + + foreach (var gp in genericInstanceMethod.ElementMethod.GenericParameters) + methodReference.GenericParameters.Add (new GenericParameter (gp.Name, methodReference)); + + methodReference.HasThis = method.HasThis; + + var m = new GenericInstanceMethod (methodReference); + foreach (var ga in genericInstanceMethod.GenericArguments) { + m.GenericArguments.Add (Resolve (ga)); + } + + methodReference = m; + } else { + methodReference = new MethodReference (method.Name, method.ReturnType, declaringType); + + foreach (var gp in method.GenericParameters) + methodReference.GenericParameters.Add (new GenericParameter (gp.Name, methodReference)); + + foreach (var p in method.Parameters) + methodReference.Parameters.Add (new ParameterDefinition (p.Name, p.Attributes, p.ParameterType)); + + methodReference.HasThis = method.HasThis; + } + + + return methodReference; + } + + public FieldReference Resolve (FieldReference field) + { + var declaringType = Resolve (field.DeclaringType); + + if (declaringType == field.DeclaringType) + return field; + + return new FieldReference (field.Name, field.FieldType, declaringType); + } + + public TypeReference ResolveReturnType (MethodReference method) + { + return Resolve (GenericParameterResolver.ResolveReturnTypeIfNeeded (method)); + } + + public TypeReference ResolveParameterType (MethodReference method, ParameterReference parameter) + { + return Resolve (GenericParameterResolver.ResolveParameterTypeIfNeeded (method, parameter)); + } + + public TypeReference ResolveVariableType (MethodReference method, VariableReference variable) + { + return Resolve (GenericParameterResolver.ResolveVariableTypeIfNeeded (method, variable)); + } + + public TypeReference ResolveFieldType (FieldReference field) + { + return Resolve (GenericParameterResolver.ResolveFieldTypeIfNeeded (field)); + } + + public TypeReference Resolve (TypeReference typeReference) + { + return Resolve (typeReference, true); + } + + public TypeReference Resolve (TypeReference typeReference, bool includeTypeDefinitions) + { + if (IsDummy ()) + return typeReference; + + if (_typeDefinitionContext != null && _typeDefinitionContext.GenericArguments.Contains (typeReference)) + return typeReference; + if (_methodDefinitionContext != null && _methodDefinitionContext.GenericArguments.Contains (typeReference)) + return typeReference; + + var genericParameter = typeReference as GenericParameter; + if (genericParameter != null) { + if (_typeDefinitionContext != null && _typeDefinitionContext.GenericArguments.Contains (genericParameter)) + return genericParameter; + if (_methodDefinitionContext != null && _methodDefinitionContext.GenericArguments.Contains (genericParameter)) + return genericParameter; + return ResolveGenericParameter (genericParameter); + } + + var arrayType = typeReference as ArrayType; + if (arrayType != null) + return new ArrayType (Resolve (arrayType.ElementType), arrayType.Rank); + + var pointerType = typeReference as PointerType; + if (pointerType != null) + return new PointerType (Resolve (pointerType.ElementType)); + + var byReferenceType = typeReference as ByReferenceType; + if (byReferenceType != null) + return new ByReferenceType (Resolve (byReferenceType.ElementType)); + + var pinnedType = typeReference as PinnedType; + if (pinnedType != null) + return new PinnedType (Resolve (pinnedType.ElementType)); + + var genericInstanceType = typeReference as GenericInstanceType; + if (genericInstanceType != null) { + var newGenericInstanceType = new GenericInstanceType (genericInstanceType.ElementType); + foreach (var genericArgument in genericInstanceType.GenericArguments) + newGenericInstanceType.GenericArguments.Add (Resolve (genericArgument)); + return newGenericInstanceType; + } + + var requiredModType = typeReference as RequiredModifierType; + if (requiredModType != null) + return Resolve (requiredModType.ElementType, includeTypeDefinitions); + + + if (includeTypeDefinitions) { + var typeDefinition = typeReference as TypeDefinition; + if (typeDefinition != null && typeDefinition.HasGenericParameters) { + var newGenericInstanceType = new GenericInstanceType (typeDefinition); + foreach (var gp in typeDefinition.GenericParameters) + newGenericInstanceType.GenericArguments.Add (Resolve (gp)); + return newGenericInstanceType; + } + } + + if (typeReference is TypeSpecification) + throw new NotSupportedException (string.Format ("The type {0} cannot be resolved correctly.", typeReference.FullName)); + + return typeReference; + } + + internal TypeResolver Nested (GenericInstanceMethod genericInstanceMethod) + { + return new TypeResolver (_typeDefinitionContext as GenericInstanceType, genericInstanceMethod); + } + + private TypeReference ResolveGenericParameter (GenericParameter genericParameter) + { + if (genericParameter.Owner == null) + return HandleOwnerlessInvalidILCode (genericParameter); + + var memberReference = genericParameter.Owner as MemberReference; + if (memberReference == null) + throw new NotSupportedException (); + + return genericParameter.Type == GenericParameterType.Type + ? _typeDefinitionContext.GenericArguments[genericParameter.Position] + : (_methodDefinitionContext != null ? _methodDefinitionContext.GenericArguments[genericParameter.Position] : genericParameter); + } + + private TypeReference HandleOwnerlessInvalidILCode (GenericParameter genericParameter) + { + // NOTE: If owner is null and we have a method parameter, then we'll assume that the method parameter + // is actually a type parameter, and we'll use the type parameter from the corresponding position. I think + // this assumption is valid, but if you're visiting this code then I might have been proven wrong. + if (genericParameter.Type == GenericParameterType.Method && (_typeDefinitionContext != null && genericParameter.Position < _typeDefinitionContext.GenericArguments.Count)) + return _typeDefinitionContext.GenericArguments[genericParameter.Position]; + + // NOTE: Owner cannot be null, but sometimes the Mono compiler generates invalid IL and we + // end up in this situation. + // When we do, we assume that the runtime doesn't care about the resolved type of the GenericParameter, + // thus we return a reference to System.Object. + return genericParameter.Module.TypeSystem.Object; + } + + private bool IsDummy () + { + return _typeDefinitionContext == null && _methodDefinitionContext == null; + } + } +} diff --git a/Mono.Cecil/WindowsRuntimeProjections.cs b/Mono.Cecil/WindowsRuntimeProjections.cs index 5ecd53c50..946116b26 100644 --- a/Mono.Cecil/WindowsRuntimeProjections.cs +++ b/Mono.Cecil/WindowsRuntimeProjections.cs @@ -15,29 +15,21 @@ namespace Mono.Cecil { - sealed class MemberReferenceProjection { - - public readonly string Name; - public readonly MemberReferenceTreatment Treatment; - - public MemberReferenceProjection (MemberReference member, MemberReferenceTreatment treatment) - { - Name = member.Name; - Treatment = treatment; - } - } - sealed class TypeDefinitionProjection { public readonly TypeAttributes Attributes; public readonly string Name; public readonly TypeDefinitionTreatment Treatment; + public readonly Collection RedirectedMethods; + public readonly Collection> RedirectedInterfaces; - public TypeDefinitionProjection (TypeDefinition type, TypeDefinitionTreatment treatment) + public TypeDefinitionProjection (TypeDefinition type, TypeDefinitionTreatment treatment, Collection redirectedMethods, Collection> redirectedInterfaces) { Attributes = type.Attributes; Name = type.Name; Treatment = treatment; + RedirectedMethods = redirectedMethods; + RedirectedInterfaces = redirectedInterfaces; } } @@ -106,16 +98,14 @@ struct ProjectionInfo { public readonly string ClrName; public readonly string ClrAssembly; public readonly bool Attribute; - public readonly bool Disposable; - public ProjectionInfo (string winrt_namespace, string clr_namespace, string clr_name, string clr_assembly, bool attribute = false, bool disposable = false) + public ProjectionInfo (string winrt_namespace, string clr_namespace, string clr_name, string clr_assembly, bool attribute = false) { WinRTNamespace = winrt_namespace; ClrNamespace = clr_namespace; ClrName = clr_name; ClrAssembly = clr_assembly; Attribute = attribute; - Disposable = disposable; } } @@ -163,7 +153,7 @@ static Dictionary Projections { "HResult", new ProjectionInfo ("Windows.Foundation", "System", "Exception", "System.Runtime") }, { "IBindableIterable", new ProjectionInfo ("Windows.UI.Xaml.Interop", "System.Collections", "IEnumerable", "System.Runtime") }, { "IBindableVector", new ProjectionInfo ("Windows.UI.Xaml.Interop", "System.Collections", "IList", "System.Runtime") }, - { "IClosable", new ProjectionInfo ("Windows.Foundation", "System", "IDisposable", "System.Runtime", disposable: true) }, + { "IClosable", new ProjectionInfo ("Windows.Foundation", "System", "IDisposable", "System.Runtime") }, { "ICommand", new ProjectionInfo ("Windows.UI.Xaml.Input", "System.Windows.Input", "ICommand", "System.ObjectModel") }, { "IIterable`1", new ProjectionInfo ("Windows.Foundation.Collections", "System.Collections.Generic", "IEnumerable`1", "System.Runtime") }, { "IKeyValuePair`2", new ProjectionInfo ("Windows.Foundation.Collections", "System.Collections.Generic", "KeyValuePair`2", "System.Runtime") }, @@ -229,21 +219,24 @@ public static void Project (TypeDefinition type) { var treatment = TypeDefinitionTreatment.None; var metadata_kind = type.Module.MetadataKind; + Collection redirectedMethods = null; + Collection> redirectedInterfaces = null; if (type.IsWindowsRuntime) { if (metadata_kind == MetadataKind.WindowsMetadata) { treatment = GetWellKnownTypeDefinitionTreatment (type); if (treatment != TypeDefinitionTreatment.None) { - ApplyProjection (type, new TypeDefinitionProjection (type, treatment)); + ApplyProjection (type, new TypeDefinitionProjection (type, treatment, redirectedMethods, redirectedInterfaces)); return; } var base_type = type.BaseType; - if (base_type != null && IsAttribute (base_type)) + if (base_type != null && IsAttribute (base_type)) { treatment = TypeDefinitionTreatment.NormalAttribute; - else - treatment = TypeDefinitionTreatment.NormalType; - } + } else { + treatment = GenerateRedirectionInformation (type, out redirectedMethods, out redirectedInterfaces); + } + } else if (metadata_kind == MetadataKind.ManagedWindowsMetadata && NeedsWindowsRuntimePrefix (type)) treatment = TypeDefinitionTreatment.PrefixWindowsRuntimeName; @@ -255,7 +248,7 @@ public static void Project (TypeDefinition type) treatment = TypeDefinitionTreatment.UnmangleWindowsRuntimeName; if (treatment != TypeDefinitionTreatment.None) - ApplyProjection (type, new TypeDefinitionProjection (type, treatment)); + ApplyProjection (type, new TypeDefinitionProjection (type, treatment, redirectedMethods, redirectedInterfaces)); } static TypeDefinitionTreatment GetWellKnownTypeDefinitionTreatment (TypeDefinition type) @@ -275,6 +268,106 @@ static TypeDefinitionTreatment GetWellKnownTypeDefinitionTreatment (TypeDefiniti return TypeDefinitionTreatment.None; } + private static TypeDefinitionTreatment GenerateRedirectionInformation (TypeDefinition type, out Collection redirectedMethods, out Collection> redirectedInterfaces) + { + bool implementsProjectedInterface = false; + redirectedMethods = null; + redirectedInterfaces = null; + + foreach (var implementedInterface in type.Interfaces) { + if (IsRedirectedType (implementedInterface.InterfaceType)) { + implementsProjectedInterface = true; + break; + } + } + + if (!implementsProjectedInterface) + return TypeDefinitionTreatment.NormalType; + + var allImplementedInterfaces = new HashSet (new TypeReferenceEqualityComparer ()); + redirectedMethods = new Collection (); + redirectedInterfaces = new Collection> (); + + foreach (var @interface in type.Interfaces) { + var interfaceType = @interface.InterfaceType; + + if (IsRedirectedType (interfaceType)) { + allImplementedInterfaces.Add (interfaceType); + CollectImplementedInterfaces (interfaceType, allImplementedInterfaces); + } + } + + foreach (var implementedInterface in type.Interfaces) { + var interfaceType = implementedInterface.InterfaceType; + if (IsRedirectedType (implementedInterface.InterfaceType)) { + var etype = interfaceType.GetElementType (); + var unprojectedType = new TypeReference (etype.Namespace, etype.Name, etype.Module, etype.Scope) { + DeclaringType = etype.DeclaringType, + projection = etype.projection + }; + + RemoveProjection (unprojectedType); + + var genericInstanceType = interfaceType as GenericInstanceType; + if (genericInstanceType != null) { + var genericUnprojectedType = new GenericInstanceType (unprojectedType); + foreach (var genericArgument in genericInstanceType.GenericArguments) + genericUnprojectedType.GenericArguments.Add (genericArgument); + + unprojectedType = genericUnprojectedType; + } + + var unprojectedInterface = new InterfaceImplementation (unprojectedType); + redirectedInterfaces.Add (new KeyValuePair (implementedInterface, unprojectedInterface)); + } + } + + // Interfaces don't inherit methods of the interfaces they implement + if (!type.IsInterface) { + foreach (var implementedInterface in allImplementedInterfaces) { + RedirectInterfaceMethods (implementedInterface, redirectedMethods); + } + } + + return TypeDefinitionTreatment.RedirectImplementedMethods; + } + + private static void CollectImplementedInterfaces (TypeReference type, HashSet results) + { + var typeResolver = TypeResolver.For (type); + var typeDef = type.Resolve (); + + foreach (var implementedInterface in typeDef.Interfaces) { + var interfaceType = typeResolver.Resolve (implementedInterface.InterfaceType); + results.Add (interfaceType); + CollectImplementedInterfaces (interfaceType, results); + } + } + + private static void RedirectInterfaceMethods (TypeReference interfaceType, Collection redirectedMethods) + { + var typeResolver = TypeResolver.For (interfaceType); + var typeDef = interfaceType.Resolve (); + + foreach (var method in typeDef.Methods) { + var redirectedMethod = new MethodDefinition (method.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot, typeResolver.Resolve (method.ReturnType)); + redirectedMethod.ImplAttributes = MethodImplAttributes.Runtime; + + foreach (var parameter in method.Parameters) { + redirectedMethod.Parameters.Add (new ParameterDefinition (parameter.Name, parameter.Attributes, typeResolver.Resolve (parameter.ParameterType))); + } + + redirectedMethod.Overrides.Add (typeResolver.Resolve (method)); + redirectedMethods.Add (redirectedMethod); + } + } + + private static bool IsRedirectedType (TypeReference type) + { + var typeRefProjection = type.GetElementType ().projection as TypeReferenceProjection; + return typeRefProjection != null && typeRefProjection.Treatment == TypeReferenceTreatment.UseProjectionInfo; + } + static bool NeedsWindowsRuntimePrefix (TypeDefinition type) { if ((type.Attributes & (TypeAttributes.VisibilityMask | TypeAttributes.Interface)) != TypeAttributes.Public) @@ -336,6 +429,32 @@ public static void ApplyProjection (TypeDefinition type, TypeDefinitionProjectio case TypeDefinitionTreatment.RedirectToClrAttribute: type.Attributes = type.Attributes & ~TypeAttributes.Public; break; + + case TypeDefinitionTreatment.RedirectImplementedMethods: { + type.Attributes |= TypeAttributes.WindowsRuntime | TypeAttributes.Import; + + foreach (var redirectedInterfacePair in projection.RedirectedInterfaces) { + type.Interfaces.Add (redirectedInterfacePair.Value); + + foreach (var customAttribute in redirectedInterfacePair.Key.CustomAttributes) + redirectedInterfacePair.Value.CustomAttributes.Add (customAttribute); + + redirectedInterfacePair.Key.CustomAttributes.Clear (); + + foreach (var method in type.Methods) { + foreach (var @override in method.Overrides) { + if (TypeReferenceEqualityComparer.AreEqual (@override.DeclaringType, redirectedInterfacePair.Key.InterfaceType)) { + @override.DeclaringType = redirectedInterfacePair.Value.InterfaceType; + } + } + } + } + + foreach (var method in projection.RedirectedMethods) { + type.Methods.Add (method); + } + } + break; } if ((treatment & TypeDefinitionTreatment.Abstract) != 0) @@ -358,6 +477,28 @@ public static TypeDefinitionProjection RemoveProjection (TypeDefinition type) type.Attributes = projection.Attributes; type.Name = projection.Name; + if (projection.Treatment == TypeDefinitionTreatment.RedirectImplementedMethods) { + foreach (var method in projection.RedirectedMethods) { + type.Methods.Remove (method); + } + + foreach (var redirectedInterfacePair in projection.RedirectedInterfaces) { + foreach (var method in type.Methods) { + foreach (var @override in method.Overrides) { + if (TypeReferenceEqualityComparer.AreEqual (@override.DeclaringType, redirectedInterfacePair.Value.InterfaceType)) { + @override.DeclaringType = redirectedInterfacePair.Key.InterfaceType; + } + } + } + + foreach (var customAttribute in redirectedInterfacePair.Value.CustomAttributes) + redirectedInterfacePair.Key.CustomAttributes.Add (customAttribute); + + redirectedInterfacePair.Value.CustomAttributes.Clear (); + type.Interfaces.Remove (redirectedInterfacePair.Value); + } + } + return projection; } @@ -482,27 +623,16 @@ public static void Project (MethodDefinition method) { var seen_redirected = false; var seen_non_redirected = false; - var disposable = false; - foreach (var @override in method.Overrides) - { - if (@override.MetadataToken.TokenType == TokenType.MemberRef && ImplementsRedirectedInterface (@override, out disposable)) - { + foreach (var @override in method.Overrides) { + if (@override.MetadataToken.TokenType == TokenType.MemberRef && ImplementsRedirectedInterface (@override)) { seen_redirected = true; - if (disposable) - break; - } - else + } else { seen_non_redirected = true; + } } - if (disposable) - { - treatment = MethodDefinitionTreatment.Dispose; - other = false; - } - else if (seen_redirected && !seen_non_redirected) - { + if (seen_redirected && !seen_non_redirected) { treatment = MethodDefinitionTreatment.Runtime | MethodDefinitionTreatment.InternalCall | MethodDefinitionTreatment.Private; other = false; } @@ -540,9 +670,6 @@ public static void ApplyProjection (MethodDefinition method, MethodDefinitionPro var treatment = projection.Treatment; - if ((treatment & MethodDefinitionTreatment.Dispose) != 0) - method.Name = "Dispose"; - if ((treatment & MethodDefinitionTreatment.Abstract) != 0) method.Attributes |= MethodAttributes.Abstract; @@ -615,19 +742,8 @@ public static FieldDefinitionProjection RemoveProjection (FieldDefinition field) return projection; } - public static void Project (MemberReference member) + static bool ImplementsRedirectedInterface (MemberReference member) { - bool disposable; - if (!ImplementsRedirectedInterface (member, out disposable) || !disposable) - return; - - ApplyProjection (member, new MemberReferenceProjection (member, MemberReferenceTreatment.Dispose)); - } - - static bool ImplementsRedirectedInterface (MemberReference member, out bool disposable) - { - disposable = false; - var declaring_type = member.DeclaringType; TypeReference type; switch (declaring_type.MetadataToken.TokenType) { @@ -655,7 +771,6 @@ static bool ImplementsRedirectedInterface (MemberReference member, out bool disp ProjectionInfo info; if (Projections.TryGetValue (type.Name, out info) && type.Namespace == info.WinRTNamespace) { - disposable = info.Disposable; found = true; } @@ -664,29 +779,6 @@ static bool ImplementsRedirectedInterface (MemberReference member, out bool disp return found; } - public static void ApplyProjection (MemberReference member, MemberReferenceProjection projection) - { - if (projection == null) - return; - - if (projection.Treatment == MemberReferenceTreatment.Dispose) - member.Name = "Dispose"; - - member.WindowsRuntimeProjection = projection; - } - - public static MemberReferenceProjection RemoveProjection (MemberReference member) - { - if (!member.IsWindowsRuntimeProjection) - return null; - - var projection = member.WindowsRuntimeProjection; - member.WindowsRuntimeProjection = null; - - member.Name = projection.Name; - - return projection; - } public void AddVirtualReferences (Collection references) { diff --git a/Test/Mono.Cecil.Tests/ILProcessorTests.cs b/Test/Mono.Cecil.Tests/ILProcessorTests.cs index f66d78c7f..17b4fb05f 100644 --- a/Test/Mono.Cecil.Tests/ILProcessorTests.cs +++ b/Test/Mono.Cecil.Tests/ILProcessorTests.cs @@ -66,6 +66,23 @@ public void InsertAfterUsingIndex () AssertOpCodeSequence (new [] { OpCodes.Ldloc_0, OpCodes.Ldloc_1, OpCodes.Ldloc_2, OpCodes.Ldloc_3 }, method); } + [Test] + public void InsertAfterWithLocalScopes () + { + var method = CreateTestMethodWithLocalScopes (); + var il = method.GetILProcessor (); + + il.InsertAfter ( + 0, + il.Create (OpCodes.Nop)); + + AssertOpCodeSequence (new [] { OpCodes.Ldloc_0, OpCodes.Nop, OpCodes.Ldloc_1, OpCodes.Ldloc_2 }, method); + var wholeBodyScope = VerifyWholeBodyScope (method); + AssertLocalScope (wholeBodyScope.Scopes [0], 0, 2); + AssertLocalScope (wholeBodyScope.Scopes [1], 2, 3); + AssertLocalScope (wholeBodyScope.Scopes [2], 3, null); + } + [Test] public void ReplaceUsingIndex () { @@ -77,6 +94,35 @@ public void ReplaceUsingIndex () AssertOpCodeSequence (new [] { OpCodes.Ldloc_0, OpCodes.Nop, OpCodes.Ldloc_3 }, method); } + [Test] + public void ReplaceWithLocalScopes () + { + var method = CreateTestMethodWithLocalScopes (); + var il = method.GetILProcessor (); + + // Replace with larger instruction + var instruction = il.Create (OpCodes.Ldstr, "test"); + instruction.Offset = method.Instructions [1].Offset; + il.Replace (1, instruction); + + AssertOpCodeSequence (new [] { OpCodes.Ldloc_0, OpCodes.Ldstr, OpCodes.Ldloc_2 }, method); + var wholeBodyScope = VerifyWholeBodyScope (method); + AssertLocalScope (wholeBodyScope.Scopes [0], 0, 1); + AssertLocalScope (wholeBodyScope.Scopes [1], 1, 6); // size of the new instruction is 5 bytes + AssertLocalScope (wholeBodyScope.Scopes [2], 6, null); + } + + [Test] + public void Clear () + { + var method = CreateTestMethod (OpCodes.Ldloc_0, OpCodes.Ldloc_2, OpCodes.Ldloc_3); + var il = method.GetILProcessor (); + + il.Clear (); + + AssertOpCodeSequence (new OpCode[] { }, method); + } + static void AssertOpCodeSequence (OpCode [] expected, MethodBody body) { var opcodes = body.Instructions.Select (i => i.OpCode).ToArray (); @@ -97,7 +143,70 @@ static MethodBody CreateTestMethod (params OpCode [] opcodes) foreach (var opcode in opcodes) il.Emit (opcode); + var instructions = method.Body.Instructions; + int size = 0; + for (int i = 0; i < instructions.Count; i++) { + var instruction = instructions [i]; + instruction.Offset = size; + size += instruction.GetSize (); + } + return method.Body; } + + static ScopeDebugInformation VerifyWholeBodyScope (MethodBody body) + { + var debug_info = body.Method.DebugInformation; + Assert.IsNotNull (debug_info); + AssertLocalScope (debug_info.Scope, 0, null); + return debug_info.Scope; + } + + static void AssertLocalScope (ScopeDebugInformation scope, int startOffset, int? endOffset) + { + Assert.IsNotNull (scope); + Assert.AreEqual (startOffset, scope.Start.Offset); + if (endOffset.HasValue) + Assert.AreEqual (endOffset.Value, scope.End.Offset); + else + Assert.IsTrue (scope.End.IsEndOfMethod); + } + + static MethodBody CreateTestMethodWithLocalScopes () + { + var methodBody = CreateTestMethod (OpCodes.Ldloc_0, OpCodes.Ldloc_1, OpCodes.Ldloc_2); + var method = methodBody.Method; + var debug_info = method.DebugInformation; + + var wholeBodyScope = new ScopeDebugInformation () { + Start = new InstructionOffset (0), + End = new InstructionOffset () + }; + int size = 0; + var instruction = methodBody.Instructions [0]; + var innerScopeBegining = new ScopeDebugInformation () { + Start = new InstructionOffset (size), + End = new InstructionOffset (size + instruction.GetSize ()) + }; + size += instruction.GetSize (); + wholeBodyScope.Scopes.Add (innerScopeBegining); + + instruction = methodBody.Instructions [1]; + var innerScopeMiddle = new ScopeDebugInformation () { + Start = new InstructionOffset (size), + End = new InstructionOffset (size + instruction.GetSize ()) + }; + size += instruction.GetSize (); + wholeBodyScope.Scopes.Add (innerScopeMiddle); + + var innerScopeEnd = new ScopeDebugInformation () { + Start = new InstructionOffset (size), + End = new InstructionOffset () + }; + wholeBodyScope.Scopes.Add (innerScopeEnd); + + debug_info.Scope = wholeBodyScope; + return methodBody; + } } } diff --git a/Test/Mono.Cecil.Tests/LoadAssemblyDefinitionForTestsBaseSimple.cs b/Test/Mono.Cecil.Tests/LoadAssemblyDefinitionForTestsBaseSimple.cs new file mode 100644 index 000000000..5c3b71385 --- /dev/null +++ b/Test/Mono.Cecil.Tests/LoadAssemblyDefinitionForTestsBaseSimple.cs @@ -0,0 +1,16 @@ +using System.Reflection; + +namespace Mono.Cecil.Tests { + + public class LoadAssemblyDefinitionForTestsBaseSimple { + + protected AssemblyDefinition _assembly; + protected AssemblyDefinition _mscorlib; + + public void SetupAssemblyDefinitions (Assembly testAssembly) + { + _assembly = AssemblyDefinition.ReadAssembly (testAssembly.Location); + _mscorlib = _assembly.MainModule.TypeSystem.Object.Resolve ().Module.Assembly; + } + } +} diff --git a/Test/Mono.Cecil.Tests/MethodReferenceComparerTests.cs b/Test/Mono.Cecil.Tests/MethodReferenceComparerTests.cs new file mode 100644 index 000000000..92e5de358 --- /dev/null +++ b/Test/Mono.Cecil.Tests/MethodReferenceComparerTests.cs @@ -0,0 +1,135 @@ +using NUnit.Framework; +using System; +using System.Linq; + +namespace Mono.Cecil.Tests { + + [TestFixture] + public class MethodReferenceComparerTests : LoadAssemblyDefinitionForTestsBaseSimple { + + private TypeDefinition _class1; + private TypeDefinition _class2; + + [SetUp] + public void SetUp () + { + SetupAssemblyDefinitions (typeof (MethodReferenceComparerTests).Assembly); + _class1 = TypeDefinitionUtils.TypeDefinitionFor (typeof (Class1), _assembly); + _class2 = TypeDefinitionUtils.TypeDefinitionFor (typeof (Class2), _assembly); + } + + [Test] + public void MethodReferenceEqualsMethodDefinition () + { + var typeDefinition = TypeDefinitionUtils.TypeDefinitionFor (typeof (Int32), _mscorlib); + var method = typeDefinition.Methods.Single (m => m.Name == "GetHashCode"); + var methodReference = new MethodReference (method.Name, method.ReturnType, method.DeclaringType); + methodReference.HasThis = method.HasThis; + + Assert.That (MethodReferenceComparer.AreEqual (method, methodReference), Is.True); + } + + [Test] + public void VerifyMethodSignatureMatches () + { + Assert.IsTrue (CompareSignatures ("MethodWithNoParametersOrReturn")); + Assert.IsTrue (CompareSignatures ("GenericMethodWithNoParametersOrReturn")); + Assert.IsFalse (CompareSignatures ("MethodWithNoParametersOrReturn", "GenericMethodWithNoParametersOrReturn")); + + Assert.IsTrue (CompareSignatures ("MethodWithIntParameterAndVoidReturn")); + } + + [Test] + public void VerifySignatureComparisonConsidersStatic () + { + Assert.IsTrue (CompareSignatures ("StaticMethodWithNoParametersOrReturn")); + Assert.IsTrue (CompareSignatures ("StaticMethodWithNoParametersOrReturn")); + Assert.IsFalse (CompareSignatures ("MethodWithNoParametersOrReturn", "StaticMethodWithNoParametersOrReturn")); + Assert.IsFalse (CompareSignatures ("GenericMethodWithNoParametersOrReturn", "GenericStaticMethodWithNoParametersOrReturn")); + } + + [Test] + public void VerifyMethodSignatureWithGenericParameters () + { + Assert.IsTrue (CompareSignatures ("GenericMethodWithGenericParameter")); + Assert.IsTrue (CompareSignatures ("GenericMethodWithGenericParameterArray")); + Assert.IsTrue (CompareSignatures ("GenericMethodWithByReferenceGenericParameter")); + Assert.IsTrue (CompareSignatures ("GenericMethodWithGenericInstanceGenericParameter")); + } + + [Test] + public void VerifyNonResolvableMethodReferencesWithDifferentParameterTypesAreNotEqual () + { + var method1 = new MethodReference ("TestMethod", _class1.Module.TypeSystem.Void, _class1); + method1.Parameters.Add (new ParameterDefinition (new ByReferenceType (_class1.Module.TypeSystem.Int16))); + + var method2 = new MethodReference ("TestMethod", _class1.Module.TypeSystem.Void, _class1); + method2.Parameters.Add (new ParameterDefinition (new ByReferenceType (_class1.Module.TypeSystem.Char))); + + Assert.IsFalse (MethodReferenceComparer.AreEqual (method1, method2)); + } + + [Test] + public void VerifyNonResolvableRecursiveMethodsDontStackOverflow () + { + var method1 = new MethodReference ("TestMethod", _class1.Module.TypeSystem.Void, _class1); + method1.GenericParameters.Add (new GenericParameter (method1)); + method1.Parameters.Add (new ParameterDefinition (method1.GenericParameters[0])); + + var method2 = new MethodReference ("TestMethod", _class1.Module.TypeSystem.Void, _class1); + method2.GenericParameters.Add (new GenericParameter (method2)); + method2.Parameters.Add (new ParameterDefinition (method2.GenericParameters[0])); + + Assert.IsTrue (MethodReferenceComparer.AreEqual (method1, method2)); + } + + bool CompareSignatures (string name) + { + return CompareSignatures (name, name); + } + + bool CompareSignatures (string name1, string name2) + { + return MethodReferenceComparer.AreSignaturesEqual (GetMethod (_class1, name1), GetMethod (_class2, name2), TypeComparisonMode.SignatureOnly); + } + + static MethodDefinition GetMethod (TypeDefinition type, string name) + { + return type.Methods.Single (m => m.Name == name); + } + + class GenericClass { + + } + + class Class1 { + + void MethodWithNoParametersOrReturn () {} + void GenericMethodWithNoParametersOrReturn () {} + static void StaticMethodWithNoParametersOrReturn () {} + static void GenericStaticMethodWithNoParametersOrReturn () {} + + void MethodWithIntParameterAndVoidReturn (int a) {} + + void GenericMethodWithGenericParameter (T t) {} + void GenericMethodWithGenericParameterArray (T[] t) {} + void GenericMethodWithByReferenceGenericParameter (ref T a) {} + void GenericMethodWithGenericInstanceGenericParameter (GenericClass a) {} + } + + class Class2 { + + void MethodWithNoParametersOrReturn () {} + void GenericMethodWithNoParametersOrReturn () {} + static void StaticMethodWithNoParametersOrReturn () {} + static void GenericStaticMethodWithNoParametersOrReturn () {} + + void MethodWithIntParameterAndVoidReturn (int a) {} + + void GenericMethodWithGenericParameter (T t) {} + void GenericMethodWithGenericParameterArray (T[] t) {} + void GenericMethodWithByReferenceGenericParameter (ref T a) {} + void GenericMethodWithGenericInstanceGenericParameter (GenericClass a) {} + } + } +} diff --git a/Test/Mono.Cecil.Tests/TypeDefinitionUtils.cs b/Test/Mono.Cecil.Tests/TypeDefinitionUtils.cs new file mode 100644 index 000000000..73245840b --- /dev/null +++ b/Test/Mono.Cecil.Tests/TypeDefinitionUtils.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Mono.Cecil { + + public static class TypeDefinitionUtils { + + public static TypeReference TypeDefinitionForGeneric ( + Type genericType, AssemblyDefinition genericAssemblyDefinition, + Type paramterType, AssemblyDefinition parameterAssemblyDefinition) + { + var paramDefinition = TypeDefinitionUtils.TypeDefinitionFor (paramterType, parameterAssemblyDefinition); + var genericDefinition = TypeDefinitionUtils.TypeDefinitionFor (genericType, genericAssemblyDefinition); + var genericInstance = new GenericInstanceType (genericDefinition); + genericInstance.GenericArguments.Add (paramDefinition); + return genericInstance; + } + + public static TypeDefinition TypeDefinitionFor (Type type, AssemblyDefinition assemblyDefinition) + { + var stack = new Stack (); + var currentType = type; + while (currentType != null) { + stack.Push ( (currentType.DeclaringType == null ? currentType.Namespace + "." : "") + currentType.Name); + currentType = currentType.DeclaringType; + } + + var typeDefinition = assemblyDefinition.MainModule.GetType (stack.Pop ()); + if (typeDefinition == null) + return null; + + while (stack.Count > 0) { + var name = stack.Pop (); + typeDefinition = typeDefinition.NestedTypes.Single (t => t.Name == name); + } + + return typeDefinition; + } + } +} diff --git a/Test/Mono.Cecil.Tests/TypeReferenceComparisonTests.cs b/Test/Mono.Cecil.Tests/TypeReferenceComparisonTests.cs new file mode 100644 index 000000000..d8332eba0 --- /dev/null +++ b/Test/Mono.Cecil.Tests/TypeReferenceComparisonTests.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using Mono.Cecil; +using NUnit.Framework; + +namespace Mono.Cecil.Tests { + + [TestFixture] + public class TypeReferenceComparisonTests : LoadAssemblyDefinitionForTestsBaseSimple { + + [SetUp] + public void SetUp () + { + SetupAssemblyDefinitions (typeof (TypeReferenceComparisonTests).Assembly); + } + + [Test] + public void TypeReferenceEqualsTypeDefinition () + { + var typeDefinition = TypeDefinitionUtils.TypeDefinitionFor (typeof (Int32), _mscorlib); + var typeReference = new TypeReference (typeDefinition.Namespace, typeDefinition.Name, typeDefinition.Module, typeDefinition.Scope); + + Assert.That (TypeReferenceEqualityComparer.AreEqual (typeDefinition, typeReference), Is.True); + } + + [Test] + public void GenericParametersFromTwoTypesAreNotEqual () + { + var listDefinition = TypeDefinitionUtils.TypeDefinitionFor (typeof (List<>), _mscorlib); + var stackDefinition = TypeDefinitionUtils.TypeDefinitionFor (typeof (Comparer<>), _mscorlib); + + Assert.That (TypeReferenceEqualityComparer.AreEqual (listDefinition.GenericParameters[0], stackDefinition.GenericParameters[0]), Is.False); + } + + [Test] + public void ArrayTypesDoNotMatchIfRankIsDifferent () + { + var elementType = TypeDefinitionUtils.TypeDefinitionFor (typeof (Int32), _mscorlib); + + Assert.That (TypeReferenceEqualityComparer.AreEqual (new ArrayType (elementType, 1), new ArrayType (elementType, 2)), Is.False, "Two array types with different ranks match, which is not expected."); + } + + [Test] + public void ArrayTypesDoNotMatchIfElementTypeIsDifferent () + { + Assert.That (TypeReferenceEqualityComparer.AreEqual (new ArrayType (TypeDefinitionUtils.TypeDefinitionFor (typeof (Int32), _mscorlib), 1), new ArrayType (TypeDefinitionUtils.TypeDefinitionFor (typeof (Int64), _mscorlib), 1)), Is.False, "Two array types with different element types match, which is not expected."); + } + + [Test] + public void ArrayTypesWithDifferentRanksToNotMatch () + { + var elementType = TypeDefinitionUtils.TypeDefinitionFor (typeof (Int32), _mscorlib); + + Assert.That (TypeReferenceEqualityComparer.AreEqual ( (TypeSpecification) new ArrayType (elementType, 1), (TypeSpecification) new ArrayType (elementType, 2)), Is.False, "Two type specifications that are array types with different ranks match, which is not expected."); + } + + [Test] + public void GenericInstanceTypeFromTwoTypesAreNotEqual () + { + var int32Definition = TypeDefinitionUtils.TypeDefinitionFor (typeof (Int32), _mscorlib); + var listDefinition = TypeDefinitionUtils.TypeDefinitionFor (typeof (List<>), _mscorlib); + var listGenericInstance = new GenericInstanceType (listDefinition); + listGenericInstance.GenericArguments.Add (int32Definition); + var stackDefinition = TypeDefinitionUtils.TypeDefinitionFor (typeof (Comparer<>), _mscorlib); + var stackGenericInstance = new GenericInstanceType (stackDefinition); + stackGenericInstance.GenericArguments.Add (int32Definition); + + Assert.That (TypeReferenceEqualityComparer.AreEqual (listGenericInstance, stackGenericInstance), Is.False); + } + + [Test] + public void GenericInstanceTypeForSameTypeIsEqual () + { + var int32Definition = TypeDefinitionUtils.TypeDefinitionFor (typeof (Int32), _mscorlib); + var listDefinition = TypeDefinitionUtils.TypeDefinitionFor (typeof (List<>), _mscorlib); + var listGenericInstance = new GenericInstanceType (listDefinition); + listGenericInstance.GenericArguments.Add (int32Definition); + var listGenericInstance2 = new GenericInstanceType (listDefinition); + listGenericInstance2.GenericArguments.Add (int32Definition); + + Assert.That (TypeReferenceEqualityComparer.AreEqual (listGenericInstance, listGenericInstance2), Is.True); + } + } +} diff --git a/Test/Mono.Cecil.Tests/VariableTests.cs b/Test/Mono.Cecil.Tests/VariableTests.cs index 577e56f5a..5454b937f 100644 --- a/Test/Mono.Cecil.Tests/VariableTests.cs +++ b/Test/Mono.Cecil.Tests/VariableTests.cs @@ -80,6 +80,45 @@ public void RemoveVariableIndex () Assert.AreEqual (1, z.Index); } + [Test] + public void RemoveVariableWithDebugInfo () + { + var object_ref = new TypeReference ("System", "Object", null, null, false); + var method = new MethodDefinition ("foo", MethodAttributes.Static, object_ref); + var body = new MethodBody (method); + var il = body.GetILProcessor (); + il.Emit (OpCodes.Ret); + + var x = new VariableDefinition (object_ref); + var y = new VariableDefinition (object_ref); + var z = new VariableDefinition (object_ref); + var z2 = new VariableDefinition (object_ref); + + body.Variables.Add (x); + body.Variables.Add (y); + body.Variables.Add (z); + body.Variables.Add (z2); + + var scope = new ScopeDebugInformation (body.Instructions [0], body.Instructions [0]); + method.DebugInformation = new MethodDebugInformation (method) { + Scope = scope + }; + scope.Variables.Add (new VariableDebugInformation (x.index, nameof (x))); + scope.Variables.Add (new VariableDebugInformation (y.index, nameof (y))); + scope.Variables.Add (new VariableDebugInformation (z.index, nameof (z))); + scope.Variables.Add (new VariableDebugInformation (z2, nameof (z2))); + + body.Variables.Remove (y); + + Assert.AreEqual (3, scope.Variables.Count); + Assert.AreEqual (x.Index, scope.Variables [0].Index); + Assert.AreEqual (nameof (x), scope.Variables [0].Name); + Assert.AreEqual (z.Index, scope.Variables [1].Index); + Assert.AreEqual (nameof (z), scope.Variables [1].Name); + Assert.AreEqual (z2.Index, scope.Variables [2].Index); + Assert.AreEqual (nameof (z2), scope.Variables [2].Name); + } + [Test] public void InsertVariableIndex () { @@ -104,5 +143,43 @@ public void InsertVariableIndex () Assert.AreEqual (1, y.Index); Assert.AreEqual (2, z.Index); } + + [Test] + public void InsertVariableWithDebugInfo () + { + var object_ref = new TypeReference ("System", "Object", null, null, false); + var method = new MethodDefinition ("foo", MethodAttributes.Static, object_ref); + var body = new MethodBody (method); + var il = body.GetILProcessor (); + il.Emit (OpCodes.Ret); + + var x = new VariableDefinition (object_ref); + var y = new VariableDefinition (object_ref); + var z = new VariableDefinition (object_ref); + var z2 = new VariableDefinition (object_ref); + + body.Variables.Add (x); + body.Variables.Add (z); + body.Variables.Add (z2); + + var scope = new ScopeDebugInformation (body.Instructions [0], body.Instructions [0]); + method.DebugInformation = new MethodDebugInformation (method) { + Scope = scope + }; + scope.Variables.Add (new VariableDebugInformation (x.index, nameof (x))); + scope.Variables.Add (new VariableDebugInformation (z.index, nameof (z))); + scope.Variables.Add (new VariableDebugInformation (z2, nameof (z2))); + + body.Variables.Insert (1, y); + + // Adding local variable doesn't add debug info for it (since there's no way to deduce the name of the variable) + Assert.AreEqual (3, scope.Variables.Count); + Assert.AreEqual (x.Index, scope.Variables [0].Index); + Assert.AreEqual (nameof (x), scope.Variables [0].Name); + Assert.AreEqual (z.Index, scope.Variables [1].Index); + Assert.AreEqual (nameof (z), scope.Variables [1].Name); + Assert.AreEqual (z2.Index, scope.Variables [2].Index); + Assert.AreEqual (nameof (z2), scope.Variables [2].Name); + } } } diff --git a/Test/Mono.Cecil.Tests/WindowsRuntimeProjectionsTests.cs b/Test/Mono.Cecil.Tests/WindowsRuntimeProjectionsTests.cs index 9b10fd5cf..bac85cfff 100644 --- a/Test/Mono.Cecil.Tests/WindowsRuntimeProjectionsTests.cs +++ b/Test/Mono.Cecil.Tests/WindowsRuntimeProjectionsTests.cs @@ -8,8 +8,10 @@ using System.Text; namespace Mono.Cecil.Tests { + [TestFixture] public abstract class BaseWindowsRuntimeProjectionsTests : BaseTestFixture { + protected abstract string ModuleName { get; } protected abstract MetadataKind ExpectedMetadataKind { get; } protected abstract string [] ManagedClassTypeNames { get; } @@ -96,6 +98,7 @@ public void CanStripType () [TestFixture] public class ManagedWindowsRuntimeProjectionsTests : BaseWindowsRuntimeProjectionsTests { + protected override string ModuleName { get { return "ManagedWinmd.winmd"; } } protected override MetadataKind ExpectedMetadataKind { get { return MetadataKind.ManagedWindowsMetadata; } } @@ -132,6 +135,7 @@ public void CanProjectClasses () [TestFixture] public class NativeWindowsRuntimeProjectionsTests : BaseWindowsRuntimeProjectionsTests { + protected override string ModuleName { get { return "NativeWinmd.winmd"; } } protected override MetadataKind ExpectedMetadataKind { get { return MetadataKind.WindowsMetadata; } } @@ -139,6 +143,146 @@ public class NativeWindowsRuntimeProjectionsTests : BaseWindowsRuntimeProjection protected override string [] ManagedClassTypeNames { get { return new [] { "ManagedClass" }; } } protected override string [] CustomListTypeNames { get { return new [] { "CustomList" }; } } + + [Test] + public void CanProjectAndRedirectInterfaces () + { + if (Platform.OnMono) + return; + + TestModule (ModuleName, (module) => { + var customListClass = module.Types.Single (t => t.Name == "CustomList"); + Assert.AreEqual (5, customListClass.Interfaces.Count); + + Assert.AreEqual (1, customListClass.Interfaces[0].CustomAttributes.Count); + Assert.AreEqual ("Windows.Foundation.Metadata.DefaultAttribute", customListClass.Interfaces[0].CustomAttributes[0].AttributeType.FullName); + Assert.AreEqual ("NativeWinmd.__ICustomListPublicNonVirtuals", customListClass.Interfaces[0].InterfaceType.FullName); + + Assert.AreEqual (0, customListClass.Interfaces[1].CustomAttributes.Count); + Assert.AreEqual ("System.Collections.Generic.IList`1", customListClass.Interfaces[1].InterfaceType.FullName); + + Assert.AreEqual (0, customListClass.Interfaces[2].CustomAttributes.Count); + Assert.AreEqual ("System.Collections.Generic.IEnumerable`1", customListClass.Interfaces[2].InterfaceType.FullName); + + Assert.AreEqual (0, customListClass.Interfaces[3].CustomAttributes.Count); + Assert.AreEqual ("Windows.Foundation.Collections.IVector`1", customListClass.Interfaces[3].InterfaceType.FullName); + + Assert.AreEqual (0, customListClass.Interfaces[4].CustomAttributes.Count); + Assert.AreEqual ("Windows.Foundation.Collections.IIterable`1", customListClass.Interfaces[4].InterfaceType.FullName); + + var customPropertySetClass = module.Types.Single (t => t.Name == "CustomPropertySet"); + Assert.AreEqual (7, customPropertySetClass.Interfaces.Count); + + Assert.AreEqual (0, customPropertySetClass.Interfaces[0].CustomAttributes.Count); + Assert.AreEqual ("Windows.Foundation.Collections.IPropertySet", customPropertySetClass.Interfaces[0].InterfaceType.FullName); + + Assert.AreEqual (1, customPropertySetClass.Interfaces[1].CustomAttributes.Count); + Assert.AreEqual ("Windows.Foundation.Metadata.DefaultAttribute", customPropertySetClass.Interfaces[1].CustomAttributes[0].AttributeType.FullName); + Assert.AreEqual ("NativeWinmd.__ICustomPropertySetPublicNonVirtuals", customPropertySetClass.Interfaces[1].InterfaceType.FullName); + + Assert.AreEqual (0, customPropertySetClass.Interfaces[2].CustomAttributes.Count); + Assert.AreEqual ("Windows.Foundation.Collections.IObservableMap`2", customPropertySetClass.Interfaces[2].InterfaceType.FullName); + + Assert.AreEqual (0, customPropertySetClass.Interfaces[3].CustomAttributes.Count); + Assert.AreEqual ("System.Collections.Generic.IDictionary`2", customPropertySetClass.Interfaces[3].InterfaceType.FullName); + + Assert.AreEqual (0, customPropertySetClass.Interfaces[4].CustomAttributes.Count); + Assert.AreEqual ("System.Collections.Generic.IEnumerable`1>", customPropertySetClass.Interfaces[4].InterfaceType.FullName); + + Assert.AreEqual (0, customPropertySetClass.Interfaces[5].CustomAttributes.Count); + Assert.AreEqual ("Windows.Foundation.Collections.IMap`2", customPropertySetClass.Interfaces[5].InterfaceType.FullName); + + Assert.AreEqual (0, customPropertySetClass.Interfaces[6].CustomAttributes.Count); + Assert.AreEqual ("Windows.Foundation.Collections.IIterable`1>", customPropertySetClass.Interfaces[6].InterfaceType.FullName); + + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (), applyWindowsRuntimeProjections: true); + } + + [Test] + public void CanProjectInterfaceMethods () + { + if (Platform.OnMono) + return; + + TestModule (ModuleName, (module) => { + var customListClass = module.Types.Single (t => t.Name == "CustomList"); + Assert.AreEqual (28, customListClass.Methods.Count); + Assert.AreEqual (TypeDefinitionTreatment.RedirectImplementedMethods, customListClass.WindowsRuntimeProjection.Treatment); + + // Verify that projections add implementations for all projected interfaces methods + Assert.AreEqual (customListClass.Methods[0].FullName, "System.Void NativeWinmd.CustomList::.ctor()"); + Assert.AreEqual (customListClass.Methods[1].FullName, "Windows.Foundation.Collections.IIterator`1 NativeWinmd.CustomList::First()"); + Assert.AreEqual (customListClass.Methods[2].FullName, "System.UInt32 NativeWinmd.CustomList::get_Size()"); + Assert.AreEqual (customListClass.Methods[3].FullName, "System.Int32 NativeWinmd.CustomList::GetAt(System.UInt32)"); + Assert.AreEqual (customListClass.Methods[4].FullName, "System.Collections.Generic.IReadOnlyList`1 NativeWinmd.CustomList::GetView()"); + Assert.AreEqual (customListClass.Methods[5].FullName, "System.Boolean NativeWinmd.CustomList::IndexOf(System.Int32,System.UInt32&)"); + Assert.AreEqual (customListClass.Methods[6].FullName, "System.Void NativeWinmd.CustomList::SetAt(System.UInt32,System.Int32)"); + Assert.AreEqual (customListClass.Methods[7].FullName, "System.Void NativeWinmd.CustomList::InsertAt(System.UInt32,System.Int32)"); + Assert.AreEqual (customListClass.Methods[8].FullName, "System.Void NativeWinmd.CustomList::RemoveAt(System.UInt32)"); + Assert.AreEqual (customListClass.Methods[9].FullName, "System.Void NativeWinmd.CustomList::Append(System.Int32)"); + Assert.AreEqual (customListClass.Methods[10].FullName, "System.Void NativeWinmd.CustomList::RemoveAtEnd()"); + Assert.AreEqual (customListClass.Methods[11].FullName, "System.Void NativeWinmd.CustomList::Clear()"); + Assert.AreEqual (customListClass.Methods[12].FullName, "System.UInt32 NativeWinmd.CustomList::GetMany(System.UInt32,System.Int32[])"); + Assert.AreEqual (customListClass.Methods[13].FullName, "System.Void NativeWinmd.CustomList::ReplaceAll(System.Int32[])"); + Assert.AreEqual (customListClass.Methods[14].FullName, "System.Int32 NativeWinmd.CustomList::get_Item(System.Int32)"); + Assert.AreEqual (customListClass.Methods[15].FullName, "System.Void NativeWinmd.CustomList::set_Item(System.Int32,System.Int32)"); + Assert.AreEqual (customListClass.Methods[16].FullName, "System.Int32 NativeWinmd.CustomList::IndexOf(System.Int32)"); + Assert.AreEqual (customListClass.Methods[17].FullName, "System.Void NativeWinmd.CustomList::Insert(System.Int32,System.Int32)"); + Assert.AreEqual (customListClass.Methods[18].FullName, "System.Void NativeWinmd.CustomList::RemoveAt(System.Int32)"); + Assert.AreEqual (customListClass.Methods[19].FullName, "System.Int32 NativeWinmd.CustomList::get_Count()"); + Assert.AreEqual (customListClass.Methods[20].FullName, "System.Boolean NativeWinmd.CustomList::get_IsReadOnly()"); + Assert.AreEqual (customListClass.Methods[21].FullName, "System.Void NativeWinmd.CustomList::Add(System.Int32)"); + Assert.AreEqual (customListClass.Methods[22].FullName, "System.Void NativeWinmd.CustomList::Clear()"); + Assert.AreEqual (customListClass.Methods[23].FullName, "System.Boolean NativeWinmd.CustomList::Contains(System.Int32)"); + Assert.AreEqual (customListClass.Methods[24].FullName, "System.Void NativeWinmd.CustomList::CopyTo(System.Int32[],System.Int32)"); + Assert.AreEqual (customListClass.Methods[25].FullName, "System.Boolean NativeWinmd.CustomList::Remove(System.Int32)"); + Assert.AreEqual (customListClass.Methods[26].FullName, "System.Collections.Generic.IEnumerator`1 NativeWinmd.CustomList::GetEnumerator()"); + Assert.AreEqual (customListClass.Methods[27].FullName, "System.Collections.IEnumerator NativeWinmd.CustomList::GetEnumerator()"); + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (), applyWindowsRuntimeProjections: true); + } + + [Test] + public void CanProjectMethodOverrides () + { + if (Platform.OnMono) + return; + + TestModule (ModuleName, (module) => { + var customListClass = module.Types.Single (t => t.Name == "CustomList"); + + for (int i = 1; i < customListClass.Methods.Count; i++) + Assert.AreEqual (1, customListClass.Methods[i].Overrides.Count); + + Assert.AreEqual (customListClass.Methods[1].Overrides[0].FullName, "Windows.Foundation.Collections.IIterator`1 Windows.Foundation.Collections.IIterable`1::First()"); + Assert.AreEqual (customListClass.Methods[2].Overrides[0].FullName, "System.UInt32 Windows.Foundation.Collections.IVector`1::get_Size()"); + Assert.AreEqual (customListClass.Methods[3].Overrides[0].FullName, "!0 Windows.Foundation.Collections.IVector`1::GetAt(System.UInt32)"); + Assert.AreEqual (customListClass.Methods[4].Overrides[0].FullName, "System.Collections.Generic.IReadOnlyList`1 Windows.Foundation.Collections.IVector`1::GetView()"); + Assert.AreEqual (customListClass.Methods[5].Overrides[0].FullName, "System.Boolean Windows.Foundation.Collections.IVector`1::IndexOf(!0,System.UInt32&)"); + Assert.AreEqual (customListClass.Methods[6].Overrides[0].FullName, "System.Void Windows.Foundation.Collections.IVector`1::SetAt(System.UInt32,!0)"); + Assert.AreEqual (customListClass.Methods[7].Overrides[0].FullName, "System.Void Windows.Foundation.Collections.IVector`1::InsertAt(System.UInt32,!0)"); + Assert.AreEqual (customListClass.Methods[8].Overrides[0].FullName, "System.Void Windows.Foundation.Collections.IVector`1::RemoveAt(System.UInt32)"); + Assert.AreEqual (customListClass.Methods[9].Overrides[0].FullName, "System.Void Windows.Foundation.Collections.IVector`1::Append(!0)"); + Assert.AreEqual (customListClass.Methods[10].Overrides[0].FullName, "System.Void Windows.Foundation.Collections.IVector`1::RemoveAtEnd()"); + Assert.AreEqual (customListClass.Methods[11].Overrides[0].FullName, "System.Void Windows.Foundation.Collections.IVector`1::Clear()"); + Assert.AreEqual (customListClass.Methods[12].Overrides[0].FullName, "System.UInt32 Windows.Foundation.Collections.IVector`1::GetMany(System.UInt32,!0[])"); + Assert.AreEqual (customListClass.Methods[13].Overrides[0].FullName, "System.Void Windows.Foundation.Collections.IVector`1::ReplaceAll(!0[])"); + Assert.AreEqual (customListClass.Methods[14].Overrides[0].FullName, "T System.Collections.Generic.IList`1::get_Item(System.Int32)"); + Assert.AreEqual (customListClass.Methods[15].Overrides[0].FullName, "System.Void System.Collections.Generic.IList`1::set_Item(System.Int32,T)"); + Assert.AreEqual (customListClass.Methods[16].Overrides[0].FullName, "System.Int32 System.Collections.Generic.IList`1::IndexOf(T)"); + Assert.AreEqual (customListClass.Methods[17].Overrides[0].FullName, "System.Void System.Collections.Generic.IList`1::Insert(System.Int32,T)"); + Assert.AreEqual (customListClass.Methods[18].Overrides[0].FullName, "System.Void System.Collections.Generic.IList`1::RemoveAt(System.Int32)"); + Assert.AreEqual (customListClass.Methods[19].Overrides[0].FullName, "System.Int32 System.Collections.Generic.ICollection`1::get_Count()"); + Assert.AreEqual (customListClass.Methods[20].Overrides[0].FullName, "System.Boolean System.Collections.Generic.ICollection`1::get_IsReadOnly()"); + Assert.AreEqual (customListClass.Methods[21].Overrides[0].FullName, "System.Void System.Collections.Generic.ICollection`1::Add(T)"); + Assert.AreEqual (customListClass.Methods[22].Overrides[0].FullName, "System.Void System.Collections.Generic.ICollection`1::Clear()"); + Assert.AreEqual (customListClass.Methods[23].Overrides[0].FullName, "System.Boolean System.Collections.Generic.ICollection`1::Contains(T)"); + Assert.AreEqual (customListClass.Methods[24].Overrides[0].FullName, "System.Void System.Collections.Generic.ICollection`1::CopyTo(T[],System.Int32)"); + Assert.AreEqual (customListClass.Methods[25].Overrides[0].FullName, "System.Boolean System.Collections.Generic.ICollection`1::Remove(T)"); + Assert.AreEqual (customListClass.Methods[26].Overrides[0].FullName, "System.Collections.Generic.IEnumerator`1 System.Collections.Generic.IEnumerable`1::GetEnumerator()"); + Assert.AreEqual (customListClass.Methods[27].Overrides[0].FullName, "System.Collections.IEnumerator System.Collections.IEnumerable::GetEnumerator()"); + + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (), applyWindowsRuntimeProjections: true); + } } } #endif diff --git a/Test/Resources/assemblies/NativeWinmd.winmd b/Test/Resources/assemblies/NativeWinmd.winmd index 53fedff79..b6417cffa 100644 Binary files a/Test/Resources/assemblies/NativeWinmd.winmd and b/Test/Resources/assemblies/NativeWinmd.winmd differ diff --git a/symbols/mdb/Mono.Cecil.Mdb/MdbWriter.cs b/symbols/mdb/Mono.Cecil.Mdb/MdbWriter.cs index becbb4f88..9b565187d 100644 --- a/symbols/mdb/Mono.Cecil.Mdb/MdbWriter.cs +++ b/symbols/mdb/Mono.Cecil.Mdb/MdbWriter.cs @@ -25,7 +25,7 @@ public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName) Mixin.CheckModule (module); Mixin.CheckFileName (fileName); - return new MdbWriter (module.Mvid, fileName); + return new MdbWriter (module, fileName); } public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream) @@ -36,13 +36,13 @@ public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStre public sealed class MdbWriter : ISymbolWriter { - readonly Guid mvid; + readonly ModuleDefinition module; readonly MonoSymbolWriter writer; readonly Dictionary source_files; - public MdbWriter (Guid mvid, string assembly) + public MdbWriter (ModuleDefinition module, string assembly) { - this.mvid = mvid; + this.module = module; this.writer = new MonoSymbolWriter (assembly); this.source_files = new Dictionary (); } @@ -169,7 +169,7 @@ public ImageDebugHeader GetDebugHeader () public void Dispose () { - writer.WriteSymbolFile (mvid); + writer.WriteSymbolFile (module.Mvid); } class SourceFile : ISourceFile { diff --git a/symbols/mdb/Test/Mono.Cecil.Tests/MdbTests.cs b/symbols/mdb/Test/Mono.Cecil.Tests/MdbTests.cs index 6041efaac..8af9e7c58 100644 --- a/symbols/mdb/Test/Mono.Cecil.Tests/MdbTests.cs +++ b/symbols/mdb/Test/Mono.Cecil.Tests/MdbTests.cs @@ -1,6 +1,8 @@ +using Mono.Cecil.Cil; using Mono.Cecil.Mdb; using NUnit.Framework; +using System.IO; namespace Mono.Cecil.Tests { @@ -80,5 +82,19 @@ public void PartialClass () }, symbolReaderProvider: typeof(MdbReaderProvider), symbolWriterProvider: typeof(MdbWriterProvider)); } + + [Test] + public void WriteAndReadAgainModuleWithDeterministicMvid () + { + const string resource = "simplemdb.exe"; + string destination = Path.GetTempFileName (); + + using (var module = GetResourceModule (resource, new ReaderParameters { SymbolReaderProvider = new DefaultSymbolReaderProvider (true) })) { + module.Write (destination, new WriterParameters { WriteSymbols = true, DeterministicMvid = true }); + } + + using (var module = ModuleDefinition.ReadModule (destination, new ReaderParameters { SymbolReaderProvider = new DefaultSymbolReaderProvider (true) })) { + } + } } }