Browse Source

Use MetadataToken for type lookups within the current assembly.

pull/1108/head
Daniel Grunwald 7 years ago
parent
commit
bf64e754df
  1. 3
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  2. 237
      ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs
  3. 5
      ICSharpCode.Decompiler/TypeSystem/IAssembly.cs
  4. 69
      ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs

3
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -271,9 +271,6 @@ namespace ICSharpCode.Decompiler.Tests @@ -271,9 +271,6 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void LINQRaytracer([ValueSource("defaultOptions")] CSharpCompilerOptions options)
{
if (options.HasFlag(CSharpCompilerOptions.UseMcs)) {
Assert.Ignore("Decompiler bug with mono!");
}
RunCS(options: options);
}

237
ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs

@ -44,10 +44,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -44,10 +44,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// (which might be incorrect due to the bug) are re-created.
/// </summary>
const int cecilLoaderVersion = 1;
#region Options
// Most options are defined in the AssemblyLoader base class
/// <summary>
/// Specifies whether to use lazy loading. The default is false.
/// If this property is set to true, the CecilLoader will not copy all the relevant information
@ -74,9 +74,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -74,9 +74,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// the callback may be invoked concurrently on multiple threads.
/// </remarks>
public Action<IUnresolvedEntity, MemberReference> OnEntityLoaded { get; set; }
bool shortenInterfaceImplNames = true;
/// <summary>
/// Specifies whether method names of explicit interface-implementations should be shortened.
/// </summary>
@ -90,17 +90,17 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -90,17 +90,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
#endregion
ModuleDefinition currentModule;
DefaultUnresolvedAssembly currentAssembly;
/// <summary>
/// Initializes a new instance of the <see cref="CecilLoader"/> class.
/// </summary>
public CecilLoader()
{
}
/// <summary>
/// Creates a nested CecilLoader for lazy-loading.
/// </summary>
@ -129,7 +129,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -129,7 +129,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
throw new ArgumentNullException("assemblyDefinition");
return LoadModule(assemblyDefinition.MainModule);
}
/// <summary>
/// Loads the module definition into a project content.
/// </summary>
@ -138,9 +138,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -138,9 +138,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
if (moduleDefinition == null)
throw new ArgumentNullException("moduleDefinition");
this.currentModule = moduleDefinition;
// Read assembly and module attributes
IList<IUnresolvedAttribute> assemblyAttributes = new List<IUnresolvedAttribute>();
IList<IUnresolvedAttribute> moduleAttributes = new List<IUnresolvedAttribute>();
@ -149,15 +149,15 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -149,15 +149,15 @@ namespace ICSharpCode.Decompiler.TypeSystem
AddAttributes(assemblyDefinition, assemblyAttributes);
}
AddAttributes(moduleDefinition, moduleAttributes);
assemblyAttributes = interningProvider.InternList(assemblyAttributes);
moduleAttributes = interningProvider.InternList(moduleAttributes);
this.currentAssembly = new DefaultUnresolvedAssembly(assemblyDefinition != null ? assemblyDefinition.Name.FullName : moduleDefinition.Name);
currentAssembly.Location = moduleDefinition.FileName;
currentAssembly.AssemblyAttributes.AddRange(assemblyAttributes);
currentAssembly.ModuleAttributes.AddRange(assemblyAttributes);
// Register type forwarders:
foreach (ExportedType type in moduleDefinition.ExportedTypes) {
if (type.IsForwarder) {
@ -172,7 +172,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -172,7 +172,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
currentAssembly.AddTypeForwarder(key, typeRef);
}
}
// Create and register all types:
CecilLoader cecilLoaderCloneForLazyLoading = LazyLoad ? new CecilLoader(this) : null;
List<TypeDefinition> cecilTypeDefs = new List<TypeDefinition>();
@ -183,7 +183,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -183,7 +183,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
string name = td.Name;
if (name.Length == 0)
continue;
if (this.LazyLoad) {
var t = new LazyCecilTypeDefinition(cecilLoaderCloneForLazyLoading, td);
currentAssembly.AddTypeDefinition(t);
@ -201,7 +201,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -201,7 +201,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
for (int i = 0; i < typeDefs.Count; i++) {
InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]);
}
// Freezing the assembly here is important:
// otherwise it will be frozen when a compilation is first created
// from it. But freezing has the effect of changing some collection instances
@ -211,13 +211,13 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -211,13 +211,13 @@ namespace ICSharpCode.Decompiler.TypeSystem
// By freezing the assembly now, we ensure it is usable on multiple
// threads without issues.
currentAssembly.Freeze();
var result = this.currentAssembly;
this.currentAssembly = null;
this.currentModule = null;
return result;
}
/// <summary>
/// Sets the current module.
/// This causes ReadTypeReference() to use <see cref="DefaultAssemblyReference.CurrentAssembly"/> for references
@ -227,7 +227,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -227,7 +227,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
this.currentModule = module;
}
/// <summary>
/// Loads a type from Cecil.
/// </summary>
@ -242,7 +242,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -242,7 +242,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
return td;
}
#endregion
#region Load Assembly From Disk
public override IUnresolvedAssembly LoadAssemblyFile(string fileName)
{
@ -253,7 +253,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -253,7 +253,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
return LoadModule(module);
}
}
// used to prevent Cecil from loading referenced assemblies
sealed class DummyAssemblyResolver : IAssemblyResolver
{
@ -261,17 +261,17 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -261,17 +261,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
return null;
}
public AssemblyDefinition Resolve(string fullName)
{
return null;
}
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
return null;
}
public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters)
{
return null;
@ -282,7 +282,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -282,7 +282,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
#endregion
#region Read Type Reference
/// <summary>
/// Reads a type reference.
@ -293,91 +293,133 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -293,91 +293,133 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// This is used to support the 'dynamic' type.</param>
/// <param name="isFromSignature">Whether this TypeReference is from a context where
/// IsValueType is set correctly.</param>
public ITypeReference ReadTypeReference(TypeReference type, ICustomAttributeProvider typeAttributes = null, bool isFromSignature=false)
public ITypeReference ReadTypeReference(TypeReference type, ICustomAttributeProvider typeAttributes = null, bool isFromSignature = false)
{
int typeIndex = 0;
return CreateType(type, typeAttributes, ref typeIndex, isFromSignature);
}
ITypeReference CreateType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int typeIndex, bool isFromSignature)
{
while (type is OptionalModifierType || type is RequiredModifierType) {
type = ((TypeSpecification)type).ElementType;
isFromSignature = true;
}
if (type == null) {
return SpecialType.UnknownType;
}
if (type is Mono.Cecil.ByReferenceType) {
typeIndex++;
return interningProvider.Intern(
new ByReferenceTypeReference(
CreateType(
(type as Mono.Cecil.ByReferenceType).ElementType,
typeAttributes, ref typeIndex, isFromSignature: true)));
} else if (type is Mono.Cecil.PointerType) {
typeIndex++;
return interningProvider.Intern(
new PointerTypeReference(
CreateType(
(type as Mono.Cecil.PointerType).ElementType,
typeAttributes, ref typeIndex, isFromSignature: true)));
} else if (type is Mono.Cecil.ArrayType) {
typeIndex++;
return interningProvider.Intern(
new ArrayTypeReference(
CreateType(
(type as Mono.Cecil.ArrayType).ElementType,
typeAttributes, ref typeIndex, isFromSignature: true),
(type as Mono.Cecil.ArrayType).Rank));
} else if (type is GenericInstanceType) {
GenericInstanceType gType = (GenericInstanceType)type;
ITypeReference baseType = CreateType(gType.ElementType, typeAttributes, ref typeIndex, isFromSignature: true);
ITypeReference[] para = new ITypeReference[gType.GenericArguments.Count];
for (int i = 0; i < para.Length; ++i) {
switch (type.MetadataType) {
case MetadataType.Void:
return KnownTypeReference.Get(KnownTypeCode.Void);
case MetadataType.Boolean:
return KnownTypeReference.Get(KnownTypeCode.Boolean);
case MetadataType.Char:
return KnownTypeReference.Get(KnownTypeCode.Char);
case MetadataType.SByte:
return KnownTypeReference.Get(KnownTypeCode.SByte);
case MetadataType.Byte:
return KnownTypeReference.Get(KnownTypeCode.Byte);
case MetadataType.Int16:
return KnownTypeReference.Get(KnownTypeCode.Int16);
case MetadataType.UInt16:
return KnownTypeReference.Get(KnownTypeCode.UInt16);
case MetadataType.Int32:
return KnownTypeReference.Get(KnownTypeCode.Int32);
case MetadataType.UInt32:
return KnownTypeReference.Get(KnownTypeCode.UInt32);
case MetadataType.Int64:
return KnownTypeReference.Get(KnownTypeCode.Int64);
case MetadataType.UInt64:
return KnownTypeReference.Get(KnownTypeCode.UInt64);
case MetadataType.Single:
return KnownTypeReference.Get(KnownTypeCode.Single);
case MetadataType.Double:
return KnownTypeReference.Get(KnownTypeCode.Double);
case MetadataType.String:
return KnownTypeReference.Get(KnownTypeCode.String);
case MetadataType.Pointer:
typeIndex++;
para[i] = CreateType(gType.GenericArguments[i], typeAttributes, ref typeIndex, isFromSignature: true);
}
return interningProvider.Intern(new ParameterizedTypeReference(baseType, para));
} else if (type is GenericParameter) {
GenericParameter typeGP = (GenericParameter)type;
return TypeParameterReference.Create(typeGP.Owner is MethodReference ? SymbolKind.Method : SymbolKind.TypeDefinition, typeGP.Position);
} else if (type is FunctionPointerType) {
return KnownTypeReference.Get(KnownTypeCode.IntPtr);
} else if (type.IsNested) {
return interningProvider.Intern(
new PointerTypeReference(
CreateType(
(type as Mono.Cecil.PointerType).ElementType,
typeAttributes, ref typeIndex, isFromSignature: true)));
case MetadataType.ByReference:
typeIndex++;
return interningProvider.Intern(
new ByReferenceTypeReference(
CreateType(
(type as Mono.Cecil.ByReferenceType).ElementType,
typeAttributes, ref typeIndex, isFromSignature: true)));
case MetadataType.Var:
return TypeParameterReference.Create(SymbolKind.TypeDefinition, ((GenericParameter)type).Position);
case MetadataType.MVar:
return TypeParameterReference.Create(SymbolKind.Method, ((GenericParameter)type).Position);
case MetadataType.Array:
typeIndex++;
return interningProvider.Intern(
new ArrayTypeReference(
CreateType(
(type as Mono.Cecil.ArrayType).ElementType,
typeAttributes, ref typeIndex, isFromSignature: true),
(type as Mono.Cecil.ArrayType).Rank));
case MetadataType.GenericInstance:
GenericInstanceType gType = (GenericInstanceType)type;
ITypeReference baseType = CreateType(gType.ElementType, typeAttributes, ref typeIndex, isFromSignature: true);
ITypeReference[] para = new ITypeReference[gType.GenericArguments.Count];
for (int i = 0; i < para.Length; ++i) {
typeIndex++;
para[i] = CreateType(gType.GenericArguments[i], typeAttributes, ref typeIndex, isFromSignature: true);
}
return interningProvider.Intern(new ParameterizedTypeReference(baseType, para));
case MetadataType.IntPtr:
return KnownTypeReference.Get(KnownTypeCode.IntPtr);
case MetadataType.UIntPtr:
return KnownTypeReference.Get(KnownTypeCode.UIntPtr);
case MetadataType.FunctionPointer:
// C# and the NR typesystem don't support function pointer types.
// Function pointer types map to StackType.I, so we'll use IntPtr instead.
return KnownTypeReference.Get(KnownTypeCode.IntPtr);
case MetadataType.Object:
if (HasDynamicAttribute(typeAttributes, typeIndex)) {
return SpecialType.Dynamic;
} else {
return KnownTypeReference.Get(KnownTypeCode.Object);
}
case MetadataType.RequiredModifier:
case MetadataType.OptionalModifier:
// we don't store modopts/modreqs in the NR type system
return CreateType(((TypeSpecification)type).ElementType, typeAttributes, ref typeIndex, isFromSignature: true);
case MetadataType.Sentinel:
return SpecialType.ArgList;
case MetadataType.Pinned:
return CreateType(((PinnedType)type).ElementType, typeAttributes, ref typeIndex, isFromSignature: true);
}
// valuetype/class/typedbyreference
if (type is TypeDefinition) {
return new TypeDefTokenTypeReference(type.MetadataToken);
}
// type.IsValueType is only reliable if we got this TypeReference from a signature,
// or if it's a TypeSpecification.
bool? isReferenceType = isFromSignature ? (bool?)!type.IsValueType : null;
if (type.IsNested) {
ITypeReference typeRef = CreateType(type.DeclaringType, typeAttributes, ref typeIndex, isFromSignature);
int partTypeParameterCount;
string namepart = ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out partTypeParameterCount);
namepart = interningProvider.Intern(namepart);
// type.IsValueType is only reliable if we got this TypeReference from a signature,
// or if it's a TypeSpecification.
bool? isReferenceType = isFromSignature ? (bool?)!type.IsValueType : null;
return interningProvider.Intern(new NestedTypeReference(typeRef, namepart, partTypeParameterCount, isReferenceType));
} else {
string ns = interningProvider.Intern(type.Namespace ?? string.Empty);
string name = type.Name;
if (name == null)
throw new InvalidOperationException("type.Name returned null. Type: " + type.ToString());
if (name == "Object" && ns == "System" && HasDynamicAttribute(typeAttributes, typeIndex)) {
return SpecialType.Dynamic;
} else {
int typeParameterCount;
name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name, out typeParameterCount);
name = interningProvider.Intern(name);
if (currentAssembly != null) {
IUnresolvedTypeDefinition c = currentAssembly.GetTypeDefinition(ns, name, typeParameterCount);
if (c != null)
return c;
}
// type.IsValueType is only reliable if we got this TypeReference from a signature,
// or if it's a TypeSpecification.
bool? isReferenceType = isFromSignature ? (bool?)!type.IsValueType : null;
return interningProvider.Intern(new GetClassTypeReference(
GetAssemblyReference(type.Scope), ns, name, typeParameterCount,
isReferenceType));
}
int typeParameterCount;
name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name, out typeParameterCount);
name = interningProvider.Intern(name);
return interningProvider.Intern(new GetClassTypeReference(
GetAssemblyReference(type.Scope), ns, name, typeParameterCount,
isReferenceType));
}
}
@ -406,6 +448,26 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -406,6 +448,26 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
return false;
}
sealed class TypeDefTokenTypeReference : ITypeReference
{
readonly MetadataToken token;
public TypeDefTokenTypeReference(MetadataToken token)
{
if (token.TokenType != TokenType.TypeDef)
throw new ArgumentException(nameof(token), "must be TypeDef token");
this.token = token;
}
public IType Resolve(ITypeResolveContext context)
{
ITypeDefinition td = context.CurrentAssembly.ResolveTypeDefToken(token);
if (td != null)
return td;
return SpecialType.UnknownType;
}
}
#endregion
#region Read Attributes
@ -918,6 +980,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -918,6 +980,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
name = name.Substring(pos + 1);
name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name);
var nestedType = new DefaultUnresolvedTypeDefinition(declaringTypeDefinition, name);
nestedType.MetadataToken = nestedTypeDef.MetadataToken;
InitTypeParameters(nestedTypeDef, nestedType.TypeParameters);
nestedTypes.Add(nestedType);
InitTypeDefinition(nestedTypeDef, nestedType);

