C#: Remove ITrapBuilder in favour of TextWriter.

This commit is contained in:
Calum Grant
2019-08-08 18:15:30 +01:00
parent aeb38a1757
commit e41e8d6547
44 changed files with 552 additions and 554 deletions

View File

@@ -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");

View File

@@ -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
{

View File

@@ -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);
}
/// <summary>
/// For debugging purposes.
/// </summary>
string DebugId => this.GetDebugLabel();
public void WriteQuotedId(TextWriter trapFile)
{
WriteId(trapFile);
}
public Id ShortId { get; set; }
public abstract Id IdSuffix { get; }

View File

@@ -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<IExtractionProduct> 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);

View File

@@ -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(() =>
{

View File

@@ -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
/// </summary>
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)

View File

@@ -375,6 +375,7 @@ namespace Semmle.Extraction.CSharp
Populators.CompilationUnit.Extract(cx, tree.GetRoot());
cx.PopulateAll();
cx.ExtractComments(cx.CommentGenerator);
cx.PopulateAll();
}
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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() =>

View File

@@ -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()

View File

@@ -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<AnnotatedType> 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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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()
{

View File

@@ -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
/// <summary>
/// Factored out to share logic between `Method` and `UserOperator`.
/// </summary>
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);
}
/// <summary>
/// Adds an appropriate label ID to the trap builder <paramref name="tb"/>
@@ -193,58 +199,54 @@ namespace Semmle.Extraction.CSharp.Entities
/// to make the reference to <code>#3</code> in the label definition <code>#4</code> for
/// <code>T</code> valid.
/// </summary>
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<object> 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<ISymbol> explicitInterfaceImplementations)
public static void AddExplicitInterfaceQualifierToId(Context cx, System.IO.TextWriter tw, IEnumerable<ISymbol> explicitInterfaceImplementations)
{
if (explicitInterfaceImplementations.Any())
{
tb.AppendList(",", explicitInterfaceImplementations.Select(impl => cx.CreateEntity(impl.ContainingType)));
tw.AppendList(",", explicitInterfaceImplementations.Select(impl => cx.CreateEntity(impl.ContainingType)));
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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()
{

View File

@@ -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()

View File

@@ -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);

View File

@@ -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<IDynamicTypeSymbol, DynamicType>
{

View File

@@ -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");
}
/// <summary>
@@ -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()

View File

@@ -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("<null>;type");
public override void WriteId(TextWriter trapFile)
{
trapFile.Write("<null>;type");
}
public override bool NeedsPopulation => true;

View File

@@ -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.

View File

@@ -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()

View File

@@ -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<string> Fragments = new List<string>();
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<Type>();

View File

@@ -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<ITypeParameterSymbol, TypeParameter>

View File

@@ -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);
}
/// <summary>

View File

