Browse Source

Add TypeSystemOptions.Uncached.

pull/1030/head
Daniel Grunwald 7 years ago
parent
commit
ab157b2fc0
  1. 6
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  2. 2
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  3. 2
      ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs
  4. 22
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  5. 12
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs
  6. 34
      ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs
  7. 7
      ILSpy/LoadedAssembly.cs
  8. 3
      ILSpy/MainWindow.xaml.cs
  9. 2
      ILSpy/TextView/DecompilerTextView.cs
  10. 3
      ILSpy/TreeNodes/BaseTypesEntryNode.cs

6
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -395,12 +395,14 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
public static string DecompileCSharp(string assemblyFileName, DecompilerSettings settings = null) public static string DecompileCSharp(string assemblyFileName, DecompilerSettings settings = null)
{ {
if (settings == null)
settings = new DecompilerSettings();
using (var file = new FileStream(assemblyFileName, FileMode.Open, FileAccess.Read)) { using (var file = new FileStream(assemblyFileName, FileMode.Open, FileAccess.Read)) {
var module = new PEFile(assemblyFileName, file, PEStreamOptions.PrefetchEntireImage); var module = new PEFile(assemblyFileName, file, PEStreamOptions.PrefetchEntireImage);
var resolver = new UniversalAssemblyResolver(assemblyFileName, false, var resolver = new UniversalAssemblyResolver(assemblyFileName, false,
module.Reader.DetectTargetFrameworkId(), PEStreamOptions.PrefetchMetadata); module.Reader.DetectTargetFrameworkId(), PEStreamOptions.PrefetchMetadata);
var typeSystem = new DecompilerTypeSystem(module, resolver); var typeSystem = new DecompilerTypeSystem(module, resolver, settings);
CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, settings ?? new DecompilerSettings()); CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, settings);
decompiler.AstTransforms.Insert(0, new RemoveEmbeddedAtttributes()); decompiler.AstTransforms.Insert(0, new RemoveEmbeddedAtttributes());
decompiler.AstTransforms.Insert(0, new RemoveCompilerAttribute()); decompiler.AstTransforms.Insert(0, new RemoveCompilerAttribute());
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers());

2
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -225,6 +225,8 @@ namespace ICSharpCode.Decompiler.CSharp
this.settings = settings; this.settings = settings;
this.module = typeSystem.MainModule; this.module = typeSystem.MainModule;
this.metadata = module.PEFile.Metadata; this.metadata = module.PEFile.Metadata;
if (module.TypeSystemOptions.HasFlag(TypeSystemOptions.Uncached))
throw new ArgumentException("Cannot use an uncached type system in the decompiler.");
} }
#region MemberIsHidden #region MemberIsHidden

2
ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs

@ -317,7 +317,7 @@ namespace ICSharpCode.Decompiler.CSharp
return Path.Combine(dir, file); return Path.Combine(dir, file);
} }
}, StringComparer.OrdinalIgnoreCase).ToList(); }, StringComparer.OrdinalIgnoreCase).ToList();
DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver); DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, settings);
Parallel.ForEach( Parallel.ForEach(
files, files,
new ParallelOptions { new ParallelOptions {

22
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -63,6 +63,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary> /// </summary>
OnlyPublicAPI = 8, OnlyPublicAPI = 8,
/// <summary> /// <summary>
/// Do not cache accessed entities.
/// In a normal type system (without this option), every type or member definition has exactly one ITypeDefinition/IMember
/// instance. This instance is kept alive until the whole type system can be garbage-collected.
/// When this option is specified, the type system avoids these caches.
/// This reduces the memory usage in many cases, but increases the number of allocations.
/// Also, some code in the decompiler expects to be able to compare type/member definitions by reference equality,
/// and thus will fail with uncached type systems.
/// </summary>
Uncached = 0x10,
/// <summary>
/// Default settings: all features enabled. /// Default settings: all features enabled.
/// </summary> /// </summary>
Default = Dynamic | Tuple | ExtensionMethods Default = Dynamic | Tuple | ExtensionMethods
@ -76,12 +86,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </remarks> /// </remarks>
public class DecompilerTypeSystem : SimpleCompilation, IDecompilerTypeSystem public class DecompilerTypeSystem : SimpleCompilation, IDecompilerTypeSystem
{ {
public DecompilerTypeSystem(PEFile mainModule, IAssemblyResolver assemblyResolver) public static TypeSystemOptions GetOptions(DecompilerSettings settings)
: this(mainModule, assemblyResolver, TypeSystemOptions.Default)
{
}
static TypeSystemOptions GetOptions(DecompilerSettings settings)
{ {
var typeSystemOptions = TypeSystemOptions.None; var typeSystemOptions = TypeSystemOptions.None;
if (settings.Dynamic) if (settings.Dynamic)
@ -93,6 +98,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
return typeSystemOptions; return typeSystemOptions;
} }
public DecompilerTypeSystem(PEFile mainModule, IAssemblyResolver assemblyResolver)
: this(mainModule, assemblyResolver, TypeSystemOptions.Default)
{
}
public DecompilerTypeSystem(PEFile mainModule, IAssemblyResolver assemblyResolver, DecompilerSettings settings) public DecompilerTypeSystem(PEFile mainModule, IAssemblyResolver assemblyResolver, DecompilerSettings settings)
: this(mainModule, assemblyResolver, GetOptions(settings ?? throw new ArgumentNullException(nameof(settings)))) : this(mainModule, assemblyResolver, GetOptions(settings ?? throw new ArgumentNullException(nameof(settings))))
{ {

12
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs

@ -127,6 +127,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
foreach (TypeDefinitionHandle h in nestedTypeCollection) { foreach (TypeDefinitionHandle h in nestedTypeCollection) {
nestedTypeList.Add(module.GetDefinition(h)); nestedTypeList.Add(module.GetDefinition(h));
} }
if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0)
return nestedTypeList;
return LazyInit.GetOrSet(ref this.nestedTypes, nestedTypeList.ToArray()); return LazyInit.GetOrSet(ref this.nestedTypes, nestedTypeList.ToArray());
} }
} }
@ -138,6 +140,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
if (members != null) if (members != null)
return members; return members;
members = this.Fields.Concat<IMember>(this.Methods).Concat(this.Properties).Concat(this.Events).ToArray(); members = this.Fields.Concat<IMember>(this.Methods).Concat(this.Properties).Concat(this.Events).ToArray();
if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0)
return members;
return LazyInit.GetOrSet(ref this.members, members); return LazyInit.GetOrSet(ref this.members, members);
} }
} }
@ -157,6 +161,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
fieldList.Add(module.GetDefinition(h)); fieldList.Add(module.GetDefinition(h));
} }
} }
if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0)
return fieldList;
return LazyInit.GetOrSet(ref this.fields, fieldList.ToArray()); return LazyInit.GetOrSet(ref this.fields, fieldList.ToArray());
} }
} }
@ -178,6 +184,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
propertyList.Add(module.GetDefinition(h)); propertyList.Add(module.GetDefinition(h));
} }
} }
if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0)
return propertyList;
return LazyInit.GetOrSet(ref this.properties, propertyList.ToArray()); return LazyInit.GetOrSet(ref this.properties, propertyList.ToArray());
} }
} }
@ -200,6 +208,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
eventList.Add(module.GetDefinition(h)); eventList.Add(module.GetDefinition(h));
} }
} }
if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0)
return eventList;
return LazyInit.GetOrSet(ref this.events, eventList.ToArray()); return LazyInit.GetOrSet(ref this.events, eventList.ToArray());
} }
} }
@ -222,6 +232,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
if (this.Kind == TypeKind.Struct || this.Kind == TypeKind.Enum) { if (this.Kind == TypeKind.Struct || this.Kind == TypeKind.Enum) {
methodsList.Add(FakeMethod.CreateDummyConstructor(Compilation, this, IsAbstract ? Accessibility.Protected : Accessibility.Public)); methodsList.Add(FakeMethod.CreateDummyConstructor(Compilation, this, IsAbstract ? Accessibility.Protected : Accessibility.Public));
} }
if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0)
return methodsList;
return LazyInit.GetOrSet(ref this.methods, methodsList.ToArray()); return LazyInit.GetOrSet(ref this.methods, methodsList.ToArray());
} }
} }

34
ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs

@ -68,12 +68,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
} }
this.rootNamespace = new MetadataNamespace(this, null, string.Empty, metadata.GetNamespaceDefinitionRoot()); this.rootNamespace = new MetadataNamespace(this, null, string.Empty, metadata.GetNamespaceDefinitionRoot());
// create arrays for resolved entities, indexed by row index if (!options.HasFlag(TypeSystemOptions.Uncached)) {
this.typeDefs = new MetadataTypeDefinition[metadata.TypeDefinitions.Count + 1]; // create arrays for resolved entities, indexed by row index
this.fieldDefs = new MetadataField[metadata.FieldDefinitions.Count + 1]; this.typeDefs = new MetadataTypeDefinition[metadata.TypeDefinitions.Count + 1];
this.methodDefs = new MetadataMethod[metadata.MethodDefinitions.Count + 1]; this.fieldDefs = new MetadataField[metadata.FieldDefinitions.Count + 1];
this.propertyDefs = new MetadataProperty[metadata.PropertyDefinitions.Count + 1]; this.methodDefs = new MetadataMethod[metadata.MethodDefinitions.Count + 1];
this.eventDefs = new MetadataEvent[metadata.EventDefinitions.Count + 1]; this.propertyDefs = new MetadataProperty[metadata.PropertyDefinitions.Count + 1];
this.eventDefs = new MetadataEvent[metadata.EventDefinitions.Count + 1];
}
} }
internal string GetString(StringHandle name) internal string GetString(StringHandle name)
@ -156,14 +158,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary> /// </summary>
public IEnumerable<ITypeDefinition> TypeDefinitions { public IEnumerable<ITypeDefinition> TypeDefinitions {
get { get {
for (int row = 1; row < typeDefs.Length; row++) { foreach (var tdHandle in metadata.TypeDefinitions) {
var typeDef = LazyInit.VolatileRead(ref typeDefs[row]); yield return GetDefinition(tdHandle);
if (typeDef != null) {
yield return typeDef;
} else {
typeDef = new MetadataTypeDefinition(this, MetadataTokens.TypeDefinitionHandle(row));
yield return LazyInit.GetOrSet(ref typeDefs[row], typeDef);
}
} }
} }
} }
@ -172,6 +168,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
{ {
if (handle.IsNil) if (handle.IsNil)
return null; return null;
if (typeDefs == null)
return new MetadataTypeDefinition(this, handle);
int row = MetadataTokens.GetRowNumber(handle); int row = MetadataTokens.GetRowNumber(handle);
if (row >= typeDefs.Length) if (row >= typeDefs.Length)
HandleOutOfRange(handle); HandleOutOfRange(handle);
@ -186,6 +184,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
{ {
if (handle.IsNil) if (handle.IsNil)
return null; return null;
if (fieldDefs == null)
return new MetadataField(this, handle);
int row = MetadataTokens.GetRowNumber(handle); int row = MetadataTokens.GetRowNumber(handle);
if (row >= fieldDefs.Length) if (row >= fieldDefs.Length)
HandleOutOfRange(handle); HandleOutOfRange(handle);
@ -200,6 +200,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
{ {
if (handle.IsNil) if (handle.IsNil)
return null; return null;
if (methodDefs == null)
return new MetadataMethod(this, handle);
int row = MetadataTokens.GetRowNumber(handle); int row = MetadataTokens.GetRowNumber(handle);
Debug.Assert(row != 0); Debug.Assert(row != 0);
if (row >= methodDefs.Length) if (row >= methodDefs.Length)
@ -215,6 +217,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
{ {
if (handle.IsNil) if (handle.IsNil)
return null; return null;
if (propertyDefs == null)
return new MetadataProperty(this, handle);
int row = MetadataTokens.GetRowNumber(handle); int row = MetadataTokens.GetRowNumber(handle);
Debug.Assert(row != 0); Debug.Assert(row != 0);
if (row >= methodDefs.Length) if (row >= methodDefs.Length)
@ -230,6 +234,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
{ {
if (handle.IsNil) if (handle.IsNil)
return null; return null;
if (eventDefs == null)
return new MetadataEvent(this, handle);
int row = MetadataTokens.GetRowNumber(handle); int row = MetadataTokens.GetRowNumber(handle);
Debug.Assert(row != 0); Debug.Assert(row != 0);
if (row >= methodDefs.Length) if (row >= methodDefs.Length)

7
ILSpy/LoadedAssembly.cs

@ -99,6 +99,9 @@ namespace ICSharpCode.ILSpy
/// Gets a type system containing all types from this assembly + primitve types from mscorlib. /// Gets a type system containing all types from this assembly + primitve types from mscorlib.
/// Returns null in case of load errors. /// Returns null in case of load errors.
/// </summary> /// </summary>
/// <remarks>
/// This is an uncached type system.
/// </remarks>
public ICompilation GetTypeSystemOrNull() public ICompilation GetTypeSystemOrNull()
{ {
if (typeSystem != null) if (typeSystem != null)
@ -106,7 +109,9 @@ namespace ICSharpCode.ILSpy
var module = GetPEFileOrNull(); var module = GetPEFileOrNull();
if (module == null) if (module == null)
return null; return null;
return typeSystem = new SimpleCompilation(module, MinimalCorlib.Instance); return typeSystem = new SimpleCompilation(
module.WithOptions(TypeSystemOptions.Default | TypeSystemOptions.Uncached),
MinimalCorlib.Instance);
} }
public AssemblyList AssemblyList => assemblyList; public AssemblyList AssemblyList => assemblyList;

3
ILSpy/MainWindow.xaml.cs

@ -637,7 +637,8 @@ namespace ICSharpCode.ILSpy
OpenLink(opCode.Link); OpenLink(opCode.Link);
break; break;
case ValueTuple<PEFile, System.Reflection.Metadata.EntityHandle> unresolvedEntity: case ValueTuple<PEFile, System.Reflection.Metadata.EntityHandle> unresolvedEntity:
var typeSystem = new DecompilerTypeSystem(unresolvedEntity.Item1, unresolvedEntity.Item1.GetAssemblyResolver()); var typeSystem = new DecompilerTypeSystem(unresolvedEntity.Item1, unresolvedEntity.Item1.GetAssemblyResolver(),
TypeSystemOptions.Default | TypeSystemOptions.Uncached);
reference = typeSystem.MainModule.ResolveEntity(unresolvedEntity.Item2); reference = typeSystem.MainModule.ResolveEntity(unresolvedEntity.Item2);
goto default; goto default;
default: default:

2
ILSpy/TextView/DecompilerTextView.cs

@ -210,7 +210,7 @@ namespace ICSharpCode.ILSpy.TextView
} else if (segment.Reference is IEntity entity) { } else if (segment.Reference is IEntity entity) {
return CreateTextBlockForEntity(entity); return CreateTextBlockForEntity(entity);
} else if (segment.Reference is ValueTuple<PEFile, System.Reflection.Metadata.EntityHandle> unresolvedEntity) { } else if (segment.Reference is ValueTuple<PEFile, System.Reflection.Metadata.EntityHandle> unresolvedEntity) {
var typeSystem = new DecompilerTypeSystem(unresolvedEntity.Item1, unresolvedEntity.Item1.GetAssemblyResolver()); var typeSystem = new DecompilerTypeSystem(unresolvedEntity.Item1, unresolvedEntity.Item1.GetAssemblyResolver(), TypeSystemOptions.Default | TypeSystemOptions.Uncached);
IEntity resolved = typeSystem.MainModule.ResolveEntity(unresolvedEntity.Item2); IEntity resolved = typeSystem.MainModule.ResolveEntity(unresolvedEntity.Item2);
if (resolved == null) if (resolved == null)
return null; return null;

3
ILSpy/TreeNodes/BaseTypesEntryNode.cs

@ -49,7 +49,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
ITypeDefinition TryResolve(PEFile module, EntityHandle handle, IType type, bool mayRetry = true) ITypeDefinition TryResolve(PEFile module, EntityHandle handle, IType type, bool mayRetry = true)
{ {
DecompilerTypeSystem typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver()); DecompilerTypeSystem typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver(),
TypeSystemOptions.Default | TypeSystemOptions.Uncached);
var t = typeSystem.MainModule.ResolveEntity(handle) as ITypeDefinition; var t = typeSystem.MainModule.ResolveEntity(handle) as ITypeDefinition;
if (t != null) { if (t != null) {
showExpander = t.DirectBaseTypes.Any(); showExpander = t.DirectBaseTypes.Any();

Loading…
Cancel
Save