Browse Source

Use lazy-loading for nested types.

Because IAssembly.ResolveTypeDefToken() instantiates all nested types early to build the lookup,
this change is important to get the type system initialization time back down to an acceptable level.
pull/1108/head
Daniel Grunwald 7 years ago
parent
commit
52670e004b
  1. 23
      ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs
  2. 28
      ILSpy/Commands/DecompileAllCommand.cs

23
ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs

@ -190,14 +190,14 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -190,14 +190,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
RegisterCecilObject(t, td);
} else {
var t = CreateTopLevelTypeDefinition(td);
currentAssembly.AddTypeDefinition(t);
cecilTypeDefs.Add(td);
typeDefs.Add(t);
currentAssembly.AddTypeDefinition(t);
// The registration will happen after the members are initialized
}
}
}
// Initialize the type's members:
// Initialize the type's members (but only if not lazy-init)
for (int i = 0; i < typeDefs.Count; i++) {
InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]);
}
@ -967,6 +967,14 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -967,6 +967,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
if (!typeDefinition.HasNestedTypes)
return;
if (LazyLoad) {
foreach (TypeDefinition nestedTypeDef in typeDefinition.NestedTypes) {
var nestedTd = new LazyCecilTypeDefinition(this, nestedTypeDef, declaringTypeDefinition);
nestedTypes.Add(nestedTd);
RegisterCecilObject(nestedTd, nestedTypeDef);
}
return;
}
foreach (TypeDefinition nestedTypeDef in typeDefinition.NestedTypes) {
TypeAttributes visibility = nestedTypeDef.Attributes & TypeAttributes.VisibilityMask;
if (this.IncludeInternalMembers
@ -1159,13 +1167,18 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -1159,13 +1167,18 @@ namespace ICSharpCode.Decompiler.TypeSystem
IList<IUnresolvedTypeDefinition> nestedTypes;
IList<IUnresolvedMember> members;
public LazyCecilTypeDefinition(CecilLoader loader, TypeDefinition typeDefinition)
public LazyCecilTypeDefinition(CecilLoader loader, TypeDefinition typeDefinition, IUnresolvedTypeDefinition declaringTypeDefinition = null)
{
this.loader = loader;
this.cecilTypeDef = typeDefinition;
this.MetadataToken = typeDefinition.MetadataToken;
this.SymbolKind = SymbolKind.TypeDefinition;
this.namespaceName = typeDefinition.Namespace;
if (declaringTypeDefinition != null) {
this.DeclaringTypeDefinition = declaringTypeDefinition;
this.namespaceName = declaringTypeDefinition.Namespace;
} else {
this.namespaceName = typeDefinition.Namespace;
}
this.Name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(typeDefinition.Name);
var tps = new List<IUnresolvedTypeParameter>();
InitTypeParameters(typeDefinition, tps);
@ -1349,7 +1362,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -1349,7 +1362,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
throw new ArgumentNullException("context");
if (context.CurrentAssembly == null)
throw new ArgumentException("An ITypeDefinition cannot be resolved in a context without a current assembly.");
return context.CurrentAssembly.GetTypeDefinition(this.FullTypeName)
return context.CurrentAssembly.ResolveTypeDefToken(this.MetadataToken)
?? (IType)new UnknownType(this.Namespace, this.Name, this.TypeParameters.Count);
}

28
ILSpy/Commands/DecompileAllCommand.cs

@ -20,7 +20,9 @@ @@ -20,7 +20,9 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.TextView;
namespace ICSharpCode.ILSpy
@ -64,6 +66,32 @@ namespace ICSharpCode.ILSpy @@ -64,6 +66,32 @@ namespace ICSharpCode.ILSpy
}, ct)).Then(output => MainWindow.Instance.TextView.ShowText(output)).HandleExceptions();
}
}
[ExportMainMenuCommand(Menu = "_File", Header = "DEBUG -- Decompile 100x", MenuCategory = "Open", MenuOrder = 2.6)]
sealed class Decompile100TimesCommand : SimpleCommand
{
public override void Execute(object parameter)
{
const int numRuns = 100;
var language = MainWindow.Instance.CurrentLanguage;
var nodes = MainWindow.Instance.SelectedNodes.ToArray();
var options = new DecompilationOptions();
MainWindow.Instance.TextView.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
options.CancellationToken = ct;
Stopwatch w = Stopwatch.StartNew();
for (int i = 0; i < numRuns; ++i) {
foreach (var node in nodes) {
node.Decompile(language, new PlainTextOutput(), options);
}
}
w.Stop();
AvalonEditTextOutput output = new AvalonEditTextOutput();
double msPerRun = w.Elapsed.TotalMilliseconds / numRuns;
output.Write($"Average time: {msPerRun.ToString("f1")}ms\n");
return output;
}, ct)).Then(output => MainWindow.Instance.TextView.ShowText(output)).HandleExceptions();
}
}
}
#endif
Loading…
Cancel
Save