@@ -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).
/// </summary>
/// <param name="cx">The extraction context.</param>
/// <param name="tb">The trap builder used to store the result.</param>
/// <param name="tw">The trap builder used to store the result.</param>
/// <param name="subTermAction">The action to apply to syntactic sub terms of this type.</param>
public static void BuildTypeId(this ITypeSymbol type, Context cx, ITrapBuilder tb, Action<Context, ITrapBuilder, ITypeSymbol> subTermAction)
public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter tw, Action<Context, TextWriter, ITypeSymbol> 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
}
}
/// <summary>
/// Constructs an array suffix string for this array type symbol.
/// </summary>
/// <param name="tb">The trap builder used to store the result.</param>
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<Context, ITrapBuilder, ITypeSymbol> subTermAction)
static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, TextWriter tw, Action<Context, TextWriter, ITypeSymbol> 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<Context, ITrapBuilder, ITypeSymbol> subTermAction, bool includeParamName)
static void BuildAnonymousName(this ITypeSymbol type, Context cx, TextWriter tw, Action<Context, TextWriter, ITypeSymbol> 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<IPropertySymbol, ITrapBuilder>)((prop, tb0) => subTermAction(cx, tb0, prop.Type));
: (Action<IPropertySymbol, TextWriter>)((prop, tb0) => subTermAction(cx, tb0, prop.Type));
int memberCount = type.GetMembers().OfType<IPropertySymbol>().Count();
int hackTypeNumber = memberCount == 1 ? 1 : 0;
tb.Append("<>__AnonType");
tb.Append(hackTypeNumber);
tb.Append("<");
tb.BuildList(",", type.GetMembers().OfType<IPropertySymbol>(), buildParam);
tb.Append(">");
tw.Write("<>__AnonType");
tw.Write(hackTypeNumber);
tw.Write('<');
tw.BuildList(",", type.GetMembers().OfType<IPropertySymbol>(), buildParam);
tw.Write('>');
}
/// <summary>
/// Constructs a display name string for this type symbol.
/// </summary>
/// <param name="tb">The trap builder used to store the result.</param>
public static void BuildDisplayName(this ITypeSymbol type, Context cx, ITrapBuilder tb)
/// <param name="tw">The trap builder used to store the result.</param>
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('>');
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}
/// <summary>
/// Creates a new entity using the factory.
/// Uses a different cache to <see cref="CreateEntity{Type, Entity}(ICachedEntityFactory{Type, Entity}, Type)"/>,
@@ -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<Type, Entity>(ICachedEntityFactory<Type, Entity> 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
/// </summary>
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<IId, ICachedEntity> idLabelCache = new Dictionary<IId, ICachedEntity>();
readonly Dictionary<string, ICachedEntity> idLabelCache = new Dictionary<string, ICachedEntity>();
#endif
readonly Dictionary<object, ICachedEntity> objectEntityCache = new Dictionary<object, ICachedEntity>();
readonly Dictionary<ICachedEntity, Label> entityLabelCache = new Dictionary<ICachedEntity, Label>();
readonly HashSet<Label> extractedGenerics = new HashSet<Label>();
public void DefineLabel(IEntity entity)
{
entity.Label = new Label(GetNewId());
DefineLabel(entity.Label, entity.Id);
}
void DefineLabel(Label label, IId id)
{
TrapWriter.Emit(new DefineLabelEmitter(label, id));
}
/// <summary>
/// Queue of items to populate later.
/// The only reason for this is so that the call stack does not
@@ -300,43 +317,6 @@ namespace Semmle.Extraction
}
}
class DefineLabelEmitter : ITrapEmitter
{
readonly Label label;
readonly IId id;
public DefineLabelEmitter(Label label, IId id)
{
this.label = label;
this.id = id;
}
public void EmitToTrapBuilder(ITrapBuilder tb)
{
label.AppendTo(tb);
tb.Append("=");
id.AppendTo(tb);
tb.AppendLine();
}
}
class DefineFreshLabelEmitter : ITrapEmitter
{
readonly Label Label;
public DefineFreshLabelEmitter(Label label)
{
Label = label;
}
public void EmitToTrapBuilder(ITrapBuilder tb)
{
Label.AppendTo(tb);
tb.Append("=*");
tb.AppendLine();
}
}
class PushEmitter : ITrapEmitter
{
readonly Key Key;
@@ -346,20 +326,19 @@ namespace Semmle.Extraction
Key = key;
}
public void EmitToTrapBuilder(ITrapBuilder tb)
public void EmitToTrapBuilder(TextWriter tw)
{
tb.Append(".push ");
Key.AppendTo(tb);
tb.AppendLine();
tw.Write(".push ");
Key.AppendTo(tw);
tw.WriteLine();
}
}
class PopEmitter : ITrapEmitter
{
public void EmitToTrapBuilder(ITrapBuilder tb)
public void EmitToTrapBuilder(TextWriter tw)
{
tb.Append(".pop");
tb.AppendLine();
tw.WriteLine(".pop");
}
}
@@ -373,6 +352,13 @@ namespace Semmle.Extraction
/// <exception cref="InternalError">Thrown on invalid trap stack behaviour.</exception>
public void Populate(ISymbol optionalSymbol, ICachedEntity entity)
{
if (DefiningLabel)
{
// Don't write tuples etc if we're currently defining a label
PopulateLater(() => Populate(optionalSymbol, entity));
return;
}
bool duplicationGuard;
bool deferred;

View File

@@ -65,14 +65,15 @@ namespace Semmle.Extraction.Entities
return AssemblyConstructorFactory.Instance.CreateEntity(cx, null);
}
public override IId Id
public override void WriteId(System.IO.TextWriter trapFile)
{
get
trapFile.Write(assembly.ToString());
if (assemblyPath is null)
{
return assemblyPath == null
? new Key(assembly, ";assembly")
: new Key(assembly, "#file:///", assemblyPath.Replace("\\", "/"), ";assembly");
trapFile.Write("#file:///");
trapFile.Write(assemblyPath.Replace("\\", "/"));
}
trapFile.Write(";assembly");
}
}
}

