mirror of
https://github.com/github/codeql.git
synced 2026-04-27 01:35:13 +02:00
C#: Remove ITrapBuilder in favour of TextWriter.
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user