Browse Source

Add lazy-loading support to CecilLoader.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
0aa37bcf44
  1. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  2. 36
      ICSharpCode.NRefactory.Tests/TypeSystem/LazyLoadedCecilLoaderTests.cs
  3. 239
      ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
  4. 6
      ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs
  5. 2
      ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs
  6. 5
      ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs

1
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -191,6 +191,7 @@
<Compile Include="TypeSystem\CecilLoaderTests.cs" /> <Compile Include="TypeSystem\CecilLoaderTests.cs" />
<Compile Include="TypeSystem\GetAllBaseTypesTest.cs" /> <Compile Include="TypeSystem\GetAllBaseTypesTest.cs" />
<Compile Include="TypeSystem\GetMembersTests.cs" /> <Compile Include="TypeSystem\GetMembersTests.cs" />
<Compile Include="TypeSystem\LazyLoadedCecilLoaderTests.cs" />
<Compile Include="TypeSystem\ReflectionHelperTests.cs" /> <Compile Include="TypeSystem\ReflectionHelperTests.cs" />
<Compile Include="TypeSystem\SerializedCecilLoaderTests.cs" /> <Compile Include="TypeSystem\SerializedCecilLoaderTests.cs" />
<Compile Include="TypeSystem\StructureTests.cs" /> <Compile Include="TypeSystem\StructureTests.cs" />

36
ICSharpCode.NRefactory.Tests/TypeSystem/LazyLoadedCecilLoaderTests.cs

@ -0,0 +1,36 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.TypeSystem
{
[TestFixture]
public class LazyLoadedCecilLoaderTests : TypeSystemTests
{
[TestFixtureSetUp]
public void FixtureSetUp()
{
CecilLoader loader = new CecilLoader() { IncludeInternalMembers = true, LazyLoad = true };
IUnresolvedAssembly pc = loader.LoadAssemblyFile(typeof(TestCase.SimplePublicClass).Assembly.Location);
base.compilation = new SimpleCompilation(pc, CecilLoaderTests.Mscorlib);
}
}
}

239
ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