View File

@@ -63,13 +63,14 @@ namespace Semmle.Extraction.Entities
}
}
public override IId Id
public override void WriteId(System.IO.TextWriter trapFile)
{
get
if (Path is null)
trapFile.Write("GENERATED;sourcefile");
else
{
return Path == null ?
new Key("GENERATED;sourcefile") :
new Key(DatabasePath, ";sourcefile");
trapFile.Write(DatabasePath);
trapFile.Write(";sourcefile");
}
}
@@ -104,7 +105,10 @@ namespace Semmle.Extraction.Entities
Context.TrapWriter.files(this, "", "", "");
}
public override IId Id => new Key("GENERATED;sourcefile");
public override void WriteId(TextWriter trapFile)
{
trapFile.Write("GENERATED;sourcefile");
}
public static GeneratedFile Create(Context cx) =>
GeneratedFileFactory.Instance.CreateEntity(cx, null);

View File

@@ -36,7 +36,11 @@ namespace Semmle.Extraction.Entities
public override bool NeedsPopulation => true;
public override IId Id => new Key(DatabasePath, ";folder");
public override void WriteId(System.IO.TextWriter trapFile)
{
trapFile.Write(DatabasePath);
trapFile.Write(";folder");
}
public static Folder Create(Context cx, DirectoryInfo folder) =>
FolderFactory.Instance.CreateEntity2(cx, folder);

View File

@@ -1,3 +1,5 @@
using System.IO;
namespace Semmle.Extraction.Entities
{
public class GeneratedLocation : SourceLocation
@@ -15,7 +17,12 @@ namespace Semmle.Extraction.Entities
Context.TrapWriter.locations_default(this, GeneratedFile, 0, 0, 0, 0);
}
public override IId Id => new Key("loc,", GeneratedFile, ",0,0,0,0");
public override void WriteId(TextWriter trapFile)
{
trapFile.Write("loc,");
trapFile.WriteSubId(GeneratedFile);
trapFile.Write(",0,0,0,0");
}
public override int GetHashCode() => 98732567;

View File

@@ -1,3 +1,4 @@
using System.IO;
using Microsoft.CodeAnalysis;
namespace Semmle.Extraction.Entities
@@ -31,15 +32,20 @@ namespace Semmle.Extraction.Entities
private set;
}
public override IId Id
public override void WriteId(System.IO.TextWriter trapFile)
{
get
{
FileLinePositionSpan l = symbol.GetLineSpan();
FileEntity = Entities.File.Create(Context, l.Path);
return new Key("loc,", FileEntity, ",", l.Span.Start.Line + 1, ",",
l.Span.Start.Character + 1, ",", l.Span.End.Line + 1, ",", l.Span.End.Character);
}
FileLinePositionSpan l = symbol.GetLineSpan();
FileEntity = Entities.File.Create(Context, l.Path);
trapFile.Write("loc,");
trapFile.WriteSubId(FileEntity);
trapFile.Write(',');
trapFile.Write(l.Span.Start.Line + 1);
trapFile.Write(',');
trapFile.Write(l.Span.Start.Character + 1);
trapFile.Write(',');
trapFile.Write(l.Span.End.Line + 1);
trapFile.Write(',');
trapFile.Write(l.Span.End.Character);
}
class SourceLocationFactory : ICachedEntityFactory<Microsoft.CodeAnalysis.Location, SourceLocation>

