2
// CodeCompletionDatabase.cs
7
// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
9
// Permission is hereby granted, free of charge, to any person obtaining
10
// a copy of this software and associated documentation files (the
11
// "Software"), to deal in the Software without restriction, including
12
// without limitation the rights to use, copy, modify, merge, publish,
13
// distribute, sublicense, and/or sell copies of the Software, and to
14
// permit persons to whom the Software is furnished to do so, subject to
15
// the following conditions:
17
// The above copyright notice and this permission notice shall be
18
// included in all copies or substantial portions of the Software.
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
//#define CHECK_STRINGS
34
using System.Collections;
35
using System.Collections.Specialized;
36
using System.Collections.Generic;
37
using System.Collections.ObjectModel;
38
using System.Runtime.Serialization;
39
using System.Runtime.Serialization.Formatters.Binary;
41
using MonoDevelop.Core;
43
using System.Reflection;
44
using MonoDevelop.Projects.Dom.Parser;
45
using MonoDevelop.Core.Instrumentation;
47
namespace MonoDevelop.Projects.Dom.Serialization
49
internal class SerializationCodeCompletionDatabase : IDisposable
51
static protected readonly int MAX_ACTIVE_COUNT = 100;
52
static protected readonly int MIN_ACTIVE_COUNT = 10;
53
static protected readonly int FORMAT_VERSION = 77;
55
NamespaceEntry rootNamespace;
56
protected ArrayList references;
57
protected Hashtable files;
58
protected Hashtable headers;
61
BinaryReader datareader;
62
FileStream dataFileStream;
63
int currentGetTime = 0;
66
bool handlesCommentTags;
70
DatabaseProjectDom sourceProjectDom;
72
// This table stores type->subclasses relations for types which are not
73
// known in this database. For example, types declared in other databases.
74
// For known types, the type->subclasses relation is stored in the corresponding
75
// ClassEntry object, not here. Inner classes don't have a class entry, so their
76
// relations are also stored here.
77
// The key of the hashtable is the full name of a type. The value is an ArrayList
78
// which can contain ClassEntry objects, or other full type names (this second case
79
// is only used for inner classes).
80
Hashtable unresolvedSubclassTable = new Hashtable ();
82
protected Object rwlock = new Object ();
84
public SerializationCodeCompletionDatabase (ParserDatabase pdb, bool handlesCommentTags)
86
Counters.LiveDatabases++;
87
this.handlesCommentTags = handlesCommentTags;
89
rootNamespace = new NamespaceEntry (null, null);
90
files = new Hashtable ();
91
references = new ArrayList ();
92
headers = new Hashtable ();
95
if (handlesCommentTags)
96
ProjectDomService.SpecialCommentTagsChanged += OnSpecialTagsChanged;
99
public virtual void Dispose ()
106
if (dataFileStream != null)
107
dataFileStream.Close ();
108
if (tempDataFile != null) {
109
File.Delete (tempDataFile);
112
if (handlesCommentTags)
113
ProjectDomService.SpecialCommentTagsChanged -= OnSpecialTagsChanged;
115
Counters.LiveDatabases--;
118
~SerializationCodeCompletionDatabase ()
120
if (tempDataFile != null) {
121
File.Delete (tempDataFile);
126
public string DataFile {
127
get { return dataFile; }
130
// File where data is actually readen from of written to. It can be a temp file.
131
public string RealDataFile {
132
get { return tempDataFile ?? dataFile; }
135
public bool Modified {
140
public bool Disposed {
141
get { return disposed; }
144
public virtual DatabaseProjectDom SourceProjectDom {
145
get { return sourceProjectDom; }
146
set { sourceProjectDom = value; }
149
public virtual Project Project {
150
get { return sourceProjectDom != null ? sourceProjectDom.Project : null; }
153
protected void SetLocation (string basePath, string name)
155
dataFile = Path.Combine (basePath, name + ".pidb");
158
protected void SetFile (string file)
163
protected internal virtual void ForceUpdateBROKEN ()
165
ArrayList list = GetModifiedFileEntries ();
166
foreach (FileEntry file in list) {
167
ParseFile (file.FileName, null);
169
FileInfo fi = new FileInfo (file.FileName);
170
file.LastParseTime = fi.LastWriteTime;
177
public virtual void Read ()
182
void Read (bool verify)
184
if (!File.Exists (dataFile)) return;
186
ITimeTracker timer = Counters.DatabasesRead.BeginTiming ("Reading Parser Database " + dataFile);
190
timer.Trace ("Clearing");
196
timer.Trace ("Opening file");
197
dataFileStream = OpenForWrite ();
198
datareader = new BinaryReader (dataFileStream);
199
} catch (Exception ex) {
200
LoggingService.LogError ("PIDB file '{0}' could not be loaded: '{1}'. The file will be recreated.", dataFile, ex);
210
BinaryFormatter bf = new BinaryFormatter ();
212
timer.Trace ("Read headers");
215
headers = (Hashtable) bf.Deserialize (dataFileStream);
216
int ver = (int) headers["Version"];
217
if (ver != FORMAT_VERSION)
218
throw new OldPidbVersionException (ver, FORMAT_VERSION);
220
timer.Trace ("Read index");
222
// Move to the index offset and read the index
223
BinaryReader br = new BinaryReader (dataFileStream);
224
long indexOffset = br.ReadInt64 ();
225
dataFileStream.Position = indexOffset;
227
object oo = bf.Deserialize (dataFileStream);
228
object[] data = (object[]) oo;
229
Queue dataQueue = new Queue (data);
230
references = (ArrayList) dataQueue.Dequeue ();
231
rootNamespace = (NamespaceEntry) dataQueue.Dequeue ();
232
files = (Hashtable) dataQueue.Dequeue ();
233
unresolvedSubclassTable = (Hashtable) dataQueue.Dequeue ();
234
DeserializeData (dataQueue);
238
OldPidbVersionException opvEx = ex as OldPidbVersionException;
240
LoggingService.LogWarning ("PIDB file '{0}' could not be loaded. Expected version {1}, found version {2}'. The file will be recreated.", dataFile, opvEx.ExpectedVersion, opvEx.FoundVersion);
242
LoggingService.LogError ("PIDB file '{0}' could not be loaded: '{1}'. The file will be recreated.", dataFile, ex);
247
timer.Trace ("Notify read comments");
249
// Notify read comments
250
foreach (FileEntry fe in files.Values) {
251
if (! fe.IsAssembly && fe.CommentTasks != null) {
252
ProjectDomService.UpdatedCommentTasks (fe.FileName, fe.CommentTasks, Project);
256
int totalEntries = 0;
257
IEnumerator<ClassEntry> ecls = rootNamespace.GetAllClasses ().GetEnumerator ();
258
while (ecls.MoveNext ())
260
Counters.TypeIndexEntries.Inc (totalEntries);
263
// Read all information from the database to ensure everything is in place
264
HashSet<ClassEntry> classes = new HashSet<ClassEntry> ();
265
foreach (ClassEntry ce in rootNamespace.GetAllClasses ()) {
269
} catch (Exception ex) {
270
LoggingService.LogWarning ("PIDB file verification failed. Class '" + ce.Name + "' could not be deserialized: " + ex.Message);
273
/* foreach (FileEntry fe in files.Values) {
274
foreach (ClassEntry ce in fe.ClassEntries) {
275
if (!classes.Contains (ce))
276
LoggingService.LogWarning ("PIDB file verification failed. Class '" + ce.Name + "' from file '" + fe.FileName + "' not found in main index.");
281
foreach (ClassEntry ce in classes)
282
LoggingService.LogWarning ("PIDB file verification failed. Class '" + ce.Name + "' not found in file index.");
285
timer.Trace ("Notify tag changes");
286
// Update comments if needed...
287
CommentTagSet lastTags = new CommentTagSet (LastValidTaskListTokens);
288
if (!lastTags.Equals (ProjectDomService.SpecialCommentTags))
289
OnSpecialTagsChanged (null, null);
296
FileStream OpenForWrite ()
298
if (tempDataFile != null) {
299
// Already a temp file.
300
return new FileStream (tempDataFile, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
304
return new FileStream (dataFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
306
catch (IOException) {
307
// If the file could not be opened nor created, and the file doesn't exist,
308
// then it must be some write permission issue. Rethrow the exception.
309
if (!File.Exists (dataFile))
313
// The file is locked, so it can't be opened. The solution is to make
314
// a copy of the file and open the copy. The copy will later be discarded,
315
// and this is not a problem because if the main file is locked it means
316
// that it is being updated by another MD instance.
318
tempDataFile = Path.GetTempFileName ();
319
File.Copy (dataFile, tempDataFile, true);
320
return new FileStream (tempDataFile, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
323
protected int ResolveTypes (ICompilationUnit unit, IList<IType> types, out List<IType> result)
325
return ProjectDomService.ResolveTypes (SourceProjectDom, unit, types, out result);
328
private class OldPidbVersionException : Exception
330
public int FoundVersion;
331
public int ExpectedVersion;
333
public OldPidbVersionException (int foundVersion, int expectedVersion)
335
FoundVersion = foundVersion;
336
ExpectedVersion = expectedVersion;
340
public static Hashtable ReadHeaders (string baseDir, string name)
342
string file = Path.Combine (baseDir, name + ".pidb");
343
using (FileStream ifile = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read)) {
344
BinaryFormatter bf = new BinaryFormatter ();
345
Hashtable headers = (Hashtable) bf.Deserialize (ifile);
350
public virtual bool Write ()
354
if (!Modified) return false;
356
ITimeTracker timer = Counters.DatabasesWritten.BeginTiming ("Writing Parser Database " + dataFile);
359
headers["Version"] = FORMAT_VERSION;
360
headers["LastValidTaskListTokens"] = ProjectDomService.SpecialCommentTags.ToString ();
362
LoggingService.LogDebug ("Writing " + dataFile);
365
if (dataFileStream == null) {
366
timer.Trace ("Opening file");
367
dataFileStream = OpenForWrite ();
368
datareader = new BinaryReader (dataFileStream);
370
} catch (Exception ex) {
371
LoggingService.LogError ("Could not write parser database.", ex);
376
MemoryStream tmpStream = new MemoryStream ();
377
BinaryFormatter bf = new BinaryFormatter ();
378
BinaryWriter bw = new BinaryWriter (tmpStream);
381
timer.Trace ("Serializing headers");
383
// The headers are the first thing to write, so they can be read
384
// without deserializing the whole file.
385
bf.Serialize (tmpStream, headers);
387
// The position of the index will be written here
388
long indexOffsetPos = tmpStream.Position;
391
MemoryStream buffer = new MemoryStream ();
392
BinaryWriter bufWriter = new BinaryWriter (buffer);
394
timer.Trace ("Writing class data");
396
INameEncoder nameEncoder = pdb.CreateNameEncoder ();
398
// Write all class data
399
foreach (ClassEntry ce in GetAllClasses ())
406
// Copy the data from the source file
407
dataFileStream.Position = ce.Position;
408
len = datareader.ReadInt32 ();
410
// Sanity check to avoid allocating huge byte arrays if something
411
// goes wrong when reading the file contents
412
if (len > 1024*1024*10 || len < 0)
413
throw new InvalidOperationException ("pidb file corrupted: " + dataFile);
415
data = new byte[len];
418
nr += dataFileStream.Read (data, nr, len - nr);
422
DomPersistence.Write (bufWriter, nameEncoder, c);
424
data = buffer.GetBuffer ();
425
len = (int)buffer.Position;
429
ce.Position = tmpStream.Position;
431
bw.Write (data, 0, len);
436
timer.Trace ("Writing index");
439
long indexOffset = tmpStream.Position;
441
Queue dataQueue = new Queue ();
442
dataQueue.Enqueue (references);
443
dataQueue.Enqueue (rootNamespace);
444
dataQueue.Enqueue (files);
445
dataQueue.Enqueue (unresolvedSubclassTable);
446
SerializeData (dataQueue);
447
bf.Serialize (tmpStream, dataQueue.ToArray ());
449
tmpStream.Position = indexOffsetPos;
450
bw.Write (indexOffset);
454
timer.Trace ("Saving to file");
456
dataFileStream.SetLength (0);
457
dataFileStream.Position = 0;
459
byte[] dataDump = tmpStream.ToArray ();
460
dataFileStream.Write (dataDump, 0, dataDump.Length);
461
} catch (Exception ex) {
462
LoggingService.LogError (ex.ToString ());
463
dataFileStream.Close ();
464
dataFileStream = null;
472
StringNameTable.PrintTop100 ();
477
protected virtual void SerializeData (Queue dataQueue)
481
protected virtual void DeserializeData (Queue dataQueue)
485
internal protected FileEntry GetFile (string name)
487
return files [name] as FileEntry;
490
protected IEnumerable<FileEntry> GetAllFiles ()
492
foreach (FileEntry fe in files.Values)
496
internal IEnumerable<ClassEntry> GetAllClasses ()
498
return rootNamespace.GetAllClasses ();
503
// Saves the database if it has too much information
504
// in memory. A parser database can't have more
505
// MAX_ACTIVE_COUNT classes loaded in memory at the
510
foreach (ClassEntry ce in GetAllClasses ()) {
511
if (ce.Class != null)
515
if (activeCount <= MAX_ACTIVE_COUNT) return;
519
foreach (ClassEntry ce in GetAllClasses ()) {
520
if (ce.Class != null && ce.LastGetTime < currentGetTime - MIN_ACTIVE_COUNT && ce.Saved) {
522
Counters.LiveTypeObjects--;
527
internal IType LoadClass (ClassEntry ce)
530
if (ce.Class != null)
532
dataFileStream.Position = ce.Position;
533
datareader.ReadInt32 ();// Length of data
534
DomType cls = DomPersistence.ReadType (datareader, pdb.CreateNameDecoder ());
535
cls.SourceProjectDom = SourceProjectDom;
538
Counters.LiveTypeObjects++;
543
protected void UnlockDatabaseFile ()
550
if (datareader != null) {
552
dataFileStream.Close ();
553
dataFileStream = null;
561
foreach (ClassEntry ce in GetAllClasses ()) {
563
if (ce.Class != null)
567
Counters.TypeIndexEntries.Dec (tce);
568
Counters.LiveTypeObjects.Dec (tcl);
570
rootNamespace = new NamespaceEntry (null, null);
571
files = new Hashtable ();
572
references = new ArrayList ();
573
headers = new Hashtable ();
574
unresolvedSubclassTable = new Hashtable ();
577
public IType GetClass (string typeName, IList<IReturnType> genericArguments, bool caseSensitive)
579
int genericArgumentCount = ProjectDom.ExtractGenericArgCount (ref typeName);
580
if (genericArguments != null)
581
genericArgumentCount = genericArguments.Count;
584
string[] path = typeName.Split ('.');
585
int len = path.Length - 1;
592
if (GetBestNamespaceEntry (path, len, false, caseSensitive, out nst, out nextPos))
594
ClassEntry ce = nst.GetClass (path[len], genericArgumentCount, caseSensitive);
595
if (ce == null) return null;
596
result = GetClass (ce);
600
// It may be an inner class
601
string nextName = path[nextPos++];
602
int partArgsCount = ProjectDom.ExtractGenericArgCount (ref nextName);
603
ClassEntry ce = nst.GetClass (nextName, partArgsCount, caseSensitive);
604
if (ce == null) return null;
605
result = SourceProjectDom.SearchInnerType (GetClass (ce), path, nextPos, genericArgumentCount, caseSensitive);
607
if (result != null && genericArguments != null && genericArguments.Count > 0)
608
return sourceProjectDom.CreateInstantiatedGenericType (result, genericArguments);
613
internal IType GetClass (ClassEntry ce)
615
ce.LastGetTime = currentGetTime++;
616
if (ce.Class != null)
618
DomTypeProxy result = new DomTypeProxy (this, ce);
619
result.SourceProjectDom = this.SourceProjectDom;
620
result.Resolved = true;
624
public IEnumerable<IType> GetSubclasses (IType btype, IList<string> namespaces)
626
InstantiatedType itype = btype as InstantiatedType;
628
foreach (IType t in GetSubclassesInternal (itype.UninstantiatedType, namespaces)) {
629
IType sub = GetCompatibleSubclass (itype, t);
634
// We don't support getting the subclasses of generic non-instantiated types.
635
if (btype.TypeParameters.Count > 0)
637
foreach (IType t in GetSubclassesInternal (btype, namespaces))
642
IEnumerable<IType> GetSubclassesInternal (IType btype, IList<string> namespaces)
644
ArrayList nsubs = (ArrayList) unresolvedSubclassTable [ParserDatabase.GetDecoratedName (btype)];
645
ArrayList csubs = null;
647
ClassEntry ce = FindClassEntry (btype.FullName, btype.TypeParameters.Count);
649
csubs = ce.Subclasses;
651
foreach (ArrayList subs in new object[] { nsubs, csubs }) {
654
foreach (object ob in subs) {
655
if (ob is ClassEntry) {
656
string ns = ((ClassEntry) ob).NamespaceRef.FullName;
657
if (namespaces == null || namespaces.Contains (ns)) {
658
IType t = GetClass ((ClassEntry)ob);
659
if (t != null && t != btype)
664
// It's a full class name
665
IType cls = this.GetClass ((string)ob, null, true);
666
if (cls != null && (namespaces == null || namespaces.Contains (cls.Namespace))) {
675
IType GetCompatibleSubclass (InstantiatedType baseType, IType subType)
677
// No generic type inferring involved
678
if (subType.TypeParameters.Count == 0 && baseType.GenericParameters.Count == 0)
681
// The subclass is compatible and can be returned if all type parameters
682
// can be inferred from the base class
684
// Find the IReturnType that is relating the subtype with the base type
686
IReturnType baseRetType = null;
687
foreach (IReturnType rt in subType.BaseTypes) {
688
if (rt.FullName == baseType.UninstantiatedType.FullName && rt.GenericArguments.Count == baseType.GenericParameters.Count) {
693
if (baseRetType == null)
694
return null; // Something went wrong. Not compatible.
696
ReadOnlyCollection<IReturnType> bparams = baseRetType.GenericArguments;
697
bool[] paramsMatched = new bool [bparams.Count];
699
List<IReturnType> args = new List<IReturnType> ();
700
foreach (TypeParameter par in subType.TypeParameters) {
702
string parTypeName = subType.FullName + "." + par.Name;
703
for (int n=0; n < bparams.Count; n++) {
704
string pname = bparams [n].FullName;
705
if (parTypeName == pname) {
706
paramsMatched [n] = true;
712
args.Add (baseType.GenericParameters [pos]);
714
return null; // Something went wrong. Not compatible.
717
// Parameter which are instantiated must match the ones in the
718
// instantiated base class
719
for (int n=0; n < bparams.Count; n++) {
720
if (paramsMatched [n])
722
if (!bparams [n].Equals (baseType.GenericParameters [n]))
725
return sourceProjectDom.CreateInstantiatedGenericType (subType, args);
728
void OnSpecialTagsChanged (object sender, EventArgs e)
730
// Update LastValidTagComments
732
string oldTokens = (string) headers["LastValidTagComments"];
733
headers["LastValidTagComments"] = ProjectDomService.SpecialCommentTags.ToString ();
735
CommentTagSet oldTags = new CommentTagSet (oldTokens);
736
foreach (string tag in oldTags.GetNames ()) {
737
// Remove them from FileEntry data
738
if (!ProjectDomService.SpecialCommentTags.ContainsTag (tag))
739
RemoveSpecialCommentTag (tag);
741
QueueAllFilesForParse ();
744
public IList<Tag> GetSpecialComments (string fileName)
748
FileEntry fe = files[fileName] as FileEntry;
749
return fe != null ? fe.CommentTasks : null;
753
public void UpdateTagComments (IList<Tag> tags, string fileName)
757
FileEntry fe = files[fileName] as FileEntry;
759
fe.CommentTasks = tags;
763
void RemoveSpecialCommentTag (string token)
765
foreach (FileEntry fe in files.Values)
767
if (fe.CommentTasks != null) {
768
List<Tag> markedTags = new List<Tag> ();
769
foreach (Tag tag in fe.CommentTasks)
770
if (tag.Key == token) markedTags.Add (tag);
771
foreach (Tag tag in markedTags)
772
fe.CommentTasks.Remove (tag);
773
ProjectDomService.UpdatedCommentTasks (fe.FileName, fe.CommentTasks, Project);
778
string LastValidTaskListTokens
782
return (string)headers["LastValidTaskListTokens"];
786
public virtual void CheckModifiedFiles ()
788
ArrayList list = GetModifiedFileEntries ();
789
foreach (FileEntry file in list)
790
QueueParseJob (file);
793
protected ArrayList GetModifiedFileEntries ()
795
ArrayList list = new ArrayList ();
798
foreach (FileEntry file in files.Values) {
799
if (IsFileModified (file)) {
807
protected virtual bool IsFileModified (FileEntry file)
809
return file.IsModified;
812
protected void QueueParseJob (FileEntry file)
814
if (file.InParseQueue)
816
file.InParseQueue = true;
817
ProjectDomService.QueueParseJob (SourceProjectDom, new JobCallback (ParseCallback), file.FileName);
820
protected void QueueAllFilesForParse ()
824
foreach (FileEntry file in files.Values)
825
file.LastParseTime = DateTime.MinValue;
827
CheckModifiedFiles ();
830
void ParseCallback (object ob, IProgressMonitor monitor)
832
string fileName = (string) ob;
833
ParseFile (fileName, monitor);
835
FileEntry file = GetFile (fileName);
837
file.InParseQueue = false;
838
FileInfo fi = new FileInfo (fileName);
839
file.LastParseTime = fi.LastWriteTime;
844
protected virtual void ParseFile (string fileName, IProgressMonitor monitor)
848
public void ParseAll ()
852
foreach (FileEntry fe in files.Values) {
853
ParseFile (fe.FileName, null);
855
FileInfo fi = new FileInfo (fe.FileName);
856
fe.LastParseTime = fi.LastWriteTime;
864
protected void AddReference (string uri)
868
// Create a new list because the reference list is accessible through a public property
869
ReferenceEntry re = new ReferenceEntry (uri);
870
ArrayList list = (ArrayList) references.Clone ();
877
protected void RemoveReference (string uri)
881
for (int n=0; n<references.Count; n++)
883
if (((ReferenceEntry)references[n]).Uri == uri) {
884
ArrayList list = (ArrayList) references.Clone ();
894
protected bool HasReference (string uri)
896
for (int n=0; n<references.Count; n++) {
897
ReferenceEntry re = (ReferenceEntry) references[n];
904
public FileEntry AddFile (string fileName)
908
FileEntry fe = new FileEntry (fileName);
909
files [fileName] = fe;
915
public void RemoveFile (string fileName)
919
TypeUpdateInformation classInfo = new TypeUpdateInformation ();
921
FileEntry fe = files [fileName] as FileEntry;
922
if (fe == null) return;
926
foreach (ClassEntry ce in fe.ClassEntries) {
929
IType c = CompoundType.RemoveFile (ce.Class, fileName);
931
classInfo.Removed.Add (ce.Class);
932
RemoveSubclassReferences (ce);
933
UnresolveSubclasses (ce);
934
ce.NamespaceRef.Remove (ce);
940
Counters.LiveTypeObjects.Dec (tc);
941
Counters.TypeIndexEntries.Dec (te);
943
files.Remove (fileName);
946
OnFileRemoved (fileName, classInfo);
950
protected virtual void OnFileRemoved (string fileName, TypeUpdateInformation classInfo)
954
public TypeUpdateInformation UpdateTypeInformation (IList<IType> newClasses, string fileName)
958
TypeUpdateInformation res = new TypeUpdateInformation ();
960
FileEntry fe = files [fileName] as FileEntry;
965
// Get the namespace entry for each class
967
bool[] added = new bool [newClasses.Count];
968
NamespaceEntry[] newNss = new NamespaceEntry [newClasses.Count];
969
for (int n = 0; n < newClasses.Count; n++) {
970
string[] path = newClasses[n].Namespace.Split ('.');
971
((DomType)newClasses[n]).SourceProjectDom = sourceProjectDom;
972
newNss[n] = GetNamespaceEntry (path, path.Length, true, true);
975
ArrayList newFileClasses = new ArrayList ();
979
foreach (ClassEntry ce in fe.ClassEntries)
981
IType newClass = null;
982
for (int n=0; n<newClasses.Count && newClass == null; n++) {
983
IType uc = newClasses [n];
984
if (uc.Name == ce.Name && uc.TypeParameters.Count == ce.TypeParameterCount && newNss[n] == ce.NamespaceRef) {
985
if (newClass == null)
988
newClass = CompoundType.Merge (newClass, uc);
993
if (newClass != null) {
994
// Class already in the database, update it
996
RemoveSubclassReferences (ce);
998
IType tp = CompoundType.RemoveFile (ce.Class, fileName);
1000
ce.Class = CompoundType.Merge (tp, CopyClass (newClass));
1002
ce.Class = CopyClass (newClass);
1003
AddSubclassReferences (ce);
1005
ce.LastGetTime = currentGetTime++;
1006
newFileClasses.Add (ce);
1007
res.Modified.Add (ce.Class);
1008
SourceProjectDom.ResetInstantiatedTypes (ce.Class);
1010
// Database class not found in the new class list, it has to be deleted
1011
IType c = LoadClass (ce);
1012
IType removed = CompoundType.RemoveFile (c, fileName);
1013
if (removed != null) {
1014
// It's still a compound class
1016
AddSubclassReferences (ce);
1017
res.Modified.Add (removed);
1019
// It's not a compoudnd class. Remove it.
1020
Counters.LiveTypeObjects--;
1021
Counters.TypeIndexEntries--;
1022
RemoveSubclassReferences (ce);
1023
UnresolveSubclasses (ce);
1024
res.Removed.Add (c);
1025
ce.NamespaceRef.Remove (ce);
1027
SourceProjectDom.ResetInstantiatedTypes (c);
1033
fe = new FileEntry (fileName);
1034
files [fileName] = fe;
1037
for (int n=0; n<newClasses.Count; n++) {
1039
IType c = CopyClass (newClasses[n]);
1041
// A ClassEntry may already exist if part of the class is defined in another file
1042
ClassEntry ce = newNss[n].GetClass (c.Name, c.TypeParameters.Count , true);
1044
// The entry exists, just update it
1046
RemoveSubclassReferences (ce);
1047
ce.Class = CompoundType.Merge (ce.Class, c);
1048
res.Modified.Add (ce.Class);
1051
ce = new ClassEntry (c, newNss[n]);
1054
ResolveSubclasses (ce);
1055
Counters.LiveTypeObjects++;
1056
Counters.TypeIndexEntries++;
1058
AddSubclassReferences (ce);
1059
newFileClasses.Add (ce);
1060
ce.LastGetTime = currentGetTime++;
1064
fe.SetClasses (newFileClasses);
1065
rootNamespace.Clean ();
1067
FileInfo fi = new FileInfo (fe.FileName);
1068
fe.LastParseTime = fi.LastWriteTime;
1070
fe.LastParseTime = DateTime.Now;
1079
void ResolveSubclasses (ClassEntry ce)
1081
// If this type is registered in the unresolved subclass table, now those subclasses
1082
// can properly be assigned.
1083
string name = ParserDatabase.GetDecoratedName (ce);
1084
ArrayList subs = (ArrayList) unresolvedSubclassTable [name];
1086
ce.Subclasses = subs;
1087
unresolvedSubclassTable.Remove (name);
1091
void UnresolveSubclasses (ClassEntry ce)
1093
// Called when a ClassEntry is removed. If there are registered subclass, add them
1094
// to the unresolved subclass table
1095
if (ce.Subclasses != null)
1096
unresolvedSubclassTable [ParserDatabase.GetDecoratedName (ce)] = ce.Subclasses;
1099
IEnumerable<IReturnType> GetAllBaseTypes (IType type)
1101
if (type.BaseType != null)
1102
yield return type.BaseType;
1103
foreach (IReturnType rt in type.ImplementedInterfaces)
1107
bool IsValidGenericSubclass (IType subType, IReturnType baseType)
1109
// Subclass relations between generic types are only useful if we can infer
1110
// an instantiated subtype from a given instantiated base type
1111
// Examples of valid subclasses:
1112
// class Sub<T>: Base<T> { }
1113
// class Sub<T1,T2>: Base<T1,T2> { }
1114
// class Sub<T>: Base<T,T> { }
1115
// class Sub<T>: Base<T,string> { }
1116
// class Sub<A,B>: Base<B,A> { }
1117
// Examples of invalid subclasses:
1118
// class Sub<T>: Base { }
1119
// class Sub<T,S>: Base<T> { }
1120
// class Sub<T1,T2>: Base<T1, string> { }
1121
// class Sub<T>: Base<string> { }
1123
if (baseType.GenericArguments.Count > 0) {
1124
if (subType.TypeParameters.Count == 0)
1126
if (subType.TypeParameters.Count > baseType.GenericArguments.Count)
1128
List<string> pars = new List<string> ();
1129
foreach (TypeParameter tpar in subType.TypeParameters)
1130
pars.Add (subType.FullName + "." + tpar.Name);
1131
foreach (IReturnType rt in baseType.GenericArguments)
1132
pars.Remove (rt.FullName);
1135
} else if (subType.TypeParameters.Count != 0)
1140
void AddSubclassReferences (ClassEntry ce)
1142
foreach (IReturnType type in GetAllBaseTypes (ce.Class)) {
1143
string bt = ParserDatabase.GetDecoratedName (type);
1144
if (bt == "System.Object")
1146
if (!IsValidGenericSubclass (ce.Class, type))
1148
ClassEntry sup = FindClassEntry (type.FullName, type.GenericArguments.Count);
1150
sup.RegisterSubclass (ce);
1152
ArrayList subs = (ArrayList) unresolvedSubclassTable [bt];
1154
subs = new ArrayList ();
1155
unresolvedSubclassTable [bt] = subs;
1160
foreach (IType cls in ce.Class.InnerTypes)
1161
AddInnerSubclassReferences (cls);
1164
void AddInnerSubclassReferences (IType cls)
1166
foreach (IReturnType type in GetAllBaseTypes (cls)) {
1167
string bt = ParserDatabase.GetDecoratedName (type);
1168
if (bt == "System.Object")
1170
if (!IsValidGenericSubclass (cls, type))
1172
ArrayList subs = (ArrayList) unresolvedSubclassTable [bt];
1174
subs = new ArrayList ();
1175
unresolvedSubclassTable [bt] = subs;
1177
subs.Add (ParserDatabase.GetDecoratedName (cls));
1179
foreach (IType ic in cls.InnerTypes)
1180
AddInnerSubclassReferences (ic);
1183
void RemoveSubclassReferences (ClassEntry ce)
1185
foreach (IReturnType type in GetAllBaseTypes (ce.Class)) {
1186
ClassEntry sup = FindClassEntry (type.FullName, type.GenericArguments.Count);
1188
sup.UnregisterSubclass (ce);
1190
ArrayList subs = (ArrayList) unresolvedSubclassTable [ParserDatabase.GetDecoratedName (type)];
1193
if (subs.Count == 0)
1194
unresolvedSubclassTable.Remove (ParserDatabase.GetDecoratedName (type));
1197
foreach (IType cls in ce.Class.InnerTypes)
1198
RemoveInnerSubclassReferences (cls);
1201
void RemoveInnerSubclassReferences (IType cls)
1203
foreach (IReturnType type in GetAllBaseTypes (cls)) {
1204
ArrayList subs = (ArrayList) unresolvedSubclassTable [ParserDatabase.GetDecoratedName (type)];
1206
subs.Remove (ParserDatabase.GetDecoratedName (cls));
1208
foreach (IType ic in cls.InnerTypes)
1209
RemoveInnerSubclassReferences (ic);
1212
ClassEntry FindClassEntry (string fullName, int genericArgumentCount)
1214
string[] path = fullName.Split ('.');
1215
int len = path.Length - 1;
1219
if (GetBestNamespaceEntry (path, len, false, true, out nst, out nextPos))
1221
ClassEntry ce = nst.GetClass (path[len], genericArgumentCount, true);
1222
if (ce == null) return null;
1228
public void GetNamespaceContents (List<IMember> list, string subNameSpace, bool caseSensitive)
1231
string[] path = subNameSpace.Split ('.');
1232
NamespaceEntry tns = GetNamespaceEntry (path, path.Length, false, caseSensitive);
1233
if (tns == null) return;
1235
foreach (DictionaryEntry en in tns.Contents) {
1236
if (en.Value is NamespaceEntry) {
1237
list.Add (new Namespace ((string)en.Key));
1239
IType type = GetClass ((ClassEntry)en.Value);
1241
if (type.Name.IndexOfAny (new char[] { '<', '>' }) >= 0)
1249
public void GetClassList (ArrayList list, string subNameSpace, bool caseSensitive)
1253
string[] path = subNameSpace.Split ('.');
1254
NamespaceEntry tns = GetNamespaceEntry (path, path.Length, false, caseSensitive);
1255
if (tns == null) return;
1257
foreach (DictionaryEntry en in tns.Contents) {
1258
if (en.Value is ClassEntry && !list.Contains (en.Key))
1264
public IType[] GetClassList ()
1268
ArrayList list = new ArrayList ();
1269
foreach (ClassEntry ce in GetAllClasses ()) {
1270
list.Add (GetClass (ce));
1272
return (IType[]) list.ToArray (typeof(IType));
1276
public IEnumerable<IType> GetClassList (bool includeInner, IList<string> namespaces)
1280
ArrayList list = new ArrayList ();
1281
foreach (ClassEntry ce in GetAllClasses ()) {
1282
IType cls = GetClass (ce);
1283
if (namespaces != null && !namespaces.Contains (cls.Namespace))
1286
if (includeInner && ((ce.ContentFlags & ContentFlags.HasInnerClasses) != 0))
1287
GetAllInnerClassesRec (list, cls);
1289
return (IType[]) list.ToArray (typeof(IType));
1293
void GetAllInnerClassesRec (ArrayList list, IType cls)
1295
foreach (IType ic in cls.InnerTypes) {
1297
GetAllInnerClassesRec (list, ic);
1301
public void GetNamespaceList (ArrayList list, string subNameSpace, bool caseSensitive)
1305
string[] path = subNameSpace.Split ('.');
1306
NamespaceEntry tns = GetNamespaceEntry (path, path.Length, false, caseSensitive);
1307
if (tns == null) return;
1309
foreach (DictionaryEntry en in tns.Contents) {
1310
if (en.Value is NamespaceEntry && !list.Contains (en.Key))
1316
public bool NamespaceExists (string name, bool caseSensitive)
1320
string[] path = name.Split ('.');
1321
NamespaceEntry tns = GetNamespaceEntry (path, path.Length, false, caseSensitive);
1326
public ICollection References
1328
get { return references; }
1331
public IType[] GetFileContents (string fileName)
1333
FileEntry fe = GetFile (fileName);
1334
if (fe == null) return new IType [0];
1336
ArrayList classes = new ArrayList ();
1337
foreach (ClassEntry ce in fe.ClassEntries) {
1338
classes.Add (GetClass (ce));
1340
return (IType[]) classes.ToArray (typeof(IType));
1343
IType CopyClass (IType cls)
1345
CopyDomVisitor<object> copier = new CopyDomVisitor<object> ();
1346
return (IType) copier.Visit (cls, null);
1349
bool GetBestNamespaceEntry (string[] path, int length, bool createPath, bool caseSensitive, out NamespaceEntry lastEntry, out int numMatched)
1351
lastEntry = rootNamespace;
1353
if (length == 0 || (length == 1 && path[0] == "")) {
1354
numMatched = length;
1359
for (int n=0; n<length; n++) {
1360
NamespaceEntry nh = lastEntry.GetNamespace (path[n], caseSensitive);
1367
nh = new NamespaceEntry (lastEntry, path[n]);
1372
numMatched = length;
1377
NamespaceEntry GetNamespaceEntry (string[] path, int length, bool createPath, bool caseSensitive)
1382
if (GetBestNamespaceEntry (path, length, createPath, caseSensitive, out nst, out matched))
1388
public void RunWithLock<T> (Action<T> act, T data)
1398
namespace MonoDevelop.Projects.Dom
1400
public interface INameEncoder
1402
int GetStringId (string text, out bool isNew);
1406
public interface INameDecoder
1408
string GetStringValue (int id);
1409
void RegisterString (int id, string str);
1413
public class StringNameTable: INameEncoder, INameDecoder
1416
Dictionary<string,int> stringToId = new Dictionary<string, int> ();
1417
Dictionary<int,string> idToString = new Dictionary<int, string> ();
1420
public StringNameTable (string[] names)
1426
public void Reset ()
1428
stringToId.Clear ();
1429
idToString.Clear ();
1430
ci = table.Length + 1;
1433
public void RegisterString (int id, string str)
1435
idToString [id] = str;
1438
public string GetStringValue (int id)
1440
if (id > table.Length) {
1442
if (idToString.TryGetValue (id, out res))
1446
if (id < 0 || id >= table.Length)
1447
return "Invalid id:" + id;
1451
public int GetStringId (string text, out bool isNew)
1455
object ob = all [text];
1457
all [text] = ((int)ob) + 1;
1462
int i = Array.BinarySearch (table, text);
1463
if (i >= 0) return i;
1465
if (stringToId.TryGetValue (text, out i))
1469
stringToId.Add (text, ++ci);
1474
static Hashtable all = new Hashtable ();
1477
public static void PrintTop100 ()
1479
string[] ss = new string [all.Count];
1480
int[] nn = new int [all.Count];
1482
foreach (DictionaryEntry e in all) {
1483
ss [n] = (string) e.Key;
1484
nn [n] = (int) e.Value;
1487
Array.Sort (nn, ss);
1489
Console.WriteLine ("{0} total strings", count);
1490
Console.WriteLine ("{0} unique strings", nn.Length);
1491
for (int i = nn.Length - 1; i > nn.Length - 101 && i >= 0; i--) {
1492
Console.WriteLine ("\"{1}\", // {2}", n, ss[i], nn[i]);