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

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

@ -0,0 +1,36 @@ @@ -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 @@ -47,6 +47,16 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
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>
/// Gets/Sets the documentation provider that is used to retrieve the XML documentation for all members.
/// </summary>
@ -137,7 +147,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -137,7 +147,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
this.CancellationToken.ThrowIfCancellationRequested();
if (this.IncludeInternalMembers || (td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) {
string name = td.Name;
if (name.Length == 0 || name[0] == '<')
if (name.Length == 0)
continue;
var t = CreateTopLevelTypeDefinition(td);
@ -166,6 +176,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -166,6 +176,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// This causes ReadTypeReference() to use <see cref="DefaultAssemblyReference.CurrentAssembly"/> for references
/// in that module.
/// </summary>
[CLSCompliant(false)]
public void SetCurrentModule(ModuleDefinition module)
{
this.currentModule = module;
@ -1449,65 +1460,76 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -1449,65 +1460,76 @@ namespace ICSharpCode.NRefactory.TypeSystem
{
string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(typeDefinition.Name);
var td = new DefaultUnresolvedTypeDefinition(typeDefinition.Namespace, name);
InitTypeParameters(typeDefinition, td);
if (typeDefinition.HasGenericParameters)
InitTypeParameters(typeDefinition, td.TypeParameters);
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
// 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++) {
if (typeDefinition.GenericParameters[i].Position != i)
throw new InvalidOperationException("g.Position != i");
td.TypeParameters.Add(new DefaultUnresolvedTypeParameter(
typeParameters.Add(new DefaultUnresolvedTypeParameter(
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)
{
td.Kind = GetTypeKind(typeDefinition);
InitTypeModifiers(typeDefinition, td);
InitTypeParameterConstraints(typeDefinition, td.TypeParameters);
if (typeDefinition.HasGenericParameters) {
for (int i = 0; i < typeDefinition.GenericParameters.Count; i++) {
AddConstraints((DefaultUnresolvedTypeParameter)td.TypeParameters[i], typeDefinition.GenericParameters[i]);
}
}
InitNestedTypes(typeDefinition, td); // nested types can be initialized only after generic parameters were created
// nested types can be initialized only after generic parameters were created
InitNestedTypes(typeDefinition, td, td.NestedTypes);
AddAttributes(typeDefinition, td);
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
if (typeDefinition.IsEnum) {
foreach (FieldDefinition enumField in typeDefinition.Fields) {
if (!enumField.IsStatic) {
td.BaseTypes.Add(ReadTypeReference(enumField.FieldType));
baseTypes.Add(ReadTypeReference(enumField.FieldType));
break;
}
}
} else {
if (typeDefinition.BaseType != null) {
td.BaseTypes.Add(ReadTypeReference(typeDefinition.BaseType));
baseTypes.Add(ReadTypeReference(typeDefinition.BaseType));
}
if (typeDefinition.HasInterfaces) {
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)
return;
@ -1522,33 +1544,35 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -1522,33 +1544,35 @@ namespace ICSharpCode.NRefactory.TypeSystem
int pos = name.LastIndexOf('/');
if (pos > 0)
name = name.Substring(pos + 1);
if (name.Length == 0 || name[0] == '<')
continue;
name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name);
var nestedType = new DefaultUnresolvedTypeDefinition(td, name);
InitTypeParameters(nestedTypeDef, nestedType);
td.NestedTypes.Add(nestedType);
var nestedType = new DefaultUnresolvedTypeDefinition(declaringTypeDefinition, name);
InitTypeParameters(nestedTypeDef, nestedType.TypeParameters);
nestedTypes.Add(nestedType);
InitTypeDefinition(nestedTypeDef, nestedType);
}
}
}
static void InitTypeModifiers(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td)
static TypeKind GetTypeKind(TypeDefinition typeDefinition)
{
// set classtype
if (typeDefinition.IsInterface) {
td.Kind = TypeKind.Interface;
return TypeKind.Interface;
} else if (typeDefinition.IsEnum) {
td.Kind = TypeKind.Enum;
return TypeKind.Enum;
} else if (typeDefinition.IsValueType) {
td.Kind = TypeKind.Struct;
return TypeKind.Struct;
} else if (IsDelegate(typeDefinition)) {
td.Kind = TypeKind.Delegate;
return TypeKind.Delegate;
} else if (IsModule(typeDefinition)) {
td.Kind = TypeKind.Module;
return TypeKind.Module;
} else {
td.Kind = TypeKind.Class;
return TypeKind.Class;
}
}
static void InitTypeModifiers(TypeDefinition typeDefinition, AbstractUnresolvedEntity td)
{
td.IsSealed = typeDefinition.IsSealed;
td.IsAbstract = typeDefinition.IsAbstract;
switch (typeDefinition.Attributes & TypeAttributes.VisibilityMask) {
@ -1598,9 +1622,8 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -1598,9 +1622,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
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) {
foreach (MethodDefinition method in typeDefinition.Methods) {
if (IsVisible(method.Attributes) && !IsAccessor(method.SemanticsAttributes)) {
@ -1611,14 +1634,14 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -1611,14 +1634,14 @@ namespace ICSharpCode.NRefactory.TypeSystem
else if (method.Name.StartsWith("op_", StringComparison.Ordinal))
type = EntityType.Operator;
}
td.Members.Add(ReadMethod(method, td, type));
members.Add(ReadMethod(method, td, type));
}
}
}
if (typeDefinition.HasFields) {
foreach (FieldDefinition field in typeDefinition.Fields) {
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 @@ -1634,14 +1657,14 @@ namespace ICSharpCode.NRefactory.TypeSystem
bool setterVisible = property.SetMethod != null && IsVisible(property.SetMethod.Attributes);
if (getterVisible || setterVisible) {
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) {
foreach (EventDefinition ev in typeDefinition.Events) {
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 @@ -1653,6 +1676,140 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
#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
[CLSCompliant(false)]
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 @@ -48,6 +48,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
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>
/// Looks up the resolved type definition from the <paramref name="context"/> corresponding to this unresolved
/// type definition.

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

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

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

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

Loading…
Cancel
Save