View File

@@ -1,5 +1,5 @@
using Microsoft.CodeAnalysis;
using System;
using System.IO;
namespace Semmle.Extraction
{
@@ -26,10 +26,17 @@ namespace Semmle.Extraction
Label Label { set; get; }
/// <summary>
/// The ID used for the entity, as it is in the trap file.
/// Could be '*'.
/// Writes the unique identifier of this entitiy to a trap file.
/// </summary>
IId Id { get; }
/// <param name="writer"></param>
void WriteId(TextWriter writer);
/// <summary>
/// Writes the quoted identifier of this entity,
/// which could be @"..." or *
/// </summary>
/// <param name="writer"></param>
void WriteQuotedId(TextWriter writer);
/// <summary>
/// The location for reporting purposes.
@@ -133,5 +140,34 @@ namespace Semmle.Extraction
/// <returns>The entity.</returns>
public static Entity CreateEntity2<Type, Entity>(this ICachedEntityFactory<Type, Entity> factory, Context cx, Type init)
where Entity : ICachedEntity => cx.CreateEntity2(factory, init);
public static void DefineLabel(this IEntity entity, TextWriter trapFile)
{
trapFile.WriteLabel(entity);
trapFile.Write("=");
entity.WriteQuotedId(trapFile);
trapFile.WriteLine();
}
public static void DefineFreshLabel(this IEntity entity, TextWriter trapFile)
{
trapFile.WriteLabel(entity);
trapFile.WriteLine("=*");
}
/// <summary>
/// Generates a debug string for this entity.
/// </summary>
/// <param name="entity">The entity to view.</param>
/// <returns>The debug string.</returns>
public static string GetDebugLabel(this IEntity entity)
{
var writer = new StringWriter();
writer.WriteLabel(entity.Label.Value);
writer.Write('=');
entity.WriteQuotedId(writer);
return writer.ToString();
}
}
}

View File