5
ICSharpCode.Decompiler/TypeSystem/IAssembly.cs

@ -123,5 +123,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -123,5 +123,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// Gets all non-nested types in the assembly.
/// </summary>
IEnumerable<ITypeDefinition> TopLevelTypeDefinitions { get; }
/// <summary>
/// Gets the type definition from the metadata token, or null if not found.
/// </summary>
ITypeDefinition ResolveTypeDefToken(Mono.Cecil.MetadataToken token);
}
}

69
ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs

@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IList<IUnresolvedAttribute> moduleAttributes;
Dictionary<TopLevelTypeName, IUnresolvedTypeDefinition> typeDefinitions = new Dictionary<TopLevelTypeName, IUnresolvedTypeDefinition>(TopLevelTypeNameComparer.Ordinal);
Dictionary<TopLevelTypeName, ITypeReference> typeForwarders = new Dictionary<TopLevelTypeName, ITypeReference>(TopLevelTypeNameComparer.Ordinal);
protected override void FreezeInternal()
{
base.FreezeInternal();
@ -283,7 +283,43 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -283,7 +283,43 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return ns;
}
#endregion
IUnresolvedTypeDefinition[] allTypesByMetadata;
IUnresolvedTypeDefinition[] BuildMetadataLookup()
{
Debug.Assert(IsFrozen);
uint maxRowId = 0;
foreach (var td in this.GetAllTypeDefinitions()) {
var token = td.MetadataToken;
if (token.TokenType == Mono.Cecil.TokenType.TypeDef && token.RID > maxRowId) {
maxRowId = token.RID;
}
}
var lookup = new IUnresolvedTypeDefinition[maxRowId + 1];
foreach (var td in this.GetAllTypeDefinitions()) {
var token = td.MetadataToken;
if (token.TokenType == Mono.Cecil.TokenType.TypeDef) {
lookup[token.RID] = td;
}
}
return lookup;
}
internal IUnresolvedTypeDefinition GetTypeDefByToken(Mono.Cecil.MetadataToken token)
{
if (token.TokenType != Mono.Cecil.TokenType.TypeDef)
throw new ArgumentException("Token must be typedef-token.");
var lookup = LazyInit.VolatileRead(ref allTypesByMetadata);
if (lookup == null) {
lookup = LazyInit.GetOrSet(ref allTypesByMetadata, BuildMetadataLookup());
}
if (token.RID < lookup.Length)
return lookup[token.RID];
else
return null;
}
sealed class DefaultResolvedAssembly : IAssembly
{
readonly DefaultUnresolvedAssembly unresolvedAssembly;
@ -398,7 +434,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -398,7 +434,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
return typeDict.GetOrAdd(unresolved, t => CreateTypeDefinition(t));
}
ITypeDefinition CreateTypeDefinition(IUnresolvedTypeDefinition unresolved)
{
if (unresolved.DeclaringTypeDefinition != null) {
@ -416,7 +452,32 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -416,7 +452,32 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return unresolvedAssembly.TopLevelTypeDefinitions.Select(t => GetTypeDefinition(t));
}
}
public ITypeDefinition ResolveTypeDefToken(Mono.Cecil.MetadataToken token)
{
var td = unresolvedAssembly.GetTypeDefByToken(token);
if (td != null)
return GetPotentiallyNestedTypeDefinition(td);
return null;
}
ITypeDefinition GetPotentiallyNestedTypeDefinition(IUnresolvedTypeDefinition unresolved)
{
var outerUnresolvedType = unresolved.DeclaringTypeDefinition;
if (outerUnresolvedType == null) {
// GetTypeDefinition() may only be called for top-level types
return GetTypeDefinition(unresolved);
}
var outerType = GetPotentiallyNestedTypeDefinition(outerUnresolvedType);
if (outerType == null)
return null;
foreach (var nestedType in outerType.NestedTypes) {
if (nestedType.MetadataToken == unresolved.MetadataToken)
return nestedType;
}
return null;
}
public override string ToString()
{
return "[DefaultResolvedAssembly " + AssemblyName + "]";

Loading…
Cancel
Save