diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs index a2e5fbc733e..e3789d3d547 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs @@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis; using System.Reflection.Metadata; using System.Reflection; using System.Reflection.Metadata.Ecma335; +using System.IO; namespace Semmle.Extraction.CIL.Entities { @@ -38,6 +39,16 @@ namespace Semmle.Extraction.CIL.Entities public IId Id => ShortId + IdSuffix; + public void WriteId(TextWriter trapFile) + { + trapFile.WriteIId(Id); + } + + public void WriteQuotedId(TextWriter trapFile) + { + WriteId(trapFile); + } + public Id IdSuffix => fieldSuffix; static readonly StringId fieldSuffix = new StringId(";cil-field"); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs index 3b840f10ba5..90311af8762 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Reflection; using System.Linq; using System.Reflection.Metadata.Ecma335; +using System.IO; namespace Semmle.Extraction.CIL.Entities { diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs index 0644d668fa1..375b2a5cbb8 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs @@ -6,6 +6,8 @@ using System.Linq; using System.Collections.Generic; using System.Reflection; using Semmle.Util; +using System.IO; +using System.Text; namespace Semmle.Extraction.CIL.Entities { @@ -55,6 +57,22 @@ namespace Semmle.Extraction.CIL.Entities public virtual IId Id { get { return ShortId + IdSuffix; } } + public void WriteId(System.IO.TextWriter trapFile) + { + // TODO: Specialise this + trapFile.WriteIId(Id); + } + + /// + /// For debugging purposes. + /// + string DebugId => this.GetDebugLabel(); + + public void WriteQuotedId(TextWriter trapFile) + { + WriteId(trapFile); + } + public Id ShortId { get; set; } public abstract Id IdSuffix { get; } diff --git a/csharp/extractor/Semmle.Extraction.CIL/ExtractionProduct.cs b/csharp/extractor/Semmle.Extraction.CIL/ExtractionProduct.cs index 8da16a22418..1f660e3ee83 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/ExtractionProduct.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/ExtractionProduct.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; namespace Semmle.Extraction.CIL { @@ -45,6 +46,16 @@ namespace Semmle.Extraction.CIL public abstract IEnumerable Contents { get; } public Label Label { get; set; } + public void WriteId(System.IO.TextWriter trapFile) + { + trapFile.Write('*'); + } + + public void WriteQuotedId(TextWriter trapFile) + { + WriteId(trapFile); + } + public Microsoft.CodeAnalysis.Location ReportingLocation => throw new NotImplementedException(); public virtual IId Id => FreshId.Instance; @@ -79,6 +90,16 @@ namespace Semmle.Extraction.CIL public abstract Id IdSuffix { get; } public IId Id => ShortId + IdSuffix; + public void WriteId(System.IO.TextWriter trapFile) + { + trapFile.WriteIId(Id); + } + + public void WriteQuotedId(TextWriter trapFile) + { + WriteId(trapFile); + } + public void Extract(Context cx2) { cx2.Populate(this); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Factories.cs b/csharp/extractor/Semmle.Extraction.CIL/Factories.cs index b8909f2b833..9448f789131 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Factories.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Factories.cs @@ -23,7 +23,8 @@ namespace Semmle.Extraction.CIL } else { - cx.DefineLabel(e); + e.Label = cx.GetNewLabel(); + cx.DefineLabel(e, cx.TrapWriter.Writer); ids.Add(id, (e.Label, id)); cx.PopulateLater(() => { diff --git a/csharp/extractor/Semmle.Extraction.CIL/Id.cs b/csharp/extractor/Semmle.Extraction.CIL/Id.cs index c8cbb04b3d5..616ca406dfd 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Id.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Id.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using System.Reflection.Metadata; namespace Semmle.Extraction.CIL @@ -9,14 +10,14 @@ namespace Semmle.Extraction.CIL /// public abstract class Id : IId { - public void AppendTo(ITrapBuilder tb) + public void AppendTo(System.IO.TextWriter tb) { - tb.Append("@\""); + tb.Write("@\""); BuildParts(tb); - tb.Append("\""); + tb.Write("\""); } - public abstract void BuildParts(ITrapBuilder tb); + public abstract void BuildParts(System.IO.TextWriter tb); public static Id operator +(Id l1, Id l2) => Create(l1, l2); @@ -76,7 +77,7 @@ namespace Semmle.Extraction.CIL hash = unchecked(12 + 3 * (left.GetHashCode() + 51 * right.GetHashCode())); } - public override void BuildParts(ITrapBuilder tb) + public override void BuildParts(System.IO.TextWriter tb) { left.BuildParts(tb); right.BuildParts(tb); @@ -112,9 +113,9 @@ namespace Semmle.Extraction.CIL value = s; } - public override void BuildParts(ITrapBuilder tb) + public override void BuildParts(System.IO.TextWriter tw) { - tb.Append(value); + tw.Write(value); } public override bool Equals(object obj) @@ -138,9 +139,9 @@ namespace Semmle.Extraction.CIL value = i; } - public override void BuildParts(ITrapBuilder tb) + public override void BuildParts(System.IO.TextWriter tw) { - tb.Append(value); + tw.Write(value); } public override bool Equals(object obj) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs index 3fadd1c13cf..6b1d260644d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs @@ -375,6 +375,7 @@ namespace Semmle.Extraction.CSharp Populators.CompilationUnit.Extract(cx, tree.GetRoot()); cx.PopulateAll(); cx.ExtractComments(cx.CommentGenerator); + cx.PopulateAll(); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs index 1d171878209..25a188223e2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs @@ -1,5 +1,6 @@ using Semmle.Extraction.CommentProcessing; using Semmle.Extraction.Entities; +using System.IO; namespace Semmle.Extraction.CSharp.Entities { @@ -21,13 +22,10 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override IId Id + public override void WriteId(TextWriter tw) { - get - { - var loc = Context.Create(symbol.Location); - return new Key(loc, ";commentblock"); - } + tw.WriteSubId(Context.Create(symbol.Location)); + tw.Write(";commentblock"); } public override Microsoft.CodeAnalysis.Location ReportingLocation => symbol.Location; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs index b831c11724f..6476ef087e8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs @@ -2,6 +2,7 @@ using Semmle.Extraction.CommentProcessing; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Semmle.Extraction.Entities; +using System.IO; namespace Semmle.Extraction.CSharp.Entities { @@ -122,13 +123,10 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override IId Id + public override void WriteId(TextWriter tw) { - get - { - var loc = Context.Create(Location); - return new Key(loc, ";commentline"); - } + tw.WriteSubId(Context.Create(Location)); + tw.Write(";commentline"); } static CommentLine Create(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw) => CommentLineFactory.Instance.CreateEntity(cx, loc, type, text, raw); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs index 1a54120682a..c6802f0cd7a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs @@ -5,6 +5,7 @@ using Semmle.Util; using System.Linq; using Microsoft.CodeAnalysis.CSharp; using Semmle.Extraction.Entities; +using System.IO; namespace Semmle.Extraction.CSharp.Entities { @@ -109,18 +110,12 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override IId Id + public override void WriteId(TextWriter trapFile) { - get - { - return new Key(tb => - { - if (symbol.IsStatic) tb.Append("static"); - tb.Append(ContainingType); - AddParametersToId(Context, tb, symbol); - tb.Append(";constructor"); - }); - } + if (symbol.IsStatic) trapFile.Write("static"); + trapFile.WriteSubId(ContainingType); + AddParametersToId(Context, trapFile, symbol); + trapFile.Write(";constructor"); } ConstructorDeclarationSyntax GetSyntax() => diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs index 963e3fea989..7d3bc898df6 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -9,19 +10,13 @@ namespace Semmle.Extraction.CSharp.Entities Event(Context cx, IEventSymbol init) : base(cx, init) { } - public override IId Id + public override void WriteId(TextWriter trapFile) { - get - { - return new Key(tb => - { - tb.Append(ContainingType); - tb.Append("."); - Method.AddExplicitInterfaceQualifierToId(Context, tb, symbol.ExplicitInterfaceImplementations); - tb.Append(symbol.Name); - tb.Append(";event"); - }); - } + trapFile.WriteSubId(ContainingType); + trapFile.Write('.'); + Method.AddExplicitInterfaceQualifierToId(Context, trapFile, symbol.ExplicitInterfaceImplementations); + trapFile.Write(symbol.Name); + trapFile.Write(";event"); } public override void Populate() diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 2d30a3ce468..b51eb92687f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -6,6 +6,7 @@ using System; using Semmle.Extraction.Entities; using Semmle.Extraction.Kinds; using Semmle.Extraction.CSharp.Entities.Expressions; +using System.IO; namespace Semmle.Extraction.CSharp.Entities { @@ -98,7 +99,13 @@ namespace Semmle.Extraction.CSharp.Entities readonly Lazy type; public AnnotatedType Type => type.Value; - public override IId Id => new Key(ContainingType, ".", symbol.Name, ";field"); + public override void WriteId(TextWriter trapFile) + { + trapFile.WriteSubId(ContainingType); + trapFile.Write('.'); + trapFile.Write(symbol.Name); + trapFile.Write(";field"); + } bool IExpressionParentEntity.IsTopLevelParent => true; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs index eefaf6071f2..0a00a4051b7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -73,20 +74,14 @@ namespace Semmle.Extraction.CSharp.Entities public static new Indexer Create(Context cx, IPropertySymbol prop) => IndexerFactory.Instance.CreateEntity(cx, prop); - public override IId Id + public override void WriteId(TextWriter trapFile) { - get - { - return new Key(tb => - { - tb.Append(ContainingType); - tb.Append("."); - tb.Append(symbol.MetadataName); - tb.Append("("); - tb.BuildList(",", symbol.Parameters, (p, tb0) => tb0.Append(Type.Create(Context, p.Type))); - tb.Append(");indexer"); - }); - } + trapFile.WriteSubId(ContainingType); + trapFile.Write('.'); + trapFile.Write(symbol.MetadataName); + trapFile.Write('('); + trapFile.BuildList(",", symbol.Parameters, (p, tb0) => tb0.WriteSubId(Type.Create(Context, p.Type))); + trapFile.Write(");indexer"); } public override Microsoft.CodeAnalysis.Location FullLocation diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs index e209b73f78d..5b6e50c05eb 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -10,23 +11,16 @@ namespace Semmle.Extraction.CSharp.Entities { } - public override IId Id + public override void WriteId(TextWriter trapFile) { - get + trapFile.WriteSubId(Location); + if (symbol.IsGenericMethod && !IsSourceDeclaration) { - return new Key(tb => - { - tb.Append(Location); - if (symbol.IsGenericMethod && !IsSourceDeclaration) - { - tb.Append("<"); - tb.BuildList(",", symbol.TypeArguments, (ta, tb0) => AddSignatureTypeToId(Context, tb0, symbol, ta)); - tb.Append(">"); - } - - tb.Append(";localfunction"); - }); + trapFile.Write('<'); + trapFile.BuildList(",", symbol.TypeArguments, (ta, tb0) => AddSignatureTypeToId(Context, tb0, symbol, ta)); + trapFile.Write('>'); } + trapFile.Write(";localfunction"); } public static new LocalFunction Create(Context cx, IMethodSymbol field) => LocalFunctionFactory.Instance.CreateEntity(cx, field); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs index 00bf9d720a4..a5e1f45b707 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Microsoft.CodeAnalysis; using Semmle.Extraction.Entities; @@ -18,7 +19,13 @@ namespace Semmle.Extraction.CSharp.Entities readonly bool IsVar; readonly Extraction.Entities.Location DeclLocation; - public override IId Id => new Key(Parent, "_", symbol.Name, ";localvar"); + public override void WriteId(TextWriter tw) + { + tw.WriteSubId(Parent); + tw.Write('_'); + tw.Write(symbol.Name); + tw.Write(";localvar"); + } public override void Populate() { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index 218e6ef5ce8..2d37cba6b8a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.CSharp.Populators; using System.Collections.Generic; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -105,57 +106,62 @@ namespace Semmle.Extraction.CSharp.Entities /// /// Factored out to share logic between `Method` and `UserOperator`. /// - protected static void BuildMethodId(Method m, ITrapBuilder tb) + protected static void BuildMethodId(Method m, TextWriter tw) { - tb.Append(m.ContainingType); + tw.WriteSubId(m.ContainingType); - AddExplicitInterfaceQualifierToId(m.Context, tb, m.symbol.ExplicitInterfaceImplementations); + AddExplicitInterfaceQualifierToId(m.Context, tw, m.symbol.ExplicitInterfaceImplementations); - tb.Append(".").Append(m.symbol.Name); + tw.Write("."); + tw.Write(m.symbol.Name); if (m.symbol.IsGenericMethod) { if (Equals(m.symbol, m.symbol.OriginalDefinition)) { - tb.Append("`").Append(m.symbol.TypeParameters.Length); + tw.Write('`'); + tw.Write(m.symbol.TypeParameters.Length); } else { - tb.Append("<"); + tw.Write('<'); // Encode the nullability of the type arguments in the label. // Type arguments with different nullability can result in // a constructed method with different nullability of its parameters and return type, // so we need to create a distinct database entity for it. - tb.BuildList(",", m.symbol.GetAnnotatedTypeArguments(), (ta, tb0) => { AddSignatureTypeToId(m.Context, tb0, m.symbol, ta.Symbol); tb.Append((int)ta.Nullability); }); - tb.Append(">"); + tw.BuildList(",", m.symbol.GetAnnotatedTypeArguments(), (ta, tb0) => { AddSignatureTypeToId(m.Context, tb0, m.symbol, ta.Symbol); tw.Write((int)ta.Nullability); }); + tw.Write('>'); } } - AddParametersToId(m.Context, tb, m.symbol); + AddParametersToId(m.Context, tw, m.symbol); switch (m.symbol.MethodKind) { case MethodKind.PropertyGet: - tb.Append(";getter"); + tw.Write(";getter"); break; case MethodKind.PropertySet: - tb.Append(";setter"); + tw.Write(";setter"); break; case MethodKind.EventAdd: - tb.Append(";adder"); + tw.Write(";adder"); break; case MethodKind.EventRaise: - tb.Append(";raiser"); + tw.Write(";raiser"); break; case MethodKind.EventRemove: - tb.Append(";remover"); + tw.Write(";remover"); break; default: - tb.Append(";method"); + tw.Write(";method"); break; } } - public override IId Id => new Key(tb => BuildMethodId(this, tb)); + public override void WriteId(TextWriter trapFile) + { + BuildMethodId(this, trapFile); + } /// /// Adds an appropriate label ID to the trap builder @@ -193,58 +199,54 @@ namespace Semmle.Extraction.CSharp.Entities /// to make the reference to #3 in the label definition #4 for /// T valid. /// - protected static void AddSignatureTypeToId(Context cx, ITrapBuilder tb, IMethodSymbol method, ITypeSymbol type) + protected static void AddSignatureTypeToId(Context cx, TextWriter tb, IMethodSymbol method, ITypeSymbol type) { if (type.ContainsTypeParameters(cx, method)) type.BuildTypeId(cx, tb, (cx0, tb0, type0) => AddSignatureTypeToId(cx, tb0, method, type0)); else - tb.Append(Type.Create(cx, type)); + tb.WriteSubId(Type.Create(cx, type)); } - protected static void AddParametersToId(Context cx, ITrapBuilder tb, IMethodSymbol method) + protected static void AddParametersToId(Context cx, TextWriter tb, IMethodSymbol method) { - tb.Append("("); - tb.AppendList(",", AddParameterPartsToId(cx, tb, method)); - tb.Append(")"); - } + tb.Write('('); + int index = 0; - // This is a slight abuse of ITrapBuilder.AppendList(). - // yield return "" is used to insert a list separator - // at the desired location. - static IEnumerable AddParameterPartsToId(Context cx, ITrapBuilder tb, IMethodSymbol method) - { if (method.MethodKind == MethodKind.ReducedExtension) { + tb.WriteSeparator(",", index++); AddSignatureTypeToId(cx, tb, method, method.ReceiverType); - yield return ""; // The next yield return outputs a "," } foreach (var param in method.Parameters) { - yield return ""; // Maybe print "," + tb.WriteSeparator(",", index++); AddSignatureTypeToId(cx, tb, method, param.Type); switch (param.RefKind) { case RefKind.Out: - yield return " out"; + tb.Write(" out"); break; case RefKind.Ref: - yield return " ref"; + tb.Write(" ref"); break; } } if (method.IsVararg) { - yield return "__arglist"; + tb.WriteSeparator(",", index++); + tb.Write("__arglist"); } + + tb.Write(')'); } - public static void AddExplicitInterfaceQualifierToId(Context cx, ITrapBuilder tb, IEnumerable explicitInterfaceImplementations) + public static void AddExplicitInterfaceQualifierToId(Context cx, System.IO.TextWriter tw, IEnumerable explicitInterfaceImplementations) { if (explicitInterfaceImplementations.Any()) { - tb.AppendList(",", explicitInterfaceImplementations.Select(impl => cx.CreateEntity(impl.ContainingType))); + tw.AppendList(",", explicitInterfaceImplementations.Select(impl => cx.CreateEntity(impl.ContainingType))); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs index 71b63dbceb5..0472499ae61 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using System; +using System.IO; using System.Reflection; namespace Semmle.Extraction.CSharp.Entities @@ -32,7 +33,11 @@ namespace Semmle.Extraction.CSharp.Entities public override Microsoft.CodeAnalysis.Location ReportingLocation => null; - public override IId Id => new Key(symbol.name, ";modifier"); + public override void WriteId(TextWriter trapFile) + { + trapFile.Write(symbol.name); + trapFile.Write(";modifier"); + } public override bool NeedsPopulation => true; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs index 30c155a6d8e..03003c620a6 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs @@ -1,3 +1,4 @@ +using System.IO; using Microsoft.CodeAnalysis; namespace Semmle.Extraction.CSharp.Entities @@ -22,13 +23,15 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override IId Id + public override void WriteId(TextWriter trapFile) { - get + if (!symbol.IsGlobalNamespace) { - return symbol.IsGlobalNamespace ? new Key(";namespace") : - new Key(Create(Context, symbol.ContainingNamespace), ".", symbol.Name, ";namespace"); + trapFile.WriteSubId(Create(Context, symbol.ContainingNamespace)); + trapFile.Write('.'); + trapFile.Write(symbol.Name); } + trapFile.Write(";namespace"); } public static Namespace Create(Context cx, INamespaceSymbol ns) => NamespaceFactory.Instance.CreateEntity2(cx, ns); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index 5a2144e553b..1847fbfb9d8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -4,6 +4,7 @@ using Semmle.Extraction.CSharp.Populators; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Entities; +using System.IO; namespace Semmle.Extraction.CSharp.Entities { @@ -75,16 +76,14 @@ namespace Semmle.Extraction.CSharp.Entities public static Parameter GetAlreadyCreated(Context cx, IParameterSymbol param) => ParameterFactory.Instance.CreateEntity(cx, param, null, null); - public override IId Id + public override void WriteId(TextWriter trapFile) { - get - { - // This is due to a bug in Roslyn when ValueTuple.cs is extracted. - // The parameter symbols don't match up properly so we don't have a parent. - if (Parent == null) - Parent = Method.Create(Context, symbol.ContainingSymbol as IMethodSymbol); - return new Key(Parent, "_", Ordinal, ";parameter"); - } + if (Parent == null) + Parent = Method.Create(Context, symbol.ContainingSymbol as IMethodSymbol); + trapFile.WriteSubId(Parent); + trapFile.Write('_'); + trapFile.Write(Ordinal); + trapFile.Write(";parameter"); } public override bool NeedsPopulation => true; @@ -193,7 +192,10 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public sealed override IId Id => new Key("__arglist;type"); + public override void WriteId(TextWriter trapFile) + { + trapFile.Write("__arglist;type"); + } public override int GetHashCode() { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index 7ff878ae896..0d3db855713 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -3,6 +3,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.CSharp.Entities.Expressions; using Semmle.Extraction.Entities; using Semmle.Extraction.Kinds; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -12,19 +13,13 @@ namespace Semmle.Extraction.CSharp.Entities protected Property(Context cx, IPropertySymbol init) : base(cx, init) { } - public override IId Id + public override void WriteId(TextWriter trapFile) { - get - { - return new Key(tb => - { - tb.Append(ContainingType); - tb.Append("."); - Method.AddExplicitInterfaceQualifierToId(Context, tb, symbol.ExplicitInterfaceImplementations); - tb.Append(symbol.Name); - tb.Append(";property"); - }); - } + trapFile.WriteSubId(ContainingType); + trapFile.Write('.'); + Method.AddExplicitInterfaceQualifierToId(Context, trapFile, symbol.ExplicitInterfaceImplementations); + trapFile.Write(symbol.Name); + trapFile.Write(";property"); } public override void Populate() diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs index 6ba4f225cfe..4ac4a3a87a1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs @@ -1,3 +1,4 @@ +using System.IO; using Microsoft.CodeAnalysis; namespace Semmle.Extraction.CSharp.Entities @@ -29,18 +30,12 @@ namespace Semmle.Extraction.CSharp.Entities ExtractNullability(symbol.ElementNullableAnnotation); } - public override IId Id + public override void WriteId(TextWriter trapFile) { - get - { - return new Key(tb => - { - tb.Append(element.Type); - tb.Append((int)symbol.ElementNullableAnnotation); - symbol.BuildArraySuffix(tb); - tb.Append(";type"); - }); - } + trapFile.WriteSubId(element.Type); + trapFile.Write((int)symbol.ElementNullableAnnotation); + symbol.BuildArraySuffix(trapFile); + trapFile.Write(";type"); } public static ArrayType Create(Context cx, IArrayTypeSymbol symbol) => ArrayTypeFactory.Instance.CreateEntity(cx, symbol); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs index 8c1f95bd423..c3f460c7853 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs @@ -1,4 +1,5 @@ using Microsoft.CodeAnalysis; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -21,9 +22,10 @@ namespace Semmle.Extraction.CSharp.Entities Context.Emit(Tuples.parent_namespace(this, Namespace.Create(Context, Context.Compilation.GlobalNamespace))); } - static readonly Key id = new Key("dynamic;type"); - - public override IId Id => id; + public override void WriteId(TextWriter trapFile) + { + trapFile.Write("dynamic;type"); + } class DynamicTypeFactory : ICachedEntityFactory { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs index 064f2339d1e..3b522fdb0db 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs @@ -4,6 +4,7 @@ using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Entities; using System; using System.Collections.Generic; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -111,17 +112,10 @@ namespace Semmle.Extraction.CSharp.Entities public override Microsoft.CodeAnalysis.Location ReportingLocation => GetLocations(symbol).FirstOrDefault(); - public override IId Id + public override void WriteId(TextWriter trapFile) { - get - { - return new Key(tb => - { - // All syntactic sub terms (types) are referenced by key in the ID - symbol.BuildTypeId(Context, tb, (cx0, tb0, sub) => tb0.Append(Create(cx0, sub))); - tb.Append(";type"); - }); - } + symbol.BuildTypeId(Context, trapFile, (cx0, tb0, sub) => tb0.WriteSubId(Create(cx0, sub))); + trapFile.Write(";type"); } /// @@ -183,15 +177,10 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override IId Id + public override void WriteId(TextWriter trapFile) { - get - { - return new Key(tb => - { - tb.Append(referencedType).Append(";typeref"); - }); - } + trapFile.WriteSubId(referencedType); + trapFile.Write(";typeRef"); } public override void Populate() diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs index d18a84d3831..889c95f2f3a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs @@ -1,3 +1,4 @@ +using System.IO; using Microsoft.CodeAnalysis; namespace Semmle.Extraction.CSharp.Entities @@ -12,7 +13,10 @@ namespace Semmle.Extraction.CSharp.Entities Context.Emit(Tuples.types(this, Kinds.TypeKind.NULL, "null")); } - public override IId Id => new Key(";type"); + public override void WriteId(TextWriter trapFile) + { + trapFile.Write(";type"); + } public override bool NeedsPopulation => true; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs index 3f722b05e6f..3a65d7cb75e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs @@ -1,3 +1,4 @@ +using System.IO; using Microsoft.CodeAnalysis; namespace Semmle.Extraction.CSharp.Entities @@ -10,7 +11,11 @@ namespace Semmle.Extraction.CSharp.Entities PointedAtType = Create(cx, symbol.PointedAtType); } - public override IId Id => new Key(PointedAtType, "*;type"); + public override void WriteId(TextWriter trapFile) + { + trapFile.WriteSubId(PointedAtType); + trapFile.Write("*;type"); + } // All pointer types are extracted because they won't // be extracted in their defining assembly. diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs index c34bbaf8a1b..3dfed2adb23 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs @@ -2,6 +2,7 @@ using Semmle.Extraction.Entities; using System; using System.Collections.Generic; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -29,16 +30,10 @@ namespace Semmle.Extraction.CSharp.Entities // All tuple types are "local types" public override bool NeedsPopulation => true; - public override IId Id + public override void WriteId(TextWriter trapFile) { - get - { - return new Key(tb => - { - symbol.BuildTypeId(Context, tb, (cx0, tb0, sub) => tb0.Append(Create(cx0, sub))); - tb.Append(";tuple"); - }); - } + symbol.BuildTypeId(Context, trapFile, (cx0, tb0, sub) => tb0.WriteSubId(Create(cx0, sub))); + trapFile.Write(";tuple"); } public override void Populate() diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs index e276183cb87..b030614441a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs @@ -4,6 +4,7 @@ using Semmle.Extraction.CSharp.Populators; using Semmle.Util; using System; using System.Collections.Generic; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -77,36 +78,14 @@ namespace Semmle.Extraction.CSharp.Entities } } - class DisplayNameTrapBuilder : ITrapBuilder - { - public readonly List Fragments = new List(); - - public ITrapBuilder Append(object arg) - { - Fragments.Add(arg.ToString()); - return this; - } - - public ITrapBuilder Append(string arg) - { - Fragments.Add(arg); - return this; - } - - public ITrapBuilder AppendLine() - { - throw new NotImplementedException(); - } - } - protected void ExtractType() { ExtractMetadataHandle(); ExtractAttributes(); - var tb = new DisplayNameTrapBuilder(); + var tb = new StringWriter(); symbol.BuildDisplayName(Context, tb); - Context.Emit(Tuples.types(this, GetClassType(Context, symbol), tb.Fragments.ToArray())); + Context.Emit(Tuples.types(this, GetClassType(Context, symbol), tb.ToString())); // Visit base types var baseTypes = new List(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs index c41eb73ff30..d0d06f7c3c2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Entities; using System.Collections.Generic; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -107,27 +108,28 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override IId Id + public override void WriteId(TextWriter trapFile) { - get + string kind; + IEntity containingEntity; + switch (symbol.TypeParameterKind) { - string kind; - IEntity containingEntity; - switch (symbol.TypeParameterKind) - { - case TypeParameterKind.Method: - kind = "methodtypeparameter"; - containingEntity = Method.Create(Context, (IMethodSymbol)symbol.ContainingSymbol); - break; - case TypeParameterKind.Type: - kind = "typeparameter"; - containingEntity = Create(Context, symbol.ContainingType); - break; - default: - throw new InternalError(symbol, $"Unhandled type parameter kind {symbol.TypeParameterKind}"); - } - return new Key(containingEntity, "_", symbol.Ordinal, ";", kind); + case TypeParameterKind.Method: + kind = "methodtypeparameter"; + containingEntity = Method.Create(Context, (IMethodSymbol)symbol.ContainingSymbol); + break; + case TypeParameterKind.Type: + kind = "typeparameter"; + containingEntity = Create(Context, symbol.ContainingType); + break; + default: + throw new InternalError(symbol, $"Unhandled type parameter kind {symbol.TypeParameterKind}"); } + trapFile.WriteSubId(containingEntity); + trapFile.Write('_'); + trapFile.Write(symbol.Ordinal); + trapFile.Write(';'); + trapFile.Write(kind); } class TypeParameterFactory : ICachedEntityFactory diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs index ce94cb5476f..ace6e3cd99c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities @@ -48,17 +49,11 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override IId Id + public override void WriteId(TextWriter trapFile) { - get - { - return new Key(tb => - { - AddSignatureTypeToId(Context, tb, symbol, symbol.ReturnType); // Needed for op_explicit(), which differs only by return type. - tb.Append(" "); - BuildMethodId(this, tb); - }); - } + AddSignatureTypeToId(Context, trapFile, symbol, symbol.ReturnType); // Needed for op_explicit(), which differs only by return type. + trapFile.Write(' '); + BuildMethodId(this, trapFile); } /// diff --git a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs index 5015e467a94..9d104a01c96 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis; using Semmle.Extraction.CSharp.Entities; using System; using System.Collections.Generic; +using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp @@ -128,9 +129,9 @@ namespace Semmle.Extraction.CSharp /// syntactic sub terms of this type (if any). /// /// The extraction context. - /// The trap builder used to store the result. + /// The trap builder used to store the result. /// The action to apply to syntactic sub terms of this type. - public static void BuildTypeId(this ITypeSymbol type, Context cx, ITrapBuilder tb, Action subTermAction) + public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter tw, Action subTermAction) { if (type.SpecialType != SpecialType.None) { @@ -139,7 +140,7 @@ namespace Semmle.Extraction.CSharp * This makes the IDs shorter and means that all built-in types map to * the same entities (even when using multiple versions of mscorlib). */ - tb.Append(type.ToDisplayString()); + tw.Write(type.ToDisplayString()); return; } @@ -149,8 +150,8 @@ namespace Semmle.Extraction.CSharp { case TypeKind.Array: var array = (IArrayTypeSymbol)type; - subTermAction(cx, tb, array.ElementType); - array.BuildArraySuffix(tb); + subTermAction(cx, tw, array.ElementType); + array.BuildArraySuffix(tw); return; case TypeKind.Class: case TypeKind.Interface: @@ -159,19 +160,19 @@ namespace Semmle.Extraction.CSharp case TypeKind.Delegate: case TypeKind.Error: var named = (INamedTypeSymbol)type; - named.BuildNamedTypeId(cx, tb, subTermAction); + named.BuildNamedTypeId(cx, tw, subTermAction); return; case TypeKind.Pointer: var ptr = (IPointerTypeSymbol)type; - subTermAction(cx, tb, ptr.PointedAtType); - tb.Append("*"); + subTermAction(cx, tw, ptr.PointedAtType); + tw.Write('*'); return; case TypeKind.TypeParameter: var tp = (ITypeParameterSymbol)type; - tb.Append(tp.Name); + tw.Write(tp.Name); return; case TypeKind.Dynamic: - tb.Append("dynamic"); + tw.Write("dynamic"); return; default: throw new InternalError(type, $"Unhandled type kind '{type.TypeKind}'"); @@ -179,64 +180,70 @@ namespace Semmle.Extraction.CSharp } } + /// /// Constructs an array suffix string for this array type symbol. /// /// The trap builder used to store the result. - public static void BuildArraySuffix(this IArrayTypeSymbol array, ITrapBuilder tb) + public static void BuildArraySuffix(this IArrayTypeSymbol array, TextWriter tb) { - tb.Append("["); + tb.Write('['); for (int i = 0; i < array.Rank - 1; i++) - tb.Append(","); - tb.Append("]"); + tb.Write(','); + tb.Write(']'); } - static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, ITrapBuilder tb, Action subTermAction) + static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, TextWriter tw, Action subTermAction) { if (named.IsTupleType) { - tb.Append("("); - tb.BuildList(",", named.TupleElements, + tw.Write('('); + tw.BuildList(",", named.TupleElements, (f, tb0) => { - tb.Append(f.Name).Append(":"); + tw.Write(f.Name); + tw.Write(":"); subTermAction(cx, tb0, f.Type); } ); - tb.Append(")"); + tw.Write(")"); return; } if (named.ContainingType != null) { - subTermAction(cx, tb, named.ContainingType); - tb.Append("."); + subTermAction(cx, tw, named.ContainingType); + tw.Write('.'); } else if (named.ContainingNamespace != null) { - named.ContainingNamespace.BuildNamespace(cx, tb); + named.ContainingNamespace.BuildNamespace(cx, tw); } if (named.IsAnonymousType) - named.BuildAnonymousName(cx, tb, subTermAction, true); + named.BuildAnonymousName(cx, tw, subTermAction, true); else if (named.TypeParameters.IsEmpty) - tb.Append(named.Name); + tw.Write(named.Name); else if (IsReallyUnbound(named)) - tb.Append(named.Name).Append("`").Append(named.TypeParameters.Length); + { + tw.Write(named.Name); + tw.Write("`"); + tw.Write(named.TypeParameters.Length); + } else { - subTermAction(cx, tb, named.ConstructedFrom); - tb.Append("<"); + subTermAction(cx, tw, named.ConstructedFrom); + tw.Write('<'); // Encode the nullability of the type arguments in the label. // Type arguments with different nullability can result in // a constructed type with different nullability of its members and methods, // so we need to create a distinct database entity for it. - tb.BuildList(",", named.GetAnnotatedTypeArguments(), (ta, tb0) => { subTermAction(cx, tb0, ta.Symbol); tb.Append((int)ta.Nullability); }); - tb.Append(">"); + tw.BuildList(",", named.GetAnnotatedTypeArguments(), (ta, tb0) => { subTermAction(cx, tb0, ta.Symbol); tw.Write((int)ta.Nullability); }); + tw.Write('>'); } } - static void BuildNamespace(this INamespaceSymbol ns, Context cx, ITrapBuilder tb) + static void BuildNamespace(this INamespaceSymbol ns, Context cx, TextWriter tw) { // Only include the assembly information in each type ID // for normal extractions. This is because standalone extractions @@ -248,38 +255,44 @@ namespace Semmle.Extraction.CSharp // Note that we exclude the revision number as this has // been observed to be unstable. var assembly = ns.ContainingAssembly.Identity; - tb.Append(assembly.Name).Append("_"). - Append(assembly.Version.Major).Append("."). - Append(assembly.Version.Minor).Append("."). - Append(assembly.Version.Build).Append("::"); + tw.Write(assembly.Name); + tw.Write('_'); + tw.Write(assembly.Version.Major); + tw.Write('.'); + tw.Write(assembly.Version.Minor); + tw.Write('.'); + tw.Write(assembly.Version.Build); + tw.Write("::"); } - tb.Append(Namespace.Create(cx, ns)).Append("."); + tw.WriteSubId(Namespace.Create(cx, ns)); + tw.Write('.'); } - static void BuildAnonymousName(this ITypeSymbol type, Context cx, ITrapBuilder tb, Action subTermAction, bool includeParamName) + static void BuildAnonymousName(this ITypeSymbol type, Context cx, TextWriter tw, Action subTermAction, bool includeParamName) { var buildParam = includeParamName ? (prop, tb0) => { - tb0.Append(prop.Name).Append(" "); + tb0.Write(prop.Name); + tw.Write(' '); subTermAction(cx, tb0, prop.Type); } - : (Action)((prop, tb0) => subTermAction(cx, tb0, prop.Type)); + : (Action)((prop, tb0) => subTermAction(cx, tb0, prop.Type)); int memberCount = type.GetMembers().OfType().Count(); int hackTypeNumber = memberCount == 1 ? 1 : 0; - tb.Append("<>__AnonType"); - tb.Append(hackTypeNumber); - tb.Append("<"); - tb.BuildList(",", type.GetMembers().OfType(), buildParam); - tb.Append(">"); + tw.Write("<>__AnonType"); + tw.Write(hackTypeNumber); + tw.Write('<'); + tw.BuildList(",", type.GetMembers().OfType(), buildParam); + tw.Write('>'); } /// /// Constructs a display name string for this type symbol. /// - /// The trap builder used to store the result. - public static void BuildDisplayName(this ITypeSymbol type, Context cx, ITrapBuilder tb) + /// The trap builder used to store the result. + public static void BuildDisplayName(this ITypeSymbol type, Context cx, TextWriter tw) { using (cx.StackGuard) { @@ -290,11 +303,11 @@ namespace Semmle.Extraction.CSharp var elementType = array.ElementType; if (elementType.MetadataName.IndexOf("`") >= 0) { - tb.Append(elementType.Name); + tw.Write(elementType.Name); return; } - elementType.BuildDisplayName(cx, tb); - array.BuildArraySuffix(tb); + elementType.BuildDisplayName(cx, tw); + array.BuildArraySuffix(tw); return; case TypeKind.Class: case TypeKind.Interface: @@ -303,18 +316,18 @@ namespace Semmle.Extraction.CSharp case TypeKind.Delegate: case TypeKind.Error: var named = (INamedTypeSymbol)type; - named.BuildNamedTypeDisplayName(cx, tb); + named.BuildNamedTypeDisplayName(cx, tw); return; case TypeKind.Pointer: var ptr = (IPointerTypeSymbol)type; - ptr.PointedAtType.BuildDisplayName(cx, tb); - tb.Append("*"); + ptr.PointedAtType.BuildDisplayName(cx, tw); + tw.Write('*'); return; case TypeKind.TypeParameter: - tb.Append(type.Name); + tw.Write(type.Name); return; case TypeKind.Dynamic: - tb.Append("dynamic"); + tw.Write("dynamic"); return; default: throw new InternalError(type, $"Unhandled type kind '{type.TypeKind}'"); @@ -322,34 +335,34 @@ namespace Semmle.Extraction.CSharp } } - public static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, ITrapBuilder tb) + public static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, TextWriter tw) { if (namedType.IsTupleType) { - tb.Append("("); - tb.BuildList(",", namedType.TupleElements.Select(f => f.Type), + tw.Write('('); + tw.BuildList(",", namedType.TupleElements.Select(f => f.Type), (t, tb0) => t.BuildDisplayName(cx, tb0) ); - tb.Append(")"); + tw.Write(")"); return; } if (namedType.IsAnonymousType) { - namedType.BuildAnonymousName(cx, tb, (cx0, tb0, sub) => sub.BuildDisplayName(cx0, tb0), false); + namedType.BuildAnonymousName(cx, tw, (cx0, tb0, sub) => sub.BuildDisplayName(cx0, tb0), false); } - tb.Append(namedType.Name); + tw.Write(namedType.Name); if (namedType.IsGenericType && namedType.TypeKind != TypeKind.Error && namedType.TypeArguments.Any()) { - tb.Append("<"); - tb.BuildList(",", namedType.TypeArguments, (p, tb0) => + tw.Write('<'); + tw.BuildList(",", namedType.TypeArguments, (p, tb0) => { if (IsReallyBound(namedType)) p.BuildDisplayName(cx, tb0); }); - tb.Append(">"); + tw.Write('>'); } } diff --git a/csharp/extractor/Semmle.Extraction.Tests/Layout.cs b/csharp/extractor/Semmle.Extraction.Tests/Layout.cs index 631a7b89a07..b36820c8981 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/Layout.cs +++ b/csharp/extractor/Semmle.Extraction.Tests/Layout.cs @@ -191,9 +191,9 @@ namespace Semmle.Extraction.Tests Content = content; } - public void EmitToTrapBuilder(ITrapBuilder tb) + public void EmitToTrapBuilder(TextWriter tb) { - tb.Append(Content); + tb.Write(Content); } } } diff --git a/csharp/extractor/Semmle.Extraction/Context.cs b/csharp/extractor/Semmle.Extraction/Context.cs index ef0e3ff5aed..aae098f2442 100644 --- a/csharp/extractor/Semmle.Extraction/Context.cs +++ b/csharp/extractor/Semmle.Extraction/Context.cs @@ -4,6 +4,7 @@ using Semmle.Extraction.Entities; using Semmle.Util.Logging; using System; using System.Collections.Generic; +using System.IO; using System.Linq; namespace Semmle.Extraction @@ -53,6 +54,28 @@ namespace Semmle.Extraction return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init); } + bool DefiningLabel = false; + + public void DefineLabel(IEntity entity, TextWriter trapFile) + { + if (DefiningLabel) + { + PopulateLater(() => DefineLabel(entity, trapFile)); + } + else + { + try + { + DefiningLabel = true; + entity.DefineLabel(trapFile); + } + finally + { + DefiningLabel = false; + } + } + } + /// /// Creates a new entity using the factory. /// Uses a different cache to , @@ -73,23 +96,27 @@ namespace Semmle.Extraction } else { - var id = entity.Id; -#if DEBUG_LABELS - CheckEntityHasUniqueLabel(id, entity); -#endif - label = new Label(GetNewId()); + label = GetNewLabel(); entity.Label = label; entityLabelCache[entity] = label; - DefineLabel(label, id); + + DefineLabel(entity, TrapWriter.Writer); + if (entity.NeedsPopulation) Populate(init as ISymbol, entity); +#if DEBUG_LABELS + var id = new StringWriter(); + entity.WriteId(id); + CheckEntityHasUniqueLabel(id.ToString(), entity); +#endif + } return entity; } } #if DEBUG_LABELS - private void CheckEntityHasUniqueLabel(IId id, ICachedEntity entity) + private void CheckEntityHasUniqueLabel(string id, ICachedEntity entity) { if (idLabelCache.TryGetValue(id, out var originalEntity)) { @@ -102,6 +129,8 @@ namespace Semmle.Extraction } #endif + public Label GetNewLabel() => new Label(GetNewId()); + private Entity CreateNonNullEntity(ICachedEntityFactory factory, Type init) where Entity : ICachedEntity { if (objectEntityCache.TryGetValue(init, out var cached)) @@ -109,22 +138,22 @@ namespace Semmle.Extraction using (StackGuard) { - var label = new Label(GetNewId()); + var label = GetNewLabel(); var entity = factory.Create(this, init); entity.Label = label; objectEntityCache[init] = entity; - var id = entity.Id; - DefineLabel(label, id); - -#if DEBUG_LABELS - CheckEntityHasUniqueLabel(id, entity); -#endif - + DefineLabel(entity, TrapWriter.Writer); if (entity.NeedsPopulation) Populate(init as ISymbol, entity); +#if DEBUG_LABELS + var id = new StringWriter(); + entity.WriteId(id); + CheckEntityHasUniqueLabel(id.ToString(), entity); +#endif + return entity; } } @@ -158,29 +187,17 @@ namespace Semmle.Extraction /// public void AddFreshLabel(IEntity entity) { - var label = new Label(GetNewId()); - TrapWriter.Emit(new DefineFreshLabelEmitter(label)); - entity.Label = label; + entity.Label = GetNewLabel(); + entity.DefineFreshLabel(TrapWriter.Writer); } #if DEBUG_LABELS - readonly Dictionary idLabelCache = new Dictionary(); + readonly Dictionary idLabelCache = new Dictionary(); #endif readonly Dictionary objectEntityCache = new Dictionary(); readonly Dictionary entityLabelCache = new Dictionary(); readonly HashSet