@ -47,6 +47,16 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary> /// </summary>
public bool IncludeInternalMembers { get; set; } public bool IncludeInternalMembers { get; set; }
/// <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
/// out of the Cecil object model, but will maintain references to the Cecil objects.
/// This speeds up the loading process and avoids loading unnecessary information, but it causes
/// the Cecil objects to stay in memory (which can significantly increase memory usage).
/// It also prevents serialization of the Cecil-loaded type system.
/// </summary>
public bool LazyLoad { get; set; }
/// <summary> /// <summary>
/// Gets/Sets the documentation provider that is used to retrieve the XML documentation for all members. /// Gets/Sets the documentation provider that is used to retrieve the XML documentation for all members.
/// </summary> /// </summary>
@ -137,7 +147,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
this.CancellationToken.ThrowIfCancellationRequested(); this.CancellationToken.ThrowIfCancellationRequested();
if (this.IncludeInternalMembers || (td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) { if (this.IncludeInternalMembers || (td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) {
string name = td.Name; string name = td.Name;
if (name.Length == 0 || name[0] == '<') if (name.Length == 0)
continue; continue;
var t = CreateTopLevelTypeDefinition(td); var t = CreateTopLevelTypeDefinition(td);
@ -166,6 +176,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// This causes ReadTypeReference() to use <see cref="DefaultAssemblyReference.CurrentAssembly"/> for references /// This causes ReadTypeReference() to use <see cref="DefaultAssemblyReference.CurrentAssembly"/> for references
/// in that module. /// in that module.
/// </summary> /// </summary>
[CLSCompliant(false)]
public void SetCurrentModule(ModuleDefinition module) public void SetCurrentModule(ModuleDefinition module)
{ {
this.currentModule = module; this.currentModule = module;
@ -1449,65 +1460,76 @@ namespace ICSharpCode.NRefactory.TypeSystem
{ {
string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(typeDefinition.Name); string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(typeDefinition.Name);
var td = new DefaultUnresolvedTypeDefinition(typeDefinition.Namespace, name); var td = new DefaultUnresolvedTypeDefinition(typeDefinition.Namespace, name);
InitTypeParameters(typeDefinition, td); if (typeDefinition.HasGenericParameters)
InitTypeParameters(typeDefinition, td.TypeParameters);
return td; return td;
} }
static void InitTypeParameters(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td) static void InitTypeParameters(TypeDefinition typeDefinition, IList<IUnresolvedTypeParameter> typeParameters)
{ {
// Type parameters are initialized within the constructor so that the class can be put into the type storage // Type parameters are initialized within the constructor so that the class can be put into the type storage
// before the rest of the initialization runs - this allows it to be available for early binding as soon as possible. // before the rest of the initialization runs - this allows it to be available for early binding as soon as possible.
for (int i = 0; i < typeDefinition.GenericParameters.Count; i++) { for (int i = 0; i < typeDefinition.GenericParameters.Count; i++) {
if (typeDefinition.GenericParameters[i].Position != i) if (typeDefinition.GenericParameters[i].Position != i)
throw new InvalidOperationException("g.Position != i"); throw new InvalidOperationException("g.Position != i");
td.TypeParameters.Add(new DefaultUnresolvedTypeParameter( typeParameters.Add(new DefaultUnresolvedTypeParameter(
EntityType.TypeDefinition, i, typeDefinition.GenericParameters[i].Name)); EntityType.TypeDefinition, i, typeDefinition.GenericParameters[i].Name));
} }
} }
void InitTypeParameterConstraints(TypeDefinition typeDefinition, IList<IUnresolvedTypeParameter> typeParameters)
{
for (int i = 0; i < typeParameters.Count; i++) {
AddConstraints((DefaultUnresolvedTypeParameter)typeParameters[i], typeDefinition.GenericParameters[i]);
}
}
void InitTypeDefinition(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td) void InitTypeDefinition(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td)
{ {
td.Kind = GetTypeKind(typeDefinition);
InitTypeModifiers(typeDefinition, td); InitTypeModifiers(typeDefinition, td);
InitTypeParameterConstraints(typeDefinition, td.TypeParameters);
if (typeDefinition.HasGenericParameters) { // nested types can be initialized only after generic parameters were created
for (int i = 0; i < typeDefinition.GenericParameters.Count; i++) { InitNestedTypes(typeDefinition, td, td.NestedTypes);
AddConstraints((DefaultUnresolvedTypeParameter)td.TypeParameters[i], typeDefinition.GenericParameters[i]);
}
}
InitNestedTypes(typeDefinition, td); // nested types can be initialized only after generic parameters were created
AddAttributes(typeDefinition, td); AddAttributes(typeDefinition, td);
td.HasExtensionMethods = HasExtensionAttribute(typeDefinition); td.HasExtensionMethods = HasExtensionAttribute(typeDefinition);
InitBaseTypes(typeDefinition, td.BaseTypes);
td.AddDefaultConstructorIfRequired = (td.Kind == TypeKind.Struct || td.Kind == TypeKind.Enum);
InitMembers(typeDefinition, td, td.Members);
if (HasCecilReferences)
typeSystemTranslationTable[td] = typeDefinition;
if (this.InterningProvider != null) {
td.ApplyInterningProvider(this.InterningProvider);
}
td.Freeze();
}
void InitBaseTypes(TypeDefinition typeDefinition, IList<ITypeReference> baseTypes)
{
// set base classes // set base classes
if (typeDefinition.IsEnum) { if (typeDefinition.IsEnum) {
foreach (FieldDefinition enumField in typeDefinition.Fields) { foreach (FieldDefinition enumField in typeDefinition.Fields) {
if (!enumField.IsStatic) { if (!enumField.IsStatic) {
td.BaseTypes.Add(ReadTypeReference(enumField.FieldType)); baseTypes.Add(ReadTypeReference(enumField.FieldType));
break; break;
} }
} }
} else { } else {
if (typeDefinition.BaseType != null) { if (typeDefinition.BaseType != null) {
td.BaseTypes.Add(ReadTypeReference(typeDefinition.BaseType)); baseTypes.Add(ReadTypeReference(typeDefinition.BaseType));
} }
if (typeDefinition.HasInterfaces) { if (typeDefinition.HasInterfaces) {
foreach (TypeReference iface in typeDefinition.Interfaces) { foreach (TypeReference iface in typeDefinition.Interfaces) {
td.BaseTypes.Add(ReadTypeReference(iface)); baseTypes.Add(ReadTypeReference(iface));
} }
} }
} }
InitMembers(typeDefinition, td);
if (HasCecilReferences)
typeSystemTranslationTable[td] = typeDefinition;
if (this.InterningProvider != null) {
td.ApplyInterningProvider(this.InterningProvider);
}
td.Freeze();
} }
void InitNestedTypes(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td) void InitNestedTypes(TypeDefinition typeDefinition, IUnresolvedTypeDefinition declaringTypeDefinition, IList<IUnresolvedTypeDefinition> nestedTypes)
{ {
if (!typeDefinition.HasNestedTypes) if (!typeDefinition.HasNestedTypes)
return; return;
@ -1522,33 +1544,35 @@ namespace ICSharpCode.NRefactory.TypeSystem
int pos = name.LastIndexOf('/'); int pos = name.LastIndexOf('/');
if (pos > 0) if (pos > 0)
name = name.Substring(pos + 1); name = name.Substring(pos + 1);
if (name.Length == 0 || name[0] == '<')
continue;
name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name); name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name);
var nestedType = new DefaultUnresolvedTypeDefinition(td, name); var nestedType = new DefaultUnresolvedTypeDefinition(declaringTypeDefinition, name);
InitTypeParameters(nestedTypeDef, nestedType); InitTypeParameters(nestedTypeDef, nestedType.TypeParameters);
td.NestedTypes.Add(nestedType); nestedTypes.Add(nestedType);
InitTypeDefinition(nestedTypeDef, nestedType); InitTypeDefinition(nestedTypeDef, nestedType);
} }
} }
} }
static void InitTypeModifiers(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td) static TypeKind GetTypeKind(TypeDefinition typeDefinition)
{ {
// set classtype // set classtype
if (typeDefinition.IsInterface) { if (typeDefinition.IsInterface) {
td.Kind = TypeKind.Interface; return TypeKind.Interface;
} else if (typeDefinition.IsEnum) { } else if (typeDefinition.IsEnum) {
td.Kind = TypeKind.Enum; return TypeKind.Enum;
} else if (typeDefinition.IsValueType) { } else if (typeDefinition.IsValueType) {
td.Kind = TypeKind.Struct; return TypeKind.Struct;
} else if (IsDelegate(typeDefinition)) { } else if (IsDelegate(typeDefinition)) {
td.Kind = TypeKind.Delegate; return TypeKind.Delegate;
} else if (IsModule(typeDefinition)) { } else if (IsModule(typeDefinition)) {
td.Kind = TypeKind.Module; return TypeKind.Module;
} else { } else {
td.Kind = TypeKind.Class; return TypeKind.Class;
} }
}
static void InitTypeModifiers(TypeDefinition typeDefinition, AbstractUnresolvedEntity td)
{
td.IsSealed = typeDefinition.IsSealed; td.IsSealed = typeDefinition.IsSealed;
td.IsAbstract = typeDefinition.IsAbstract; td.IsAbstract = typeDefinition.IsAbstract;
switch (typeDefinition.Attributes & TypeAttributes.VisibilityMask) { switch (typeDefinition.Attributes & TypeAttributes.VisibilityMask) {
@ -1598,9 +1622,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
return false; return false;
} }
void InitMembers(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td) void InitMembers(TypeDefinition typeDefinition, IUnresolvedTypeDefinition td, IList<IUnresolvedMember> members)
{ {
td.AddDefaultConstructorIfRequired = (td.Kind == TypeKind.Struct || td.Kind == TypeKind.Enum);
if (typeDefinition.HasMethods) { if (typeDefinition.HasMethods) {
foreach (MethodDefinition method in typeDefinition.Methods) { foreach (MethodDefinition method in typeDefinition.Methods) {
if (IsVisible(method.Attributes) && !IsAccessor(method.SemanticsAttributes)) { if (IsVisible(method.Attributes) && !IsAccessor(method.SemanticsAttributes)) {
@ -1611,14 +1634,14 @@ namespace ICSharpCode.NRefactory.TypeSystem
else if (method.Name.StartsWith("op_", StringComparison.Ordinal)) else if (method.Name.StartsWith("op_", StringComparison.Ordinal))
type = EntityType.Operator; type = EntityType.Operator;
} }
td.Members.Add(ReadMethod(method, td, type)); members.Add(ReadMethod(method, td, type));
} }
} }
} }
if (typeDefinition.HasFields) { if (typeDefinition.HasFields) {
foreach (FieldDefinition field in typeDefinition.Fields) { foreach (FieldDefinition field in typeDefinition.Fields) {
if (IsVisible(field.Attributes) && !field.IsSpecialName) { if (IsVisible(field.Attributes) && !field.IsSpecialName) {
td.Members.Add(ReadField(field, td)); members.Add(ReadField(field, td));
} }
} }
} }
@ -1634,14 +1657,14 @@ namespace ICSharpCode.NRefactory.TypeSystem
bool setterVisible = property.SetMethod != null && IsVisible(property.SetMethod.Attributes); bool setterVisible = property.SetMethod != null && IsVisible(property.SetMethod.Attributes);
if (getterVisible || setterVisible) { if (getterVisible || setterVisible) {
EntityType type = property.Name == defaultMemberName ? EntityType.Indexer : EntityType.Property; EntityType type = property.Name == defaultMemberName ? EntityType.Indexer : EntityType.Property;
td.Members.Add(ReadProperty(property, td, type)); members.Add(ReadProperty(property, td, type));
} }
} }
} }
if (typeDefinition.HasEvents) { if (typeDefinition.HasEvents) {
foreach (EventDefinition ev in typeDefinition.Events) { foreach (EventDefinition ev in typeDefinition.Events) {
if (ev.AddMethod != null && IsVisible(ev.AddMethod.Attributes)) { if (ev.AddMethod != null && IsVisible(ev.AddMethod.Attributes)) {
td.Members.Add(ReadEvent(ev, td)); members.Add(ReadEvent(ev, td));
} }
} }
} }
@ -1653,6 +1676,140 @@ namespace ICSharpCode.NRefactory.TypeSystem
} }
#endregion #endregion
#region Lazy-Loaded Type Definition
sealed class LazyCecilTypeDefinition : AbstractUnresolvedEntity, IUnresolvedTypeDefinition
{
readonly CecilLoader loader;
readonly string namespaceName;
readonly TypeDefinition cecilTypeDef;
readonly TypeKind kind;
readonly IList<IUnresolvedTypeParameter> typeParameters;
IList<ITypeReference> baseTypes;
IList<IUnresolvedTypeDefinition> nestedTypes;
IList<IUnresolvedMember> members;
public LazyCecilTypeDefinition(CecilLoader loader, TypeDefinition typeDefinition)
{
this.loader = loader;
this.cecilTypeDef = typeDefinition;
this.EntityType = EntityType.TypeDefinition;
this.namespaceName = cecilTypeDef.Namespace;
this.Name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(cecilTypeDef.Name);
var tps = new List<IUnresolvedTypeParameter>();
InitTypeParameters(cecilTypeDef, tps);
this.typeParameters = FreezableHelper.FreezeList(tps);
this.kind = GetTypeKind(typeDefinition);
InitTypeModifiers(typeDefinition, this);
loader.InitTypeParameterConstraints(typeDefinition, typeParameters);
loader.AddAttributes(typeDefinition, this);
flags[FlagHasExtensionMethods] = HasExtensionAttribute(typeDefinition);
if (loader.HasCecilReferences)
loader.typeSystemTranslationTable[this] = typeDefinition;
if (loader.InterningProvider != null) {
this.ApplyInterningProvider(loader.InterningProvider);
}
this.Freeze();
}
public override string Namespace {
get { return namespaceName; }
set { throw new NotSupportedException(); }
}
public TypeKind Kind {
get { return kind; }
}
public IList<IUnresolvedTypeParameter> TypeParameters {
get { return typeParameters; }
}
public IList<ITypeReference> BaseTypes {
get {
var result = LazyInit.VolatileRead(ref this.baseTypes);
if (result != null) {
return result;
} else {
result = new List<ITypeReference>();
loader.InitBaseTypes(cecilTypeDef, result);
return LazyInit.GetOrSet(ref this.baseTypes, FreezableHelper.FreezeList(result));
}
}
}
public IList<IUnresolvedTypeDefinition> NestedTypes {
get {
var result = LazyInit.VolatileRead(ref this.nestedTypes);
if (result != null) {
return result;
} else {
result = new List<IUnresolvedTypeDefinition>();
loader.InitNestedTypes(cecilTypeDef, this, result);
return LazyInit.GetOrSet(ref this.nestedTypes, FreezableHelper.FreezeList(result));
}
}
}
public IList<IUnresolvedMember> Members {
get {
var result = LazyInit.VolatileRead(ref this.members);
if (result != null) {
return result;
} else {
result = new List<IUnresolvedMember>();
loader.InitMembers(cecilTypeDef, this, result);
return LazyInit.GetOrSet(ref this.members, FreezableHelper.FreezeList(result));
}
}
}
public IEnumerable<IUnresolvedMethod> Methods {
get { return Members.OfType<IUnresolvedMethod>(); }
}
public IEnumerable<IUnresolvedProperty> Properties {
get { return Members.OfType<IUnresolvedProperty>(); }
}
public IEnumerable<IUnresolvedField> Fields {
get { return Members.OfType<IUnresolvedField>(); }
}
public IEnumerable<IUnresolvedEvent> Events {
get { return Members.OfType<IUnresolvedEvent>(); }
}
public bool AddDefaultConstructorIfRequired {
get { return kind == TypeKind.Struct || kind == TypeKind.Enum; }
}
public bool? HasExtensionMethods {
get { return flags[FlagHasExtensionMethods]; }
// we always return true or false, never null.
// FlagHasNoExtensionMethods is unused in LazyCecilTypeDefinition
}
public IType Resolve(ITypeResolveContext context)
{
if (context == null)
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)
?? (IType)new UnknownType(this.Namespace, this.Name, this.TypeParameters.Count);
}
public ITypeResolveContext CreateResolveContext(ITypeResolveContext parentContext)
{
return parentContext;
}
}
#endregion
#region Read Method #region Read Method
[CLSCompliant(false)] [CLSCompliant(false)]
public IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, EntityType methodType = EntityType.Method) public IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, EntityType methodType = EntityType.Method)

