diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
index 407f7dc19b..9b68806161 100644
--- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
+++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
@@ -191,6 +191,7 @@
+
diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/LazyLoadedCecilLoaderTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/LazyLoadedCecilLoaderTests.cs
new file mode 100644
index 0000000000..2000207509
--- /dev/null
+++ b/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);
+ }
+ }
+}
diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
index aa60bd995c..8b97a43897 100644
--- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
+++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
@@ -47,6 +47,16 @@ namespace ICSharpCode.NRefactory.TypeSystem
///
public bool IncludeInternalMembers { get; set; }
+ ///
+ /// 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.
+ ///
+ public bool LazyLoad { get; set; }
+
///
/// Gets/Sets the documentation provider that is used to retrieve the XML documentation for all members.
///
@@ -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
/// This causes ReadTypeReference() to use for references
/// in that module.
///
+ [CLSCompliant(false)]
public void SetCurrentModule(ModuleDefinition module)
{
this.currentModule = module;
@@ -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 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 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 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 nestedTypes)
{
if (!typeDefinition.HasNestedTypes)
return;
@@ -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
return false;
}
- void InitMembers(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td)
+ void InitMembers(TypeDefinition typeDefinition, IUnresolvedTypeDefinition td, IList 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
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
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
}
#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 typeParameters;
+
+ IList baseTypes;
+ IList nestedTypes;
+ IList 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();
+ 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 TypeParameters {
+ get { return typeParameters; }
+ }
+
+ public IList BaseTypes {
+ get {
+ var result = LazyInit.VolatileRead(ref this.baseTypes);
+ if (result != null) {
+ return result;
+ } else {
+ result = new List();
+ loader.InitBaseTypes(cecilTypeDef, result);
+ return LazyInit.GetOrSet(ref this.baseTypes, FreezableHelper.FreezeList(result));
+ }
+ }
+ }
+
+ public IList NestedTypes {
+ get {
+ var result = LazyInit.VolatileRead(ref this.nestedTypes);
+ if (result != null) {
+ return result;
+ } else {
+ result = new List();
+ loader.InitNestedTypes(cecilTypeDef, this, result);
+ return LazyInit.GetOrSet(ref this.nestedTypes, FreezableHelper.FreezeList(result));
+ }
+ }
+ }
+
+ public IList Members {
+ get {
+ var result = LazyInit.VolatileRead(ref this.members);
+ if (result != null) {
+ return result;
+ } else {
+ result = new List();
+ loader.InitMembers(cecilTypeDef, this, result);
+ return LazyInit.GetOrSet(ref this.members, FreezableHelper.FreezeList(result));
+ }
+ }
+ }
+
+ public IEnumerable Methods {
+ get { return Members.OfType(); }
+ }
+
+ public IEnumerable Properties {
+ get { return Members.OfType(); }
+ }
+
+ public IEnumerable Fields {
+ get { return Members.OfType(); }
+ }
+
+ public IEnumerable Events {
+ get { return Members.OfType(); }
+ }
+
+ 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)
diff --git a/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs
index 508f9ce810..1438505d8b 100644
--- a/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs
+++ b/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs
@@ -48,6 +48,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
///
bool? HasExtensionMethods { get; }
+ ///
+ /// Gets whether this unresolved type definition causes the addition of a default constructor
+ /// if no other constructor is present.
+ ///
+ bool AddDefaultConstructorIfRequired { get; }
+
///
/// Looks up the resolved type definition from the corresponding to this unresolved
/// type definition.
diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs
index d0a040e454..66d29b8c0c 100644
--- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs
+++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs
@@ -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;
diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs
index c4fb589e9b..4ea28dbe69 100644
--- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs
+++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs
@@ -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;