Skip to content

Commit ebd2972

Browse files
committed
Add initial support for conditional deserialization\COM reflection.
1 parent 1afea24 commit ebd2972

File tree

9 files changed

+135
-55
lines changed

9 files changed

+135
-55
lines changed

RetailCoder.VBE/Root/RubberduckModule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ private void ApplyConfigurationConvention(IEnumerable<Assembly> assemblies)
187187
.BindAllInterfaces()
188188
.Configure(binding => binding.InSingletonScope()));
189189

190-
Bind<IPersistable<SerializableDeclarationTree>>().To<XmlPersistableDeclarations>().InCallScope();
190+
Bind<IPersistable<SerializableProject>>().To<XmlPersistableDeclarations>().InCallScope();
191191

192192
Bind<IPersistanceService<CodeInspectionSettings>>().To<XmlPersistanceService<CodeInspectionSettings>>().InCallScope();
193193
Bind<IPersistanceService<GeneralSettings>>().To<XmlPersistanceService<GeneralSettings>>().InCallScope();

RetailCoder.VBE/UI/Command/MenuItems/CommandBars/SerializeDeclarationsCommandMenuItem.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ public SerializeDeclarationsCommandMenuItem(CommandBase command) : base(command)
2020
public class SerializeDeclarationsCommand : CommandBase
2121
{
2222
private readonly RubberduckParserState _state;
23-
private readonly IPersistable<SerializableDeclarationTree> _service;
23+
private readonly IPersistable<SerializableProject> _service;
2424

25-
public SerializeDeclarationsCommand(RubberduckParserState state, IPersistable<SerializableDeclarationTree> service)
25+
public SerializeDeclarationsCommand(RubberduckParserState state, IPersistable<SerializableProject> service)
2626
: base(LogManager.GetCurrentClassLogger())
2727
{
2828
_state = state;
@@ -39,14 +39,14 @@ protected override bool CanExecuteImpl(object parameter)
3939

4040
protected override void ExecuteImpl(object parameter)
4141
{
42-
var path = Path.Combine(BasePath, "declarations");
42+
var path = Path.Combine(BasePath, "Declarations");
4343
if (!Directory.Exists(path)) { Directory.CreateDirectory(path); }
4444

4545
foreach (var tree in _state.BuiltInDeclarationTrees)
4646
{
4747
System.Diagnostics.Debug.Assert(path != null, "project path isn't supposed to be null");
4848

49-
var filename = Path.GetFileNameWithoutExtension(tree.Node.QualifiedMemberName.QualifiedModuleName.ProjectName) + ".xml";
49+
var filename = string.Format("{0}.{1}.{2}", tree.Node.QualifiedMemberName.QualifiedModuleName.ProjectName, tree.MajorVersion, tree.MinorVersion) + ".xml";
5050
_service.Persist(Path.Combine(path, filename), tree);
5151
}
5252
}

Rubberduck.Parsing/Symbols/ComInformation.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Collections.Generic;
2-
using System.Runtime.InteropServices.ComTypes;
1+
using System.Runtime.InteropServices.ComTypes;
32
using Rubberduck.VBEditor;
43

54
namespace Rubberduck.Parsing.Symbols

Rubberduck.Parsing/Symbols/ProjectDeclaration.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ public ProjectDeclaration(
3131
_projectReferences = new List<ProjectReference>();
3232
}
3333

34+
public long MajorVersion { get; set; }
35+
public long MinorVersion { get; set; }
36+
3437
public IReadOnlyList<ProjectReference> ProjectReferences
3538
{
3639
get

Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
34
using System.Runtime.InteropServices;
45
using System.Runtime.InteropServices.ComTypes;
6+
using NLog;
57
using Rubberduck.Parsing.VBA;
68
using Rubberduck.VBEditor;
79
using CALLCONV = System.Runtime.InteropServices.ComTypes.CALLCONV;
@@ -16,15 +18,16 @@
1618
using TYPEFLAGS = System.Runtime.InteropServices.ComTypes.TYPEFLAGS;
1719
using VARDESC = System.Runtime.InteropServices.ComTypes.VARDESC;
1820
using Rubberduck.Parsing.Annotations;
19-
using Rubberduck.Parsing.Grammar;
2021
using IMPLTYPEFLAGS = System.Runtime.InteropServices.ComTypes.IMPLTYPEFLAGS;
22+
using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR;
2123
using System.Linq;
2224
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
2325

2426
namespace Rubberduck.Parsing.Symbols
2527
{
2628
public class ReferencedDeclarationsCollector
2729
{
30+
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
2831
private readonly RubberduckParserState _state;
2932

3033
/// <summary>
@@ -146,6 +149,40 @@ private string GetTypeName(ITypeInfo info)
146149
return typeName.Equals("LONG_PTR") ? "LongPtr" : typeName; //Quickfix for http://chat.stackexchange.com/transcript/message/30119269#30119269
147150
}
148151

152+
private void LoadVersionFromTypeLib(ITypeLib tlb, ProjectDeclaration project)
153+
{
154+
var attribPtr = IntPtr.Zero;
155+
tlb.GetLibAttr(out attribPtr);
156+
var typeAttr = (TYPELIBATTR)Marshal.PtrToStructure(attribPtr, typeof(TYPELIBATTR));
157+
project.MajorVersion = typeAttr.wMajorVerNum;
158+
project.MinorVersion = typeAttr.wMinorVerNum;
159+
}
160+
161+
private bool SerializedVersionExists(ProjectDeclaration project)
162+
{
163+
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "Declarations");
164+
if (!Directory.Exists(path))
165+
{
166+
return false;
167+
}
168+
//TODO: This is naively based on file name for now - this should attempt to deserialize any SerializableProject.Nodes in the directory and test for equity.
169+
var testFile = Path.Combine(path, string.Format("{0}.{1}.{2}", project.ProjectName, project.MajorVersion, project.MinorVersion) + ".xml");
170+
if (File.Exists(testFile))
171+
{
172+
return true;
173+
}
174+
return false;
175+
}
176+
177+
private List<Declaration> LoadSerializedBuiltInReferences(ProjectDeclaration project)
178+
{
179+
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "Declarations");
180+
var file = Path.Combine(path, string.Format("{0}.{1}.{2}", project.ProjectName, project.MajorVersion, project.MinorVersion) + ".xml");
181+
var reader = new XmlPersistableDeclarations();
182+
var deserialized = reader.Load(file);
183+
return deserialized.Unwrap();
184+
}
185+
149186
public List<Declaration> GetDeclarationsForReference(IReference reference)
150187
{
151188
var output = new List<Declaration>();
@@ -161,6 +198,13 @@ public List<Declaration> GetDeclarationsForReference(IReference reference)
161198
var projectQualifiedModuleName = new QualifiedModuleName(projectName, path, projectName);
162199
var projectQualifiedMemberName = new QualifiedMemberName(projectQualifiedModuleName, projectName);
163200
var projectDeclaration = new ProjectDeclaration(projectQualifiedMemberName, projectName, isBuiltIn: true);
201+
LoadVersionFromTypeLib(typeLibrary, projectDeclaration);
202+
if (SerializedVersionExists(projectDeclaration))
203+
{
204+
Logger.Trace(string.Format("Deserializing reference '{0}'.", reference.Name));
205+
return LoadSerializedBuiltInReferences(projectDeclaration);
206+
}
207+
Logger.Trace(string.Format("COM reflecting reference '{0}'.", reference.Name));
164208
output.Add(projectDeclaration);
165209

166210
var typeCount = typeLibrary.GetTypeInfoCount();
@@ -169,7 +213,7 @@ public List<Declaration> GetDeclarationsForReference(IReference reference)
169213
ITypeInfo info;
170214
try
171215
{
172-
typeLibrary.GetTypeInfo(i, out info);
216+
typeLibrary.GetTypeInfo(i, out info);
173217
}
174218
catch (NullReferenceException)
175219
{
@@ -200,6 +244,7 @@ public List<Declaration> GetDeclarationsForReference(IReference reference)
200244
IntPtr typeAttributesPointer;
201245
info.GetTypeAttr(out typeAttributesPointer);
202246
var typeAttributes = (TYPEATTR)Marshal.PtrToStructure(typeAttributesPointer, typeof(TYPEATTR));
247+
203248
var attributes = new Attributes();
204249

205250
if (typeAttributes.wTypeFlags.HasFlag(TYPEFLAGS.TYPEFLAG_FPREDECLID))

Rubberduck.Parsing/Symbols/SerializableDeclaration.cs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77

88
namespace Rubberduck.Parsing.Symbols
99
{
10-
[DataContract]
1110
public class SerializableDeclarationTree
1211
{
13-
[DataMember(IsRequired = true)]
14-
public readonly SerializableDeclaration Node;
12+
public SerializableDeclaration Node;
1513

16-
[DataMember(IsRequired = true)]
17-
public readonly IEnumerable<SerializableDeclarationTree> Children;
14+
public IEnumerable<SerializableDeclarationTree> Children;
15+
16+
public SerializableDeclarationTree() { }
1817

1918
public SerializableDeclarationTree(Declaration declaration)
2019
: this(new SerializableDeclaration(declaration)) { }
@@ -48,6 +47,57 @@ public SerializableMemberAttribute(string name, IEnumerable<string> values)
4847
public readonly IEnumerable<string> Values;
4948
}
5049

50+
[DataContract]
51+
public class SerializableProject
52+
{
53+
public SerializableProject() { }
54+
55+
public SerializableProject(Declaration declaration)
56+
{
57+
Node = new SerializableDeclaration(declaration);
58+
var project = (ProjectDeclaration)declaration;
59+
MajorVersion = project.MajorVersion;
60+
MinorVersion = project.MinorVersion;
61+
}
62+
63+
[DataMember(IsRequired = true)]
64+
public SerializableDeclaration Node { get; set; }
65+
[DataMember(IsRequired = true)]
66+
public IEnumerable<SerializableDeclarationTree> Declarations { get; set; }
67+
[DataMember(IsRequired = true)]
68+
public long MajorVersion { get; set; }
69+
[DataMember(IsRequired = true)]
70+
public long MinorVersion { get; set; }
71+
72+
public List<Declaration> Unwrap()
73+
{
74+
var project = (ProjectDeclaration)Node.Unwrap(null);
75+
project.MajorVersion = MajorVersion;
76+
project.MinorVersion = MinorVersion;
77+
var output = new List<Declaration> {project};
78+
foreach (var declaration in Declarations)
79+
{
80+
output.AddRange(UnwrapTree(declaration, project));
81+
}
82+
return output;
83+
}
84+
85+
private IEnumerable<Declaration> UnwrapTree(SerializableDeclarationTree tree, Declaration parent = null)
86+
{
87+
var current = tree.Node.Unwrap(parent);
88+
yield return current;
89+
90+
foreach (var serializableDeclarationTree in tree.Children)
91+
{
92+
var unwrapped = UnwrapTree(serializableDeclarationTree, current);
93+
foreach (var declaration in unwrapped)
94+
{
95+
yield return declaration;
96+
}
97+
}
98+
}
99+
}
100+
51101
public class SerializableDeclaration
52102
{
53103
public SerializableDeclaration()
@@ -123,7 +173,7 @@ public Declaration Unwrap(Declaration parent)
123173
switch (DeclarationType)
124174
{
125175
case DeclarationType.Project:
126-
return new ProjectDeclaration(QualifiedMemberName, IdentifierName, true);
176+
return new ProjectDeclaration(QualifiedMemberName, IdentifierName, true);
127177
case DeclarationType.ClassModule:
128178
return new ClassModuleDeclaration(QualifiedMemberName, parent, IdentifierName, true, annotations, attributes);
129179
case DeclarationType.ProceduralModule:

Rubberduck.Parsing/Symbols/XmlPersistableDeclarations.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
namespace Rubberduck.Parsing.Symbols
99
{
10-
public class XmlPersistableDeclarations : IPersistable<SerializableDeclarationTree>
10+
public class XmlPersistableDeclarations : IPersistable<SerializableProject>
1111
{
12-
public void Persist(string path, SerializableDeclarationTree tree)
12+
public void Persist(string path, SerializableProject tree)
1313
{
1414
if (string.IsNullOrEmpty(path)) { throw new InvalidOperationException(); }
1515

@@ -26,20 +26,20 @@ public void Persist(string path, SerializableDeclarationTree tree)
2626
{
2727
writer.WriteStartDocument();
2828
var settings = new DataContractSerializerSettings {RootNamespace = XmlDictionaryString.Empty};
29-
var serializer = new DataContractSerializer(typeof (SerializableDeclarationTree), settings);
29+
var serializer = new DataContractSerializer(typeof(SerializableProject), settings);
3030
serializer.WriteObject(writer, tree);
3131
}
3232
}
3333

34-
public SerializableDeclarationTree Load(string path)
34+
public SerializableProject Load(string path)
3535
{
3636
if (string.IsNullOrEmpty(path)) { throw new InvalidOperationException(); }
3737
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
3838
using (var xmlReader = XmlReader.Create(stream))
3939
using (var reader = XmlDictionaryReader.CreateDictionaryReader(xmlReader))
4040
{
41-
var serializer = new DataContractSerializer(typeof(SerializableDeclarationTree));
42-
return (SerializableDeclarationTree)serializer.ReadObject(reader);
41+
var serializer = new DataContractSerializer(typeof(SerializableProject));
42+
return (SerializableProject)serializer.ReadObject(reader);
4343
}
4444
}
4545
}

Rubberduck.Parsing/VBA/RubberduckParser.cs

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ private void SyncComReferences(IReadOnlyList<IVBProject> projects)
432432
{
433433
try
434434
{
435+
Logger.Trace(string.Format("Loading referenced type '{0}'.", reference.Name));
435436
var comReflector = new ReferencedDeclarationsCollector(State);
436437

437438
var items = comReflector.GetDeclarationsForReference(localReference);
@@ -442,11 +443,11 @@ private void SyncComReferences(IReadOnlyList<IVBProject> projects)
442443
State.AddDeclaration(declaration);
443444
}
444445
serialize.Remove(root);
445-
var tree = GetSerializableTreeForDeclaration(root, serialize);
446446

447-
if (tree != null)
447+
var project = GetSerializableProject(root, serialize);
448+
if (project != null)
448449
{
449-
var added = State.BuiltInDeclarationTrees.TryAdd(tree);
450+
var added = State.BuiltInDeclarationTrees.TryAdd(project);
450451
//if (!added) { throw new Exception();}
451452
}
452453
}
@@ -491,47 +492,29 @@ private void SyncComReferences(IReadOnlyList<IVBProject> projects)
491492
}
492493
}
493494

494-
private SerializableDeclarationTree GetSerializableTreeForDeclaration(Declaration declaration, List<Declaration> declarations)
495+
private SerializableProject GetSerializableProject(ProjectDeclaration declaration, List<Declaration> declarations)
495496
{
497+
var project = new SerializableProject(declaration);
496498
var children = new List<SerializableDeclarationTree>();
497499
var nodes = declarations.Where(x => x.ParentDeclaration.Equals(declaration)).ToList();
498-
declarations.RemoveAll(nodes.Contains);
499500
foreach (var item in nodes)
500501
{
501502
children.Add(GetSerializableTreeForDeclaration(item, declarations));
502503
}
503-
504-
return new SerializableDeclarationTree(declaration, children);
505-
}
506-
507-
private void LoadSerializedBuiltInReferences(RubberduckParserState state)
508-
{
509-
var basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "Declarations");
510-
var files = Directory.GetFiles(basePath, "*.xml");
511-
var reader = new XmlPersistableDeclarations();
512-
foreach (var file in files)
513-
{
514-
var tree = reader.Load(file);
515-
foreach (var declaration in UnwrapTree(tree))
516-
{
517-
state.AddDeclaration(declaration);
518-
}
519-
}
504+
project.Declarations = children;
505+
return project;
520506
}
521507

522-
private IEnumerable<Declaration> UnwrapTree(SerializableDeclarationTree tree, Declaration parent = null)
508+
private SerializableDeclarationTree GetSerializableTreeForDeclaration(Declaration declaration, List<Declaration> declarations)
523509
{
524-
var current = tree.Node.Unwrap(parent);
525-
yield return current;
526-
527-
foreach (var serializableDeclarationTree in tree.Children)
510+
var children = new List<SerializableDeclarationTree>();
511+
var nodes = declarations.Where(x => x.ParentDeclaration.Equals(declaration)).ToList();
512+
declarations.RemoveAll(nodes.Contains);
513+
foreach (var item in nodes)
528514
{
529-
var unwrapped = UnwrapTree(serializableDeclarationTree, current);
530-
foreach (var declaration in unwrapped)
531-
{
532-
yield return declaration;
533-
}
515+
children.Add(GetSerializableTreeForDeclaration(item, declarations));
534516
}
517+
return new SerializableDeclarationTree(declaration, children);
535518
}
536519

537520
private void UnloadComReference(IReference reference, IReadOnlyList<IVBProject> projects)

Rubberduck.Parsing/VBA/RubberduckParserState.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,8 +586,8 @@ public IReadOnlyList<Declaration> AllDeclarations
586586
}
587587
}
588588

589-
private readonly ConcurrentBag<SerializableDeclarationTree> _builtInDeclarationTrees = new ConcurrentBag<SerializableDeclarationTree>();
590-
public IProducerConsumerCollection<SerializableDeclarationTree> BuiltInDeclarationTrees { get { return _builtInDeclarationTrees; } }
589+
private readonly ConcurrentBag<SerializableProject> _builtInDeclarationTrees = new ConcurrentBag<SerializableProject>();
590+
public IProducerConsumerCollection<SerializableProject> BuiltInDeclarationTrees { get { return _builtInDeclarationTrees; } }
591591

592592
/// <summary>
593593
/// Gets a copy of the collected declarations, excluding the built-in ones.

0 commit comments

Comments
 (0)