@@ -1,4 +1,5 @@
using Semmle.Extraction.Entities;
using System.IO;
namespace Semmle.Extraction
{
@@ -20,6 +21,16 @@ namespace Semmle.Extraction
get; set;
}
public void WriteId(TextWriter writer)
{
writer.Write('*');
}
public void WriteQuotedId(TextWriter writer)
{
WriteId(writer);
}
public override string ToString() => Label.ToString();
public IId Id => FreshId.Instance;

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Semmle.Extraction
@@ -16,7 +17,7 @@ namespace Semmle.Extraction
/// <summary>
/// Appends this ID to the supplied trap builder.
/// </summary>
void AppendTo(ITrapBuilder tb);
void AppendTo(TextWriter tw);
}
/// <summary>
@@ -37,9 +38,9 @@ namespace Semmle.Extraction
public override int GetHashCode() => 0;
public void AppendTo(ITrapBuilder tb)
public void AppendTo(System.IO.TextWriter tw)
{
tb.Append("*");
tw.Write('*');
}
}
@@ -49,34 +50,41 @@ namespace Semmle.Extraction
/// </summary>
public class Key : IId
{
readonly IdTrapBuilder TrapBuilder;
readonly StringWriter TrapBuilder = new StringWriter();
/// <summary>
/// Creates a new key by concatenating the contents of the supplied arguments.
/// </summary>
public Key(params object[] args)
{
TrapBuilder = new IdTrapBuilder();
TrapBuilder = new StringWriter();
foreach (var arg in args)
TrapBuilder.Append(arg);
{
if (arg is IEntity)
{
var key = ((IEntity)arg).Label;
TrapBuilder.Write("{#");
TrapBuilder.Write(key.Value.ToString());
TrapBuilder.Write("}");
}
else
TrapBuilder.Write(arg.ToString());
}
}
/// <summary>
/// Creates a new key by applying the supplied action to an empty
/// trap builder.
/// </summary>
public Key(Action<ITrapBuilder> action)
public Key(Action<TextWriter> action)
{
TrapBuilder = new IdTrapBuilder();
action(TrapBuilder);
}
public override string ToString()
{
// Only implemented for debugging purposes
var tsb = new TrapStringBuilder();
AppendTo(tsb);
return tsb.ToString();
return TrapBuilder.ToString();
}
public override bool Equals(object obj)
@@ -84,37 +92,23 @@ namespace Semmle.Extraction
if (obj.GetType() != GetType())
return false;
var id = (Key)obj;
return id.TrapBuilder.Fragments.SequenceEqual(TrapBuilder.Fragments);
return TrapBuilder.ToString() == id.TrapBuilder.ToString();
}
public override int GetHashCode()
public override int GetHashCode() => TrapBuilder.ToString().GetHashCode();
public void AppendTo(TextWriter tb)
{
unchecked
{
int hash = 17;
foreach (var fragment in TrapBuilder.Fragments)
{
hash = hash * 23 + fragment.GetHashCode();
}
return hash;
}
tb.Write("@\"");
tb.Write(TrapBuilder.ToString());
tb.Write("\"");
}
public void AppendTo(ITrapBuilder tb)
{
tb.Append("@\"");
foreach (var fragment in TrapBuilder.Fragments)
tb.Append(fragment);
tb.Append("\"");
}
class IdTrapBuilder : ITrapBuilder
class IdTrapBuilder
{
readonly public List<string> Fragments = new List<string>();
public ITrapBuilder Append(object arg)
public void Append(object arg)
{
if (arg is IEntity)
{
@@ -125,17 +119,14 @@ namespace Semmle.Extraction
}
else
Fragments.Add(arg.ToString());
return this;
}
public ITrapBuilder Append(string arg)
public void Append(string arg)
{
Fragments.Add(arg);
return this;
}
public ITrapBuilder AppendLine()
public void AppendLine()
{
throw new NotImplementedException();
}
@@ -145,7 +136,7 @@ namespace Semmle.Extraction
/// <summary>
/// A label referencing an entity, of the form "#123".
/// </summary>
public struct Label : IId
public struct Label
{
public Label(int value) : this()
{
@@ -181,12 +172,13 @@ namespace Semmle.Extraction
/// Constructs a unique string for this label.
/// </summary>
/// <param name="tb">The trap builder used to store the result.</param>
public void AppendTo(ITrapBuilder tb)
public void AppendTo(System.IO.TextWriter tw)
{
if (!Valid)
throw new NullReferenceException("Attempt to use an invalid label");
tb.Append("#").Append(Value);
tw.Write('#');
tw.Write(Value);
}
}
}

View File

@@ -1,3 +1,5 @@
using System.IO;
namespace Semmle.Extraction
{
/// <summary>
@@ -34,9 +36,13 @@ namespace Semmle.Extraction
public Initializer UnderlyingObject => symbol;
public abstract IId Id
public abstract void WriteId(System.IO.TextWriter trapFile);
public void WriteQuotedId(TextWriter trapFile)
{
get;
trapFile.Write("@\"");
WriteId(trapFile);
trapFile.Write('\"');
}
public abstract bool NeedsPopulation

View File

@@ -1,35 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Semmle.Extraction
{
/// <summary>
/// A trap builder.
///
/// A trap builder is used to construct a string that is to be
/// persisted in a trap file (similar to how a <see cref="StringBuilder"/>
/// can be used to construct a string).
/// </summary>
public interface ITrapBuilder
{
/// <summary>
/// Append the given object to this trap builder.
/// </summary>
ITrapBuilder Append(object arg);
/// <summary>
/// Append the given string to this trap builder.
/// </summary>
ITrapBuilder Append(string arg);
/// <summary>
/// Append a newline to this trap builder.
/// </summary>
ITrapBuilder AppendLine();
}
public static class ITrapBuilderExtensions
public static class TrapBuilderExtensions
{
/// <summary>
/// Appends a [comma] separated list to a trap builder.
@@ -39,9 +15,9 @@ namespace Semmle.Extraction
/// <param name="separator">The separator string (e.g. ",")</param>
/// <param name="items">The list of items.</param>
/// <returns>The original trap builder (fluent interface).</returns>
public static ITrapBuilder AppendList<T>(this ITrapBuilder tb, string separator, IEnumerable<T> items)
public static TextWriter AppendList<T>(this TextWriter tb, string separator, IEnumerable<T> items) where T:IEntity
{
return tb.BuildList(separator, items, (x, tb0) => { tb0.Append(x); });
return tb.BuildList(separator, items, (x, tb0) => { tb0.WriteSubId(x); });
}
/// <summary>
@@ -53,44 +29,15 @@ namespace Semmle.Extraction
/// <param name="items">The list of items.</param>
/// <param name="action">The action on each item.</param>
/// <returns>The original trap builder (fluent interface).</returns>
public static ITrapBuilder BuildList<T>(this ITrapBuilder tb, string separator, IEnumerable<T> items, Action<T, ITrapBuilder> action)
public static TextWriter BuildList<T>(this TextWriter tb, string separator, IEnumerable<T> items, Action<T, TextWriter> action)
{
bool first = true;
foreach (var item in items)
{
if (first) first = false; else tb.Append(separator);
if (first) first = false; else tb.Write(separator);
action(item, tb);
}
return tb;
}
}
/// <summary>
/// A <see cref="StringBuilder"/> implementation of <see cref="ITrapBuilder"/>,
/// used for debugging only.
/// </summary>
public class TrapStringBuilder : ITrapBuilder
{
readonly StringBuilder StringBuilder = new StringBuilder();
public ITrapBuilder Append(object arg)
{
StringBuilder.Append(arg);
return this;
}
public ITrapBuilder Append(string arg)
{
StringBuilder.Append(arg);
return this;
}
public ITrapBuilder AppendLine()
{
StringBuilder.AppendLine();
return this;
}
public override string ToString() => StringBuilder.ToString();
}
}

View File

@@ -10,7 +10,7 @@ namespace Semmle.Extraction
{
public interface ITrapEmitter
{
void EmitToTrapBuilder(ITrapBuilder tb);
void EmitToTrapBuilder(TextWriter tw);
}
public sealed class TrapWriter : IDisposable
@@ -35,9 +35,6 @@ namespace Semmle.Extraction
public StreamWriter Writer => WriterLazy.Value;
readonly Lazy<TrapBuilder> BuilderLazy;
TrapBuilder Builder => BuilderLazy.Value;
readonly ILogger Logger;
public TrapWriter(ILogger logger, string outputfile, string trap, string archive, bool discardDuplicates)
@@ -68,7 +65,6 @@ namespace Semmle.Extraction
var compressionStream = new GZipStream(fileStream, CompressionMode.Compress);
return new StreamWriter(compressionStream, UTF8, 2000000);
});
BuilderLazy = new Lazy<TrapBuilder>(() => new TrapBuilder(WriterLazy.Value));
this.archive = archive;
this.discardDuplicates = discardDuplicates;
}
@@ -177,7 +173,7 @@ namespace Semmle.Extraction
public void Emit(ITrapEmitter emitter)
{
emitter.EmitToTrapBuilder(Builder);
emitter.EmitToTrapBuilder(Writer);
}
/// <summary>
@@ -196,34 +192,6 @@ namespace Semmle.Extraction
}
}
class TrapBuilder : ITrapBuilder
{
readonly StreamWriter StreamWriter;
public TrapBuilder(StreamWriter sw)
{
StreamWriter = sw;
}
public ITrapBuilder Append(object arg)
{
StreamWriter.Write(arg);
return this;
}
public ITrapBuilder Append(string arg)
{
StreamWriter.Write(arg);
return this;
}
public ITrapBuilder AppendLine()
{
StreamWriter.WriteLine();
return this;
}
}
/// <summary>
/// Attempts to archive the specified input file to the normal area of the source archive.
/// The file's path must be sufficiently short so as to render the path of its copy in the

View File

@@ -1,3 +1,4 @@
using System.IO;
using System.Linq;
namespace Semmle.Extraction
@@ -35,7 +36,7 @@ namespace Semmle.Extraction
array.Sum(encoding.GetByteCount) > maxStringBytes;
}
private static void WriteString(ITrapBuilder tb, string s) => tb.Append(EncodeString(s));
private static void WriteString(TextWriter tb, string s) => tb.Write(EncodeString(s));
/// <summary>
/// Truncates a string such that the output UTF8 does not exceed <paramref name="bytesRemaining"/> bytes.
@@ -73,7 +74,7 @@ namespace Semmle.Extraction
/// <param name="tb">The trapbuilder</param>
/// <param name="s">The string to output.</param>
/// <param name="bytesRemaining">The remaining bytes available to output.</param>
private static void WriteTruncatedString(ITrapBuilder tb, string s, ref int bytesRemaining)
private static void WriteTruncatedString(TextWriter tb, string s, ref int bytesRemaining)
{
WriteString(tb, TruncateString(s, ref bytesRemaining));
}
@@ -81,63 +82,64 @@ namespace Semmle.Extraction
/// <summary>
/// Constructs a unique string for this tuple.
/// </summary>
/// <param name="tb">The trap builder used to store the result.</param>
public void EmitToTrapBuilder(ITrapBuilder tb)
/// <param name="tw">The trap builder used to store the result.</param>
public void EmitToTrapBuilder(TextWriter tw)
{
tb.Append(Name).Append("(");
tw.Write(Name);
tw.Write("(");
int column = 0;
foreach (var a in Args)
{
if (column > 0) tb.Append(", ");
tw.WriteSeparator(", ", column++);
switch(a)
{
case Label l:
l.AppendTo(tb);
l.AppendTo(tw);
break;
case IEntity e:
e.Label.AppendTo(tb);
e.Label.AppendTo(tw);
break;
case string s:
tb.Append("\"");
tw.Write("\"");
if (NeedsTruncation(s))
{
// Slow path
int remaining = maxStringBytes;
WriteTruncatedString(tb, s, ref remaining);
WriteTruncatedString(tw, s, ref remaining);
}
else
{
// Fast path
WriteString(tb, s);
WriteString(tw, s);
}
tb.Append("\"");
tw.Write("\"");
break;
case System.Enum _:
tb.Append((int)a);
tw.Write((int)a);
break;
case int i:
tb.Append(i);
tw.Write(i);
break;
case float f:
tb.Append(f.ToString("0.#####e0")); // Trap importer won't accept ints
tw.Write(f.ToString("0.#####e0")); // Trap importer won't accept ints
break;
case string[] array:
tb.Append("\"");
tw.Write('\"');
if (NeedsTruncation(array))
{
// Slow path
int remaining = maxStringBytes;
foreach (var element in array)
WriteTruncatedString(tb, element, ref remaining);
WriteTruncatedString(tw, element, ref remaining);
}
else
{
// Fast path
foreach (var element in array)
WriteString(tb, element);
WriteString(tw, element);
}
tb.Append("\"");
tw.Write('\"');
break;
case null:
throw new InternalError($"Attempt to write a null argument tuple {Name} at column {column}");
@@ -147,14 +149,13 @@ namespace Semmle.Extraction
++column;
}
tb.Append(")");
tb.AppendLine();
tw.WriteLine(")");
}
public override string ToString()
{
// Only implemented for debugging purposes
var tsb = new TrapStringBuilder();
var tsb = new StringWriter();
EmitToTrapBuilder(tsb);
return tsb.ToString();
}