/// <summary> /// Add public types from the given source PackageDatabase to this one. /// Returns an array indicating which items were added. True means the /// member or a descendant were unique. False means nothing was new. /// Null means the type and descendants were excluded as non-public. /// </summary> /// <param name="source">PackageDatabase to add from</param> /// <returns>For each member, true if added, false if not unique, null if not a public type</returns> public DatabaseAddResult Add(PackageDatabase source, ArdbVersion ardbVersion) { // Merge namespaces into a common tree to find unique parts of this package DatabaseAddResult result = new DatabaseAddResult(source); AddUniqueMembers(source, 0, 0, result, ardbVersion); return(result); }
public AddReferenceDatabase(ArdbVersion ardbVersion) { this.DatabaseFormatVersion = ardbVersion; this.StringStore = new StringStore(); this.DeclaredMembers = new ItemTree(); this.DeclaredMemberTypes = new List <SymbolType>(); this.DeclaredMemberTypes.Add(SymbolType.Package); this.Index = new MemberIndex(); _mergedMembers = new MergedMembersDatabase(this.StringStore); }
private void VerifyQueryResults(AddReferenceDatabase ardb, ArdbVersion version) { // "Diagnostics." MemberQuery query = new MemberQuery(PackageDatabaseTests.NS_DIAGNOSTICS + ".", false, false); PartialArray <Symbol> results = new PartialArray <Symbol>(10); query.TryFindMembers(ardb, ref results); Assert.AreEqual(3, results.Count); Assert.AreEqual("Logger, Memory, TraceWatch", PackageDatabaseTests.ResultNamesToString(results)); if (version == ArdbVersion.V1) { // V1 has no TFM data. This call also verifies that most current client // can query older format without raising an exception. Assert.AreEqual(String8.Empty, ardb.GetFrameworkTargets(results[0].Index)); return; } for (int i = 0; i < results.Count; i++) { Symbol symbol = results[i]; string fx = ardb.GetFrameworkTargets(symbol.Index).ToString(); if (symbol.Name.ToString() == "TraceWatch") { Assert.AreEqual(PackageDatabaseTests.NET20, fx); } else if (symbol.Name.ToString() == "Memory") { Assert.AreEqual(PackageDatabaseTests.NET35, fx); } else { Assert.AreEqual(@"<tfms><tfm>net20</tfm><tfm>net35</tfm></tfms>", fx); } } }
private bool AddUniqueMembers( PackageDatabase source, int sourceMemberIndex, int targetMemberIndex, DatabaseAddResult result, ArdbVersion version) { int pathLength = result.CurrentPath.Length; SymbolDetails memberDetails = source.DeclaredMemberDetails[sourceMemberIndex]; bool hasUniqueDescendants = false; int indexToAddChildrenUnder = targetMemberIndex; // Add public types and namespaces which contain either bool addMember = false; if (memberDetails.Type == SymbolType.FrameworkTarget) { String8 encodedFrameworkNames = source.StringStore[source.DeclaredMembers.GetNameIdentifier(sourceMemberIndex)]; result.CurrentFrameworkNames = new HashSet <string>(encodedFrameworkNames.ToString().ToFrameworkNames()); } else if (memberDetails.Type == SymbolType.Namespace) { addMember = ContainsPublics(source, sourceMemberIndex); } else if (memberDetails.Type == SymbolType.Assembly) { if (result.CurrentFrameworkNames != null) { result.CurrentFrameworkNames.Clear(); } } else if (memberDetails.Type.IsType()) { addMember = memberDetails.Modifiers.HasFlag(SymbolModifier.Public); } // Add the member if it is a public type or contains them [and set 'indexInTarget' to the added member] if (addMember) { result.PublicTypeCount++; // Remap name for target StringStore [all unique names will be added anyway] int memberName = source.DeclaredMembers.GetNameIdentifier(sourceMemberIndex); String8 memberNameText = source.StringStore[memberName]; result.CurrentPath.Append((result.CurrentPath.Length > 0 ? "." : "") + memberNameText.ToString()); memberName = this.StringStore.FindOrAddString(memberNameText); string fullTypeName; HashSet <string> frameworkTargets; // See if this name is already found in the merged namespace tree if (!this.MergedMembers.TryFindChildByName(targetMemberIndex, memberName, out indexToAddChildrenUnder)) { // If not, add it, and the tree is unique because this name is indexToAddChildrenUnder = this.MergedMembers.Add(targetMemberIndex, memberName); // Identify the package which added it int packageNameIdentifier = this.StringStore.FindOrAddString(source.Identity.PackageName); this.MergedMemberSourcePackageIdentifier.Add(packageNameIdentifier); this.MergedMemberDuplicateCount.Add(1); hasUniqueDescendants = true; result.MergedTypeCount++; if (version != ArdbVersion.V1 && memberDetails.Type.IsType()) { // We have encountered a fully-qualified type name for the // very first time. We will associate this name with the // current framework target. We will also remember this // sourceMemberIndex, in order to use it as the item // to add when populating the ARDB. fullTypeName = result.CurrentPath.ToString(); result.TypeToFrameworkTargetsMap.Add(fullTypeName, new HashSet <string>()); if (result.CurrentFrameworkNames != null) { result.TypeToFrameworkTargetsMap[fullTypeName].UnionWith(result.CurrentFrameworkNames); } result.TypeNameToTypeIndexMap.Add(fullTypeName, sourceMemberIndex); } } else { // Otherwise, if this is a duplicate with another entry in the same package, still include it (different framework targets) String8 sourcePackage = this.StringStore[this.MergedMemberSourcePackageIdentifier[indexToAddChildrenUnder]]; if (version != ArdbVersion.V1 && result.CurrentFrameworkNames != null && sourcePackage.Equals(source.Identity.PackageName)) { hasUniqueDescendants = true; result.MergedTypeCount++; if (version != ArdbVersion.V1 && memberDetails.Type.IsType()) { // We have encountered a fully-qualified type name that we've seen // before. We will record the current framework target but // will already have a source member index to use to add // to the ARDB later. fullTypeName = result.CurrentPath.ToString(); frameworkTargets = result.TypeToFrameworkTargetsMap[fullTypeName]; frameworkTargets.UnionWith(result.CurrentFrameworkNames); Debug.Assert(result.TypeNameToTypeIndexMap.ContainsKey(fullTypeName)); } } else { // If this isn't unique, increment the count of copies this.MergedMemberDuplicateCount[indexToAddChildrenUnder] += 1; } } } if (!memberDetails.Type.IsType()) { int childIndex = source.DeclaredMembers.GetFirstChild(sourceMemberIndex); while (childIndex > 0) { hasUniqueDescendants |= AddUniqueMembers(source, childIndex, indexToAddChildrenUnder, result, version); childIndex = source.DeclaredMembers.GetNextSibling(childIndex); } } // Add the package which had this member to results if it wasn't unique if (hasUniqueDescendants == false) { result.SourcePackageNames[sourceMemberIndex] = this.StringStore[this.MergedMemberSourcePackageIdentifier[indexToAddChildrenUnder]]; } // Record whether this member was unique (and added) result.WasMemberAdded[sourceMemberIndex] = hasUniqueDescendants; result.CurrentPath.Length = pathLength; return(hasUniqueDescendants); }
private void ImportPublics( PackageDatabase source, int sourceMemberIndex, int targetMemberIndex, bool?[] uniqueMembers, ArdbVersion ardbVersion) { SymbolDetails memberDetails = source.DeclaredMemberDetails[sourceMemberIndex]; // Skip adding any subtrees which are not unique if (uniqueMembers != null) { bool?wasUnique = uniqueMembers[sourceMemberIndex]; if (wasUnique == null) { // Not added because not a public type (or ancestor wasn't) return; } else if (wasUnique == false) { // Not added because a non-unique item was detected for a package db // that is not permitted to add duplicates (only MS frameworks pkgs should). return; } } int indexInTarget = targetMemberIndex; // Index everything except the sentinel root. Also skip assembly nodes, // as matches will occur at the package node (which will actually // represent either a package or a reference assembly). if (sourceMemberIndex > 0 && (ardbVersion != ArdbVersion.V1 || memberDetails.Type != SymbolType.FrameworkTarget)) { // Remap name for target StringStore int memberName = source.DeclaredMembers.GetNameIdentifier(sourceMemberIndex); memberName = this.StringStore.FindOrAddString(source.StringStore[memberName]); // Add this member if it doesn't already exist [ex: same namespaces in multiple binaries in Package] if (!this.DeclaredMembers.TryFindChildByName(targetMemberIndex, memberName, out indexInTarget)) { // Add member to target tree indexInTarget = this.DeclaredMembers.Add(targetMemberIndex, memberName); this.DeclaredMemberTypes.Add(memberDetails.Type); // Validate DeclaredMembers and DeclaredMemberTypes are in sync if (this.DeclaredMemberTypes.Count != this.DeclaredMembers.Count) { throw new InvalidOperationException(String.Format(Resources.DatabaseArraysOutOfSync, "DeclaredMemberTypes")); } // Add member to index [types and namespaces so 'find second-to-last value' search works] this.Index.AddItem(memberName, indexInTarget); if (memberDetails.Type == SymbolType.Package) { int addedNode; // If this package is a pre-release version, we will introduce // a new node that holds the specific release details. This is // a short-term fix required to allow a fixer to request installation // of a pre-release only package. Nuget will add a new API in the future // that prevents the need for this node (which we can then remove). if (source.Identity.ReleaseName.IsPrereleaseVersion()) { int packageReleaseId = this.StringStore.FindOrAddString(source.Identity.ReleaseName); addedNode = this.DeclaredMembers.Add(indexInTarget, packageReleaseId); if (ardbVersion == ArdbVersion.V1) { // For V1, we injected the prerelease node as a parent to all // following nodes. For post-V1, this node is a sibling with // popularity rank. indexInTarget = addedNode; } this.DeclaredMemberTypes.Add(SymbolType.Version); } // Inject popularity rank as an sibling node of package contents int popularityRank = source.GetPopularityRank(sourceMemberIndex); int popularityRankId = this.StringStore.FindOrAddString(popularityRank.ToString()); addedNode = this.DeclaredMembers.Add(indexInTarget, popularityRankId); if (ardbVersion == ArdbVersion.V1) { // For V1, we injected the popularity rank directly underneath the // package or pre-release node. Post-V1, this node is a sibling of // the target frameworks node. // popularity rank. indexInTarget = addedNode; } this.DeclaredMemberTypes.Add(SymbolType.PopularityRank); } } else if (ardbVersion == ArdbVersion.V1) { if (memberDetails.Type == SymbolType.Package) { // For V1, we need to skip injected popularity node and prerelease nodes // when looking for duplicate children. This isn't required for post-V1, as // these nodes are introduced as siblings of the TFM and/or assembly-level data. indexInTarget = this.DeclaredMembers.GetFirstChild(indexInTarget); SymbolType symbolType = this.GetMemberType(indexInTarget); if (symbolType == SymbolType.Version) { indexInTarget = this.DeclaredMembers.GetFirstChild(indexInTarget); } // At this point, we should always have the popularity rank in hand Debug.Assert(this.GetMemberType(indexInTarget) == SymbolType.PopularityRank); } else if (memberDetails.Type == SymbolType.FrameworkTarget) { // If we find a TFM node, we will skip past it; indexInTarget = this.DeclaredMembers.GetFirstChild(indexInTarget); } } } // Recurse on children down to type level. We do not walk type members. // This means that we will exclude public nested types from the database. if (!memberDetails.Type.IsType()) { int childIndex = source.DeclaredMembers.GetFirstChild(sourceMemberIndex); while (childIndex > 0) { ImportPublics(source, childIndex, indexInTarget, uniqueMembers, ardbVersion); childIndex = source.DeclaredMembers.GetNextSibling(childIndex); } } }
private void AddReferenceDatabaseBasicHelper(ArdbVersion version) { AddReferenceDatabase ardb = new AddReferenceDatabase(version); DatabaseAddResult result; // Build and add the sample PackageDatabase PackageDatabase source = PackageDatabaseTests.BuildDefaultSample("V1"); result = CallAddUniqueMembers(ardb, source); // Verify at least something was added int ardbCountFirstAdd = ardb.Count; Assert.IsTrue(result.WasMemberAdded[0].Value); // Add the sample again; verify nothing was added source = PackageDatabaseTests.BuildDefaultSample("V2"); result = CallAddUniqueMembers(ardb, source); Assert.IsFalse(result.WasMemberAdded[0].Value); Assert.AreEqual(ardbCountFirstAdd, ardb.Count); // Add a namespace with a private class; verify nothing added source = PackageDatabaseTests.BuildDefaultSample("V3"); MutableSymbol diagnostics = source.MutableRoot.FindByFullName(BuildDiagnosticsNamespaceFor(source.Identity.PackageName), PackageDatabaseTests.SEPARATOR_CHAR); MutableSymbol internalNs = diagnostics.AddChild(new MutableSymbol("Internal", SymbolType.Namespace)); internalNs.AddChild(new MutableSymbol("Tracer", SymbolType.Class) { Modifiers = SymbolModifier.Internal }); result = CallAddUniqueMembers(ardb, source); Assert.IsFalse(result.WasMemberAdded[0].Value); Assert.AreEqual(ardbCountFirstAdd, ardb.Count); // Add a new public class (existing namespace); verify it is added source = PackageDatabaseTests.BuildDefaultSample("V4"); diagnostics = source.MutableRoot.FindByFullName(BuildDiagnosticsNamespaceFor(source.Identity.PackageName), PackageDatabaseTests.SEPARATOR_CHAR); diagnostics.AddChild(new MutableSymbol("TraceWatch", SymbolType.Class) { Modifiers = SymbolModifier.Public | SymbolModifier.Static }); result = CallAddUniqueMembers(ardb, source); Assert.IsTrue(result.WasMemberAdded[0].Value); Assert.IsTrue(result.WasMemberAdded[result.WasMemberAdded.Length - 1].Value); Assert.AreNotEqual(ardbCountFirstAdd, ardb.Count); // Verify a query [expect Diagnostics. to match Logger, Memory, and TraceWatch ardb.ConvertToImmutable(); VerifyQueryResults(ardb, version); // Double-convert ARDB. Verify queries still work correctly. ardb.ConvertToImmutable(); VerifyQueryResults(ardb, version); // Round trip to string; verify query still right, count matches string sampleArdbFilePath = "Sample.ardb.txt"; Write.ToFile(ardb.WriteText, sampleArdbFilePath); AddReferenceDatabase reloaded = new AddReferenceDatabase(version); Read.FromFile(reloaded.ReadText, sampleArdbFilePath); VerifyQueryResults(reloaded, version); Assert.AreEqual(ardb.Count, reloaded.Count); string sampleRewriteArdbFilePath = "Sample.Rewrite.ardb.txt"; Write.ToFile(reloaded.WriteText, sampleRewriteArdbFilePath); Assert.AreEqual(File.ReadAllText(sampleArdbFilePath), File.ReadAllText(sampleRewriteArdbFilePath)); }