6
ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs

@ -48,6 +48,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary> /// </summary>
bool? HasExtensionMethods { get; } bool? HasExtensionMethods { get; }
/// <summary>
/// Gets whether this unresolved type definition causes the addition of a default constructor
/// if no other constructor is present.
/// </summary>
bool AddDefaultConstructorIfRequired { get; }
/// <summary> /// <summary>
/// Looks up the resolved type definition from the <paramref name="context"/> corresponding to this unresolved /// Looks up the resolved type definition from the <paramref name="context"/> corresponding to this unresolved
/// type definition. /// type definition.

2
ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
internal const ushort FlagShadowing = 0x0008; internal const ushort FlagShadowing = 0x0008;
internal const ushort FlagSynthetic = 0x0010; internal const ushort FlagSynthetic = 0x0010;
internal const ushort FlagStatic = 0x0020; internal const ushort FlagStatic = 0x0020;
// flags for DefaultUnresolvedTypeDefinition // flags for DefaultUnresolvedTypeDefinition/LazyCecilTypeDefinition
internal const ushort FlagAddDefaultConstructorIfRequired = 0x0040; internal const ushort FlagAddDefaultConstructorIfRequired = 0x0040;
internal const ushort FlagHasExtensionMethods = 0x0080; internal const ushort FlagHasExtensionMethods = 0x0080;
internal const ushort FlagHasNoExtensionMethods = 0x0100; internal const ushort FlagHasNoExtensionMethods = 0x0100;

5
ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs

@ -322,10 +322,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
} }
} }
DefaultUnresolvedTypeDefinition dutd = part as DefaultUnresolvedTypeDefinition; addDefaultConstructorIfRequired |= part.AddDefaultConstructorIfRequired;
if (dutd != null) {
addDefaultConstructorIfRequired |= dutd.AddDefaultConstructorIfRequired;
}
} }
if (addDefaultConstructorIfRequired) { if (addDefaultConstructorIfRequired) {
TypeKind kind = this.Kind; TypeKind kind = this.Kind;

Loading…
Cancel
Save