C#: Compare CIL entities directly by handle rather than by label.

C#: Remove IDs from the CIL extractor and make consistent with C# extractor.
C#: Fix method collisions.
This commit is contained in:
Calum Grant 2019-08-13 18:50:25 +01:00
Родитель 685c494bcb
Коммит b500a02b1e
34 изменённых файлов: 878 добавлений и 642 удалений

Просмотреть файл

@ -31,7 +31,7 @@ namespace Semmle.Extraction.CIL
mdReader = peReader.GetMetadataReader();
TypeSignatureDecoder = new Entities.TypeSignatureDecoder(this);
globalNamespace = new Lazy<Entities.Namespace>(() => Populate(new Entities.Namespace(this, GetId(""), null)));
globalNamespace = new Lazy<Entities.Namespace>(() => Populate(new Entities.Namespace(this, "", null)));
systemNamespace = new Lazy<Entities.Namespace>(() => Populate(new Entities.Namespace(this, "System")));
genericHandleFactory = new CachedFunction<GenericContext, Handle, ILabelledEntity>(CreateGenericHandle);
namespaceFactory = new CachedFunction<StringHandle, Entities.Namespace>(n => CreateNamespace(mdReader.GetString(n)));
@ -42,9 +42,6 @@ namespace Semmle.Extraction.CIL
defaultGenericContext = new EmptyContext(this);
var def = mdReader.GetAssemblyDefinition();
AssemblyPrefix = GetId(def.Name) + "_" + def.Version.ToString() + "::";
if (extractPdbs)
{
pdb = PDB.PdbReader.Create(assemblyPath, peReader);
@ -75,7 +72,14 @@ namespace Semmle.Extraction.CIL
}
}
public readonly Id AssemblyPrefix;
public void WriteAssemblyPrefix(TextWriter trapFile)
{
var def = mdReader.GetAssemblyDefinition();
trapFile.Write(GetString(def.Name));
trapFile.Write('_');
trapFile.Write(def.Version.ToString());
trapFile.Write("::");
}
public readonly Entities.TypeSignatureDecoder TypeSignatureDecoder;

Просмотреть файл

@ -4,6 +4,7 @@ using System.Collections.Generic;
using Semmle.Util.Logging;
using System;
using Semmle.Extraction.Entities;
using System.IO;
namespace Semmle.Extraction.CIL.Entities
{
@ -20,8 +21,6 @@ namespace Semmle.Extraction.CIL.Entities
/// </summary>
public class Assembly : LabelledEntity, IAssembly
{
public override Id IdSuffix => suffix;
readonly File file;
readonly AssemblyName assemblyName;
@ -38,12 +37,24 @@ namespace Semmle.Extraction.CIL.Entities
if (!def.PublicKey.IsNil)
assemblyName.SetPublicKey(cx.mdReader.GetBlobBytes(def.PublicKey));
ShortId = cx.GetId(FullName) + "#file:///" + cx.assemblyPath.Replace("\\", "/");
file = new File(cx, cx.assemblyPath);
}
static readonly Id suffix = new StringId(";assembly");
public override void WriteId(TextWriter trapFile)
{
trapFile.Write(FullName);
trapFile.Write("#file:///");
trapFile.Write(cx.assemblyPath.Replace("\\", "/"));
}
public override bool Equals(object obj)
{
return GetType() == obj.GetType() && Equals(file, ((Assembly)obj).file);
}
public override int GetHashCode() => 7 * file.GetHashCode();
public override string IdSuffix => ";assembly";
string FullName => assemblyName.GetPublicKey() is null ? assemblyName.FullName + ", PublicKeyToken=null" : assemblyName.FullName;

Просмотреть файл

@ -14,8 +14,9 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// Entity representing a CIL attribute.
/// </summary>
class Attribute : UnlabelledEntity, IAttribute
sealed class Attribute : UnlabelledEntity, IAttribute
{
readonly CustomAttributeHandle handle;
readonly CustomAttribute attrib;
readonly IEntity @object;
@ -25,6 +26,13 @@ namespace Semmle.Extraction.CIL.Entities
this.@object = @object;
}
public override bool Equals(object obj)
{
return obj is Attribute attribute && handle.Equals(attribute.handle);
}
public override int GetHashCode() => handle.GetHashCode();
public override IEnumerable<IExtractionProduct> Contents
{
get
@ -78,7 +86,7 @@ namespace Semmle.Extraction.CIL.Entities
readonly Context cx;
public CustomAttributeDecoder(Context cx) { this.cx = cx; }
public Type GetPrimitiveType(PrimitiveTypeCode typeCode) => cx.Populate(new PrimitiveType(cx, typeCode));
public Type GetPrimitiveType(PrimitiveTypeCode typeCode) => cx.Create(typeCode);
public Type GetSystemType() => throw new NotImplementedException();

Просмотреть файл

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection.Metadata;
namespace Semmle.Extraction.CIL.Entities
@ -13,19 +14,35 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// An event entity.
/// </summary>
class Event : LabelledEntity, IEvent
sealed class Event : LabelledEntity, IEvent
{
readonly EventDefinitionHandle handle;
readonly Type parent;
readonly EventDefinition ed;
static readonly Id suffix = CIL.Id.Create(";cil-event");
public Event(Context cx, Type parent, EventDefinitionHandle handle) : base(cx)
{
this.handle = handle;
this.parent = parent;
ed = cx.mdReader.GetEventDefinition(handle);
ShortId = parent.ShortId + cx.Dot + cx.ShortName(ed.Name) + suffix;
}
public override void WriteId(TextWriter trapFile)
{
parent.WriteId(trapFile);
trapFile.Write('.');
trapFile.Write(cx.ShortName(ed.Name));
}
public override string IdSuffix => ";cil-event";
public override bool Equals(object obj)
{
return obj is Event e && handle.Equals(e.handle);
}
public override int GetHashCode() => handle.GetHashCode();
public override IEnumerable<IExtractionProduct> Contents
{
get
@ -61,7 +78,5 @@ namespace Semmle.Extraction.CIL.Entities
yield return c;
}
}
public override Id IdSuffix => suffix;
}
}

Просмотреть файл

@ -37,28 +37,24 @@ namespace Semmle.Extraction.CIL.Entities
public Label Label { get; set; }
public IId Id => ShortId + IdSuffix;
public void WriteId(TextWriter trapFile)
{
trapFile.WriteIId(Id);
trapFile.WriteSubId(DeclaringType);
trapFile.Write('.');
trapFile.Write(Name);
}
public void WriteQuotedId(TextWriter trapFile)
{
trapFile.Write("@\"");
WriteId(trapFile);
trapFile.Write(IdSuffix);
trapFile.Write('\"');
}
public Id IdSuffix => fieldSuffix;
public string IdSuffix => ";cil-field";
static readonly StringId fieldSuffix = new StringId(";cil-field");
public Id ShortId
{
get; set;
}
public abstract Id Name { get; }
public abstract string Name { get; }
public abstract Type DeclaringType { get; }
@ -70,7 +66,7 @@ namespace Semmle.Extraction.CIL.Entities
{
get
{
yield return Tuples.cil_field(this, DeclaringType, Name.Value, Type);
yield return Tuples.cil_field(this, DeclaringType, Name, Type);
}
}
@ -93,9 +89,15 @@ namespace Semmle.Extraction.CIL.Entities
this.handle = handle;
this.gc = gc;
fd = cx.mdReader.GetFieldDefinition(handle);
ShortId = DeclaringType.ShortId + cx.Dot + Name;
}
public override bool Equals(object obj)
{
return obj is DefinitionField field && handle.Equals(field.handle);
}
public override int GetHashCode() => handle.GetHashCode();
public override IEnumerable<IExtractionProduct> Contents
{
get
@ -125,7 +127,7 @@ namespace Semmle.Extraction.CIL.Entities
}
}
public override Id Name => cx.GetId(fd.Name);
public override string Name => cx.GetString(fd.Name);
public override Type DeclaringType => (Type)cx.Create(fd.GetDeclaringType());
@ -138,19 +140,30 @@ namespace Semmle.Extraction.CIL.Entities
sealed class MemberReferenceField : Field
{
readonly MemberReferenceHandle Handle;
readonly MemberReference mr;
readonly GenericContext gc;
readonly Type declType;
public MemberReferenceField(GenericContext gc, MemberReferenceHandle handle) : base(gc.cx)
{
Handle = handle;
this.gc = gc;
mr = cx.mdReader.GetMemberReference(handle);
declType = (Type)cx.CreateGeneric(gc, mr.Parent);
ShortId = declType.ShortId + cx.Dot + Name;
}
public override Id Name => cx.GetId(mr.Name);
public override bool Equals(object obj)
{
return obj is MemberReferenceField field && Handle.Equals(field.Handle);
}
public override int GetHashCode()
{
return Handle.GetHashCode();
}
public override string Name => cx.GetString(mr.Name);
public override Type DeclaringType => declType;

Просмотреть файл

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
namespace Semmle.Extraction.CIL.Entities
{
@ -17,9 +18,20 @@ namespace Semmle.Extraction.CIL.Entities
public File(Context cx, string path) : base(cx)
{
this.path = Semmle.Extraction.Entities.File.PathAsDatabaseString(path);
ShortId = new StringId(Semmle.Extraction.Entities.File.PathAsDatabaseId(path));
}
public override void WriteId(TextWriter trapFile)
{
trapFile.Write(Semmle.Extraction.Entities.File.PathAsDatabaseId(path));
}
public override bool Equals(object obj)
{
return GetType() == obj.GetType() && path == ((File)obj).path;
}
public override int GetHashCode() => 11 * path.GetHashCode();
public override IEnumerable<IExtractionProduct> Contents
{
get
@ -31,9 +43,7 @@ namespace Semmle.Extraction.CIL.Entities
}
}
public override Id IdSuffix => suffix;
static readonly Id suffix = new StringId(";sourcefile");
public override string IdSuffix => ";sourcefile";
}
public class PdbSourceFile : File

Просмотреть файл

@ -7,17 +7,22 @@ namespace Semmle.Extraction.CIL.Entities
{
}
public class Folder : LabelledEntity, IFolder
public sealed class Folder : LabelledEntity, IFolder
{
readonly string path;
public Folder(Context cx, string path) : base(cx)
{
this.path = path;
ShortId = new StringId(Semmle.Extraction.Entities.File.PathAsDatabaseId(path));
}
static readonly Id suffix = new StringId(";folder");
public override void WriteId(TextWriter trapFile)
{
trapFile.Write(Semmle.Extraction.Entities.File.PathAsDatabaseId(path));
}
public override string IdSuffix => ";folder";
public override IEnumerable<IExtractionProduct> Contents
{
@ -37,6 +42,11 @@ namespace Semmle.Extraction.CIL.Entities
}
}
public override Id IdSuffix => suffix;
public override bool Equals(object obj)
{
return obj is Folder folder && path == folder.path;
}
public override int GetHashCode() => path.GetHashCode();
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
namespace Semmle.Extraction.CIL.Entities
{
@ -17,12 +18,16 @@ namespace Semmle.Extraction.CIL.Entities
method = m;
index = i;
type = t;
ShortId = CIL.Id.Create(method.Label) + underscore + index;
}
static readonly Id underscore = CIL.Id.Create("_");
static readonly Id suffix = CIL.Id.Create(";cil-local");
public override Id IdSuffix => suffix;
public override void WriteId(TextWriter trapFile)
{
trapFile.WriteSubId(method);
trapFile.Write('_');
trapFile.Write(index);
}
public override string IdSuffix => ";cil-local";
public override IEnumerable<IExtractionProduct> Contents
{

Просмотреть файл

@ -7,6 +7,7 @@ using System.Reflection;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.IO;
using Semmle.Util;
namespace Semmle.Extraction.CIL.Entities
{
@ -42,34 +43,40 @@ namespace Semmle.Extraction.CIL.Entities
public virtual IList<LocalVariable> LocalVariables => throw new NotImplementedException();
public IList<Parameter> Parameters { get; private set; }
static readonly Id tick = CIL.Id.Create("`");
static readonly Id space = CIL.Id.Create(" ");
static readonly Id dot = CIL.Id.Create(".");
static readonly Id open = CIL.Id.Create("(");
static readonly Id close = CIL.Id.Create(")");
public override void WriteId(TextWriter trapFile) => MakeMethodId(trapFile, DeclaringType, NameLabel);
internal protected Id MakeMethodId(Type parent, Id methodName)
public abstract string NameLabel { get; }
internal protected void MakeMethodId(TextWriter trapFile, Type parent, string methodName)
{
var id = signature.ReturnType.MakeId(this) + space + parent.ShortId + dot + methodName;
signature.ReturnType.WriteId(trapFile, this);
trapFile.Write(' ');
parent.WriteId(trapFile);
trapFile.Write('.');
trapFile.Write(methodName);
if (signature.GenericParameterCount > 0)
{
id += tick + signature.GenericParameterCount;
trapFile.Write('`');
trapFile.Write(signature.GenericParameterCount);
}
id += open + CIL.Id.CommaSeparatedList(signature.ParameterTypes.Select(p => p.MakeId(this))) + close;
return id;
trapFile.Write('(');
int index = 0;
foreach (var param in signature.ParameterTypes)
{
trapFile.WriteSeparator(",", index++);
param.WriteId(trapFile, this);
}
trapFile.Write(')');
}
protected MethodTypeParameter[] genericParams;
protected Type declaringType;
protected GenericContext gc;
protected MethodSignature<ITypeSignature> signature;
protected Id name;
protected string name;
static readonly StringId methodSuffix = new StringId(";cil-method");
public override Id IdSuffix => methodSuffix;
public override string IdSuffix => ";cil-method";
protected void PopulateParameters(IEnumerable<Type> parameterTypes)
{
@ -134,7 +141,7 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// A definition method - a method defined in the current assembly.
/// </summary>
class DefinitionMethod : Method, IMember
sealed class DefinitionMethod : Method, IMember
{
readonly Handle handle;
readonly MethodDefinition md;
@ -151,22 +158,30 @@ namespace Semmle.Extraction.CIL.Entities
md = cx.mdReader.GetMethodDefinition(handle);
this.gc = gc;
this.handle = handle;
name = cx.GetId(md.Name);
name = cx.GetString(md.Name);
declaringType = (Type)cx.CreateGeneric(this, md.GetDeclaringType());
signature = md.DecodeSignature(new SignatureDecoder(), this);
ShortId = MakeMethodId(declaringType, name);
methodDebugInformation = cx.GetMethodDebugInformation(handle);
}
public override bool Equals(object obj)
{
return obj is DefinitionMethod method && handle.Equals(method.handle);
}
public override int GetHashCode() => handle.GetHashCode();
public override bool IsStatic => !signature.Header.IsInstance;
public override Type DeclaringType => declaringType;
public override string Name => cx.ShortName(md.Name);
public override string NameLabel => name;
/// <summary>
/// Holds if this method has bytecode.
/// </summary>
@ -382,8 +397,9 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// This is a late-bound reference to a method.
/// </summary>
class MemberReferenceMethod : Method
sealed class MemberReferenceMethod : Method
{
readonly MemberReferenceHandle handle;
readonly MemberReference mr;
readonly Type declType;
readonly GenericContext parent;
@ -391,6 +407,7 @@ namespace Semmle.Extraction.CIL.Entities
public MemberReferenceMethod(GenericContext gc, MemberReferenceHandle handle) : base(gc)
{
this.handle = handle;
this.gc = gc;
mr = cx.mdReader.GetMemberReference(handle);
@ -399,19 +416,31 @@ namespace Semmle.Extraction.CIL.Entities
parent = (GenericContext)cx.CreateGeneric(gc, mr.Parent);
var parentMethod = parent as Method;
var nameLabel = cx.GetId(mr.Name);
nameLabel = cx.GetString(mr.Name);
declType = parentMethod == null ? parent as Type : parentMethod.DeclaringType;
if (declType is null)
throw new InternalError("Parent context of method is not a type");
ShortId = MakeMethodId(declType, nameLabel);
var typeSourceDeclaration = declType.SourceDeclaration;
sourceDeclaration = typeSourceDeclaration == declType ? (Method)this : typeSourceDeclaration.LookupMethod(mr.Name, mr.Signature);
}
private readonly string nameLabel;
public override string NameLabel => nameLabel;
public override bool Equals(object obj)
{
return obj is MemberReferenceMethod method && handle.Equals(method.handle);
}
public override int GetHashCode()
{
return handle.GetHashCode();
}
public override Method SourceDeclaration => sourceDeclaration;
public override bool IsStatic => !signature.Header.IsInstance;
@ -451,26 +480,46 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// A constructed method.
/// </summary>
class MethodSpecificationMethod : Method
sealed class MethodSpecificationMethod : Method
{
readonly MethodSpecificationHandle handle;
readonly MethodSpecification ms;
readonly Method unboundMethod;
readonly ImmutableArray<Type> typeParams;
public MethodSpecificationMethod(GenericContext gc, MethodSpecificationHandle handle) : base(gc)
{
this.handle = handle;
ms = cx.mdReader.GetMethodSpecification(handle);
typeParams = ms.DecodeSignature(cx.TypeSignatureDecoder, gc);
unboundMethod = (Method)cx.CreateGeneric(gc, ms.Method);
declaringType = unboundMethod.DeclaringType;
ShortId = unboundMethod.ShortId + openAngle + CIL.Id.CommaSeparatedList(typeParams.Select(p => p.ShortId)) + closeAngle;
}
static readonly Id openAngle = CIL.Id.Create("<");
static readonly Id closeAngle = CIL.Id.Create(">");
public override void WriteId(TextWriter trapFile)
{
unboundMethod.WriteId(trapFile);
trapFile.Write('<');
int index = 0;
foreach(var param in typeParams)
{
trapFile.WriteSeparator(",", index++);
trapFile.WriteSubId(param);
}
trapFile.Write('>');
}
public override string NameLabel => throw new NotImplementedException();
public override bool Equals(object obj)
{
return obj is MethodSpecificationMethod method && handle.Equals(method.handle) && typeParams.SequenceEqual(method.typeParams);
}
public override int GetHashCode()
{
return handle.GetHashCode() * 11 + typeParams.SequenceHash();
}
public override Method SourceDeclaration => unboundMethod;

Просмотреть файл

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using Semmle.Extraction.Entities;
namespace Semmle.Extraction.CIL.Entities
@ -14,28 +15,43 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// A namespace.
/// </summary>
public class Namespace : TypeContainer, INamespace
public sealed class Namespace : TypeContainer, INamespace
{
public Namespace ParentNamespace;
public readonly StringId Name;
public readonly string Name;
public bool IsGlobalNamespace => ParentNamespace == null;
static readonly Id suffix = CIL.Id.Create(";namespace");
public override string IdSuffix => ";namespace";
public Id CreateId
public override void WriteId(TextWriter trapFile)
{
get
if (ParentNamespace != null && !ParentNamespace.IsGlobalNamespace)
{
if (ParentNamespace != null && !ParentNamespace.IsGlobalNamespace)
{
return ParentNamespace.ShortId + cx.Dot + Name;
}
return Name;
ParentNamespace.WriteId(trapFile);
trapFile.Write('.');
}
trapFile.Write(Name);
}
public override Id IdSuffix => suffix;
public override bool Equals(object obj)
{
if (obj is Namespace ns && Name == ns.Name)
{
if (ParentNamespace is null)
return ns.ParentNamespace is null;
if (!(ns.ParentNamespace is null))
return ParentNamespace.Equals(ns.ParentNamespace);
}
return false;
}
public override int GetHashCode()
{
int h = ParentNamespace is null ? 19 : ParentNamespace.GetHashCode();
return 13 * h + Name.GetHashCode();
}
public override IEnumerable<Type> TypeParameters => throw new NotImplementedException();
@ -54,22 +70,21 @@ namespace Semmle.Extraction.CIL.Entities
return i == -1 ? cx.GlobalNamespace : cx.Populate(new Namespace(cx, fqn.Substring(0, i)));
}
public Namespace(Context cx, string fqn) : this(cx, cx.GetId(parseNamespaceName(fqn)), createParentNamespace(cx, fqn))
public Namespace(Context cx, string fqn) : this(cx, parseNamespaceName(fqn), createParentNamespace(cx, fqn))
{
}
public Namespace(Context cx, StringId name, Namespace parent) : base(cx)
public Namespace(Context cx, string name, Namespace parent) : base(cx)
{
Name = name;
ParentNamespace = parent;
ShortId = CreateId;
}
public override IEnumerable<IExtractionProduct> Contents
{
get
{
yield return Tuples.namespaces(this, Name.Value);
yield return Tuples.namespaces(this, Name);
if (!IsGlobalNamespace)
yield return Tuples.parent_namespace(this, ParentNamespace);
}

Просмотреть файл

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
namespace Semmle.Extraction.CIL.Entities
{
@ -12,7 +13,7 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// A parameter entity.
/// </summary>
class Parameter : LabelledEntity, IParameter
sealed class Parameter : LabelledEntity, IParameter
{
readonly Method method;
readonly int index;
@ -23,14 +24,26 @@ namespace Semmle.Extraction.CIL.Entities
method = m;
index = i;
type = t;
ShortId = openCurly + method.Label.Value + closeCurly + index;
}
static readonly Id parameterSuffix = CIL.Id.Create(";cil-parameter");
static readonly Id openCurly = CIL.Id.Create("{#");
static readonly Id closeCurly = CIL.Id.Create("}_");
public override void WriteId(TextWriter trapFile)
{
trapFile.WriteSubId(method);
trapFile.Write('_');
trapFile.Write(index);
}
public override Id IdSuffix => parameterSuffix;
public override bool Equals(object obj)
{
return obj is Parameter param && method.Equals(param.method) && index == param.index;
}
public override int GetHashCode()
{
return 23 * method.GetHashCode() + index;
}
public override string IdSuffix => ";cil-parameter";
public override IEnumerable<IExtractionProduct> Contents
{

Просмотреть файл

@ -2,6 +2,7 @@
using System.Reflection.Metadata;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.IO;
namespace Semmle.Extraction.CIL.Entities
{
@ -15,25 +16,44 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// A property.
/// </summary>
class Property : LabelledEntity, IProperty
sealed class Property : LabelledEntity, IProperty
{
readonly Handle handle;
readonly Type type;
readonly PropertyDefinition pd;
static readonly Id suffix = CIL.Id.Create(";cil-property");
public override string IdSuffix => ";cil-property";
readonly GenericContext gc;
public Property(GenericContext gc, Type type, PropertyDefinitionHandle handle) : base(gc.cx)
{
this.gc = gc;
this.handle = handle;
pd = cx.mdReader.GetPropertyDefinition(handle);
this.type = type;
var id = type.ShortId + gc.cx.Dot + cx.ShortName(pd.Name);
var signature = pd.DecodeSignature(new SignatureDecoder(), gc);
id += "(" + CIL.Id.CommaSeparatedList(signature.ParameterTypes.Select(p => p.MakeId(gc))) + ")";
ShortId = id;
}
public override void WriteId(TextWriter trapFile)
{
trapFile.WriteSubId(type);
trapFile.Write('.');
trapFile.Write(cx.GetString(pd.Name));
trapFile.Write("(");
int index=0;
var signature = pd.DecodeSignature(new SignatureDecoder(), gc);
foreach (var param in signature.ParameterTypes)
{
trapFile.WriteSeparator(",", index++);
param.WriteId(trapFile, gc);
}
trapFile.Write(")");
}
public override bool Equals(object obj)
{
return obj is Property property && Equals(handle, property.handle);
}
public override int GetHashCode() => handle.GetHashCode();
public override IEnumerable<IExtractionProduct> Contents
{
get
@ -62,7 +82,5 @@ namespace Semmle.Extraction.CIL.Entities
yield return c;
}
}
public override Id IdSuffix => suffix;
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using Semmle.Extraction.PDB;
namespace Semmle.Extraction.CIL.Entities
@ -16,12 +17,27 @@ namespace Semmle.Extraction.CIL.Entities
{
this.location = location;
file = cx.CreateSourceFile(location.File);
ShortId = file.ShortId + separator + new IntId(location.StartLine) + separator + new IntId(location.StartColumn) + separator + new IntId(location.EndLine) + separator + new IntId(location.EndColumn);
}
static readonly Id suffix = new StringId(";sourcelocation");
static readonly Id separator = new StringId(",");
public override void WriteId(TextWriter trapFile)
{
file.WriteId(trapFile);
trapFile.Write(',');
trapFile.Write(location.StartLine);
trapFile.Write(',');
trapFile.Write(location.StartColumn);
trapFile.Write(',');
trapFile.Write(location.EndLine);
trapFile.Write(',');
trapFile.Write(location.EndColumn);
}
public override bool Equals(object obj)
{
return obj is PdbSourceLocation l && location.Equals(l.location);
}
public override int GetHashCode() => location.GetHashCode();
public override IEnumerable<IExtractionProduct> Contents
{
@ -32,6 +48,6 @@ namespace Semmle.Extraction.CIL.Entities
}
}
public override Id IdSuffix => suffix;
public override string IdSuffix => ";sourcelocation";
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -58,8 +58,6 @@ namespace Semmle.Extraction.CIL
public Microsoft.CodeAnalysis.Location ReportingLocation => throw new NotImplementedException();
public virtual IId Id => FreshId.Instance;
public virtual void Extract(Context cx2)
{
cx2.Extract(this);
@ -86,18 +84,16 @@ namespace Semmle.Extraction.CIL
public Label Label { get; set; }
public Microsoft.CodeAnalysis.Location ReportingLocation => throw new NotImplementedException();
public Id ShortId { get; set; }
public abstract Id IdSuffix { get; }
public IId Id => ShortId + IdSuffix;
public abstract void WriteId(System.IO.TextWriter trapFile);
public void WriteId(System.IO.TextWriter trapFile)
{
trapFile.WriteIId(Id);
}
public abstract string IdSuffix { get; }
public void WriteQuotedId(TextWriter trapFile)
{
trapFile.Write("@\"");
WriteId(trapFile);
trapFile.Write(IdSuffix);
trapFile.Write('\"');
}
public void Extract(Context cx2)
@ -112,7 +108,14 @@ namespace Semmle.Extraction.CIL
this.cx = cx;
}
public override string ToString() => Id.ToString();
public override string ToString()
{
using (var writer = new StringWriter())
{
WriteQuotedId(writer);
return writer.ToString();
}
}
TrapStackBehaviour IEntity.TrapStackBehaviour => TrapStackBehaviour.NoLabel;
}
@ -122,8 +125,6 @@ namespace Semmle.Extraction.CIL
/// </summary>
public interface ILabelledEntity : IExtractedEntity
{
Id ShortId { get; set; }
Id IdSuffix { get; }
}
/// <summary>

Просмотреть файл

@ -1,5 +1,7 @@
using System;
using Semmle.Extraction.CIL.Entities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.Metadata;
namespace Semmle.Extraction.CIL
@ -9,38 +11,78 @@ namespace Semmle.Extraction.CIL
/// </summary>
public partial class Context
{
readonly Dictionary<Id, (Label, Id)> ids = new Dictionary<Id, (Label, Id)>();
readonly Dictionary<object, Label> ids = new Dictionary<object, Label>();
public T Populate<T>(T e) where T : ILabelledEntity
{
Id id = e.ShortId;
if(e.Label.Valid)
{
return e; // Already populated
}
if (ids.TryGetValue(id, out var existing))
if (ids.TryGetValue(e, out var existing))
{
// It exists already
e.Label = existing.Item1;
e.ShortId = existing.Item2; // Reuse ID for efficiency
e.Label = existing;
}
else
{
e.Label = cx.GetNewLabel();
cx.DefineLabel(e, cx.TrapWriter.Writer);
ids.Add(id, (e.Label, id));
ids.Add(e, e.Label);
cx.PopulateLater(() =>
{
foreach (var c in e.Contents)
c.Extract(this);
});
#if DEBUG_LABELS
using (var writer = new StringWriter())
{
e.WriteId(writer);
var id = writer.ToString();
if (debugLabels.TryGetValue(id, out ILabelledEntity previousEntity))
{
cx.Extractor.Message(new Message("Duplicate trap ID", id, null, severity: Util.Logging.Severity.Warning));
}
else
{
debugLabels.Add(id, e);
}
}
#endif
}
return e;
}
#if DEBUG_LABELS
Dictionary<string, ILabelledEntity> debugLabels = new Dictionary<string, ILabelledEntity>();
#endif
public IExtractedEntity Create(Handle h)
{
var entity = CreateGeneric(defaultGenericContext, h);
return entity;
}
// Lazily cache primitive types.
PrimitiveType[] primitiveTypes = new PrimitiveType[(int)PrimitiveTypeCode.Object + 1];
public PrimitiveType Create(PrimitiveTypeCode code)
{
PrimitiveType e = primitiveTypes[(int)code];
if(e is null)
{
e = new PrimitiveType(this, code);
e.Label = cx.GetNewLabel();
cx.DefineLabel(e, cx.TrapWriter.Writer);
primitiveTypes[(int)code] = e;
}
return e;
}
/// <summary>
/// Creates an entity from a Handle in a GenericContext.
/// The type of the returned entity depends on the type of the handle.
@ -63,25 +105,27 @@ namespace Semmle.Extraction.CIL
switch (handle.Kind)
{
case HandleKind.MethodDefinition:
entity = new Entities.DefinitionMethod(gc, (MethodDefinitionHandle)handle);
entity = new DefinitionMethod(gc, (MethodDefinitionHandle)handle);
break;
case HandleKind.MemberReference:
entity = Create(gc, (MemberReferenceHandle)handle);
break;
case HandleKind.MethodSpecification:
entity = new Entities.MethodSpecificationMethod(gc, (MethodSpecificationHandle)handle);
entity = new MethodSpecificationMethod(gc, (MethodSpecificationHandle)handle);
break;
case HandleKind.FieldDefinition:
entity = new Entities.DefinitionField(gc, (FieldDefinitionHandle)handle);
entity = new DefinitionField(gc, (FieldDefinitionHandle)handle);
break;
case HandleKind.TypeReference:
entity = new Entities.TypeReferenceType(this, (TypeReferenceHandle)handle);
var tr = new TypeReferenceType(this, (TypeReferenceHandle)handle);
if (tr.TryGetPrimitiveType(gc.cx, out var pt))
return pt;
entity = tr;
break;
case HandleKind.TypeSpecification:
entity = new Entities.TypeSpecificationType(gc, (TypeSpecificationHandle)handle);
break;
return Entities.Type.DecodeType(gc, (TypeSpecificationHandle)handle);
case HandleKind.TypeDefinition:
entity = new Entities.TypeDefinitionType(this, (TypeDefinitionHandle)handle);
entity = new TypeDefinitionType(this, (TypeDefinitionHandle)handle);
break;
default:
throw new InternalError("Unhandled handle kind " + handle.Kind);
@ -97,123 +141,90 @@ namespace Semmle.Extraction.CIL
switch (mr.GetKind())
{
case MemberReferenceKind.Method:
return new Entities.MemberReferenceMethod(gc, handle);
return new MemberReferenceMethod(gc, handle);
case MemberReferenceKind.Field:
return new Entities.MemberReferenceField(gc, handle);
return new MemberReferenceField(gc, handle);
default:
throw new InternalError("Unhandled member reference handle");
}
}
#region Strings
readonly Dictionary<StringHandle, StringId> stringHandleIds = new Dictionary<StringHandle, StringId>();
readonly Dictionary<string, StringId> stringIds = new Dictionary<string, StringId>();
/// <summary>
/// Return an ID containing the given string.
/// Gets the string for a string handle.
/// </summary>
/// <param name="h">The string handle.</param>
/// <returns>An ID.</returns>
public StringId GetId(StringHandle h)
{
StringId result;
if (!stringHandleIds.TryGetValue(h, out result))
{
result = new StringId(mdReader.GetString(h));
stringHandleIds.Add(h, result);
}
return result;
}
/// <returns>The string.</returns>
public string GetString(StringHandle h) => mdReader.GetString(h);
public readonly StringId Dot = new StringId(".");
#region Namespaces
/// <summary>
/// Gets an ID containing the given string.
/// Caches existing IDs for more compact storage.
/// </summary>
/// <param name="str">The string.</param>
/// <returns>An ID containing the string.</returns>
public StringId GetId(string str)
{
StringId result;
if (!stringIds.TryGetValue(str, out result))
{
result = new StringId(str);
stringIds.Add(str, result);
}
return result;
}
#endregion
readonly CachedFunction<StringHandle, Namespace> namespaceFactory;
#region Namespaces
public Namespace CreateNamespace(StringHandle fqn) => namespaceFactory[fqn];
readonly CachedFunction<StringHandle, Entities.Namespace> namespaceFactory;
public Entities.Namespace CreateNamespace(StringHandle fqn) => namespaceFactory[fqn];
readonly Lazy<Entities.Namespace> globalNamespace, systemNamespace;
readonly Lazy<Namespace> globalNamespace, systemNamespace;
/// <summary>
/// The entity representing the global namespace.
/// </summary>
public Entities.Namespace GlobalNamespace => globalNamespace.Value;
public Namespace GlobalNamespace => globalNamespace.Value;
/// <summary>
/// The entity representing the System namespace.
/// </summary>
public Entities.Namespace SystemNamespace => systemNamespace.Value;
public Namespace SystemNamespace => systemNamespace.Value;
/// <summary>
/// Creates a namespace from a fully-qualified name.
/// </summary>
/// <param name="fqn">The fully-qualified namespace name.</param>
/// <returns>The namespace entity.</returns>
Entities.Namespace CreateNamespace(string fqn) => Populate(new Entities.Namespace(this, fqn));
Namespace CreateNamespace(string fqn) => Populate(new Namespace(this, fqn));
readonly CachedFunction<NamespaceDefinitionHandle, Entities.Namespace> namespaceDefinitionFactory;
readonly CachedFunction<NamespaceDefinitionHandle, Namespace> namespaceDefinitionFactory;
/// <summary>
/// Creates a namespace from a namespace handle.
/// </summary>
/// <param name="handle">The handle of the namespace.</param>
/// <returns>The namespace entity.</returns>
public Entities.Namespace Create(NamespaceDefinitionHandle handle) => namespaceDefinitionFactory[handle];
public Namespace Create(NamespaceDefinitionHandle handle) => namespaceDefinitionFactory[handle];
Entities.Namespace CreateNamespace(NamespaceDefinitionHandle handle)
Namespace CreateNamespace(NamespaceDefinitionHandle handle)
{
if (handle.IsNil) return GlobalNamespace;
NamespaceDefinition nd = mdReader.GetNamespaceDefinition(handle);
return Populate(new Entities.Namespace(this, GetId(nd.Name), Create(nd.Parent)));
return Populate(new Namespace(this, GetString(nd.Name), Create(nd.Parent)));
}
#endregion
#endregion
#region Locations
readonly CachedFunction<PDB.ISourceFile, Entities.PdbSourceFile> sourceFiles;
readonly CachedFunction<string, Entities.Folder> folders;
readonly CachedFunction<PDB.Location, Entities.PdbSourceLocation> sourceLocations;
#region Locations
readonly CachedFunction<PDB.ISourceFile, PdbSourceFile> sourceFiles;
readonly CachedFunction<string, Folder> folders;
readonly CachedFunction<PDB.Location, PdbSourceLocation> sourceLocations;
/// <summary>
/// Creates a source file entity from a PDB source file.
/// </summary>
/// <param name="file">The PDB source file.</param>
/// <returns>A source file entity.</returns>
public Entities.PdbSourceFile CreateSourceFile(PDB.ISourceFile file) => sourceFiles[file];
public PdbSourceFile CreateSourceFile(PDB.ISourceFile file) => sourceFiles[file];
/// <summary>
/// Creates a folder entitiy with the given path.
/// </summary>
/// <param name="path">The path of the folder.</param>
/// <returns>A folder entity.</returns>
public Entities.Folder CreateFolder(string path) => folders[path];
public Folder CreateFolder(string path) => folders[path];
/// <summary>
/// Creates a source location.
/// </summary>
/// <param name="loc">The source location from PDB.</param>
/// <returns>A source location entity.</returns>
public Entities.PdbSourceLocation CreateSourceLocation(PDB.Location loc) => sourceLocations[loc];
public PdbSourceLocation CreateSourceLocation(PDB.Location loc) => sourceLocations[loc];
#endregion
#endregion
readonly CachedFunction<GenericContext, Handle, ILabelledEntity> genericHandleFactory;

Просмотреть файл

@ -1,202 +1,31 @@
using System.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata;
namespace Semmle.Extraction.CIL
{
/// <summary>
/// An ID fragment which is designed to be shared, reused
/// and composed using the + operator.
/// </summary>
public abstract class Id : IId
{
public void AppendTo(System.IO.TextWriter tb)
{
tb.Write("@\"");
BuildParts(tb);
tb.Write("\"");
}
public abstract void BuildParts(System.IO.TextWriter tb);
public static Id operator +(Id l1, Id l2) => Create(l1, l2);
public static Id operator +(Id l1, string l2) => Create(l1, Create(l2));
public static Id operator +(Id l1, int l2) => Create(l1, Create(l2));
public static Id operator +(string l1, Id l2) => Create(Create(l1), l2);
public static Id operator +(int l1, Id l2) => Create(Create(l1), l2);
public static Id Create(string s) => s == null ? null : new StringId(s);
public static Id Create(int i) => new IntId(i);
static readonly Id openCurly = Create("{#");
static readonly Id closeCurly = Create("}");
public static Id Create(Label l) => openCurly + l.Value + closeCurly;
static readonly Id comma = Id.Create(",");
public static Id CommaSeparatedList(IEnumerable<Id> items)
{
Id result = null;
bool first = true;
foreach (var i in items)
{
if (first) first = false; else result += comma;
result += i;
}
return result;
}
public static Id Create(Id l1, Id l2)
{
return l1 == null ? l2 : l2 == null ? l1 : new ConsId(l1, l2);
}
public abstract string Value { get; }
public override string ToString() => Value;
}
/// <summary>
/// An ID concatenating two other IDs.
/// </summary>
public sealed class ConsId : Id
{
readonly Id left, right;
readonly int hash;
public ConsId(Id l1, Id l2)
{
left = l1;
right = l2;
hash = unchecked(12 + 3 * (left.GetHashCode() + 51 * right.GetHashCode()));
}
public override void BuildParts(System.IO.TextWriter tb)
{
left.BuildParts(tb);
right.BuildParts(tb);
}
public override bool Equals(object other)
{
return other is ConsId && Equals((ConsId)other);
}
public bool Equals(ConsId other)
{
return this == other ||
(hash == other.hash && left.Equals(other.left) && right.Equals(other.right));
}
public override int GetHashCode() => hash;
public override string Value => left.Value + right.Value;
}
/// <summary>
/// A leaf ID storing a string.
/// </summary>
public sealed class StringId : Id
{
readonly string value;
public override string Value => value;
public StringId(string s)
{
value = s;
}
public override void BuildParts(System.IO.TextWriter tw)
{
tw.Write(value);
}
public override bool Equals(object obj)
{
return obj is StringId && ((StringId)obj).value == value;
}
public override int GetHashCode() => Value.GetHashCode() * 31 + 9;
}
/// <summary>
/// A leaf ID storing an integer.
/// </summary>
public sealed class IntId : Id
{
readonly int value;
public override string Value => value.ToString();
public IntId(int i)
{
value = i;
}
public override void BuildParts(System.IO.TextWriter tw)
{
tw.Write(value);
}
public override bool Equals(object obj)
{
return obj is IntId && ((IntId)obj).value == value;
}
public override int GetHashCode() => unchecked(12 + value * 17);
}
/// <summary>
/// Some predefined IDs.
/// </summary>
public static class IdUtils
{
public static StringId boolId = new StringId("Boolean");
public static StringId byteId = new StringId("Byte");
public static StringId charId = new StringId("Char");
public static StringId doubleId = new StringId("Double");
public static StringId shortId = new StringId("Int16");
public static StringId intId = new StringId("Int32");
public static StringId longId = new StringId("Int64");
public static StringId intptrId = new StringId("IntPtr");
public static StringId objectId = new StringId("Object");
public static StringId sbyteId = new StringId("SByte");
public static StringId floatId = new StringId("Single");
public static StringId stringId = new StringId("String");
public static StringId ushortId = new StringId("UInt16");
public static StringId uintId = new StringId("UInt32");
public static StringId ulongId = new StringId("UInt64");
public static StringId uintptrId = new StringId("UIntPtr");
public static StringId voidId = new StringId("Void");
public static StringId typedReferenceId = new StringId("TypedReference");
public static StringId Id(this PrimitiveTypeCode typeCode)
public static string Id(this PrimitiveTypeCode typeCode)
{
switch (typeCode)
{
case PrimitiveTypeCode.Boolean: return boolId;
case PrimitiveTypeCode.Byte: return byteId;
case PrimitiveTypeCode.Char: return charId;
case PrimitiveTypeCode.Double: return doubleId;
case PrimitiveTypeCode.Int16: return shortId;
case PrimitiveTypeCode.Int32: return intId;
case PrimitiveTypeCode.Int64: return longId;
case PrimitiveTypeCode.IntPtr: return intptrId;
case PrimitiveTypeCode.Object: return objectId;
case PrimitiveTypeCode.SByte: return sbyteId;
case PrimitiveTypeCode.Single: return floatId;
case PrimitiveTypeCode.String: return stringId;
case PrimitiveTypeCode.UInt16: return ushortId;
case PrimitiveTypeCode.UInt32: return uintId;
case PrimitiveTypeCode.UInt64: return ulongId;
case PrimitiveTypeCode.UIntPtr: return uintptrId;
case PrimitiveTypeCode.Void: return voidId;
case PrimitiveTypeCode.TypedReference: return typedReferenceId;
case PrimitiveTypeCode.Boolean: return "Boolean";
case PrimitiveTypeCode.Byte: return "Byte";
case PrimitiveTypeCode.Char: return "Char";
case PrimitiveTypeCode.Double: return "Double";
case PrimitiveTypeCode.Int16: return "Int16";
case PrimitiveTypeCode.Int32: return "Int32";
case PrimitiveTypeCode.Int64: return "Int64";
case PrimitiveTypeCode.IntPtr: return "IntPtr";
case PrimitiveTypeCode.Object: return "Object";
case PrimitiveTypeCode.SByte: return "SByte";
case PrimitiveTypeCode.Single: return "Single";
case PrimitiveTypeCode.String: return "String";
case PrimitiveTypeCode.UInt16: return "UInt16";
case PrimitiveTypeCode.UInt32: return "UInt32";
case PrimitiveTypeCode.UInt64: return "UInt64";
case PrimitiveTypeCode.UIntPtr: return "UIntPtr";
case PrimitiveTypeCode.Void: return "Void";
case PrimitiveTypeCode.TypedReference: return "TypedReference";
default: throw new InternalError($"Unhandled type code {typeCode}");
}
}

Просмотреть файл

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
@ -8,6 +8,10 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;DEBUG_LABELS</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Semmle.Extraction\Semmle.Extraction.csproj" />
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />

Просмотреть файл

@ -8,8 +8,8 @@ namespace Semmle.Extraction.CSharp.Entities
{
class Compilation : FreshEntity
{
string cwd;
string[] args;
private readonly string cwd;
private readonly string[] args;
public Compilation(Context cx, string cwd, string[] args) : base(cx)
{

Просмотреть файл

@ -235,7 +235,7 @@ namespace Semmle.Extraction.CSharp.Entities
if (method.IsVararg)
{
tb.WriteSeparator(",", index++);
tb.WriteSeparator(",", index);
tb.Write("__arglist");
}

Просмотреть файл

@ -43,7 +43,7 @@ namespace Semmle.Extraction.CSharp.Entities
public override void Populate(TextWriter trapFile)
{
trapFile.Emit(new Tuple("modifiers", Label, symbol.name));
trapFile.modifiers(Label, symbol.name);
}
public static string AccessbilityModifier(Accessibility access)

Просмотреть файл

@ -83,9 +83,11 @@ namespace Semmle.Extraction.CSharp.Entities
ExtractMetadataHandle(trapFile);
ExtractAttributes();
var tb = new StringWriter();
symbol.BuildDisplayName(Context, tb);
trapFile.types(this, GetClassType(Context, symbol), tb.ToString());
using (var tb = new StringWriter())
{
symbol.BuildDisplayName(Context, tb);
trapFile.types(this, GetClassType(Context, symbol), tb.ToString());
}
// Visit base types
var baseTypes = new List<Type>();

Просмотреть файл

@ -16,7 +16,6 @@ namespace Semmle.Extraction.CSharp.Entities
public UsingDirective(Context cx, UsingDirectiveSyntax usingDirective, NamespaceDeclaration parent)
: base(cx)
{
var trapFile = cx.TrapWriter.Writer;
Node = usingDirective;
Parent = parent;
TryPopulate();

Просмотреть файл

@ -308,6 +308,12 @@ namespace Semmle.Extraction.CSharp
{
trapFile.WriteTuple("methods", method, name, declType, retType, originalDefinition);
}
internal static void modifiers(this TextWriter trapFile, Label entity, string modifier)
{
trapFile.BeginTuple("modifiers").Param(entity).Param(modifier).EndTuple();
}
internal static void mutator_invocation_mode(this TextWriter trapFile, Expression expr, int mode)
{
trapFile.WriteTuple("mutator_invocation_mode", expr, mode);

Просмотреть файл

@ -332,7 +332,7 @@ namespace Semmle.Extraction.CommentProcessing
class CommentBlock : ICommentBlock
{
private List<ICommentLine> lines = new List<ICommentLine>();
private readonly List<ICommentLine> lines = new List<ICommentLine>();
public IEnumerable<ICommentLine> CommentLines => lines;

Просмотреть файл

@ -105,9 +105,11 @@ namespace Semmle.Extraction
if (entity.NeedsPopulation)
Populate(init as ISymbol, entity);
#if DEBUG_LABELS
var id = new StringWriter();
entity.WriteId(id);
CheckEntityHasUniqueLabel(id.ToString(), entity);
using (var id = new StringWriter())
{
entity.WriteId(id);
CheckEntityHasUniqueLabel(id.ToString(), entity);
}
#endif
}
@ -149,9 +151,11 @@ namespace Semmle.Extraction
Populate(init as ISymbol, entity);
#if DEBUG_LABELS
var id = new StringWriter();
entity.WriteId(id);
CheckEntityHasUniqueLabel(id.ToString(), entity);
using (var id = new StringWriter())
{
entity.WriteId(id);
CheckEntityHasUniqueLabel(id.ToString(), entity);
}
#endif
return entity;

Просмотреть файл

@ -69,7 +69,7 @@ namespace Semmle.Extraction.Entities
public override void WriteId(System.IO.TextWriter trapFile)
{
trapFile.Write(assembly.ToString());
if (assemblyPath is null)
if (!(assemblyPath is null))
{
trapFile.Write("#file:///");
trapFile.Write(assemblyPath.Replace("\\", "/"));

Просмотреть файл

@ -162,11 +162,13 @@ namespace Semmle.Extraction
/// <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();
using (var writer = new StringWriter())
{
writer.WriteLabel(entity.Label.Value);
writer.Write('=');
entity.WriteQuotedId(writer);
return writer.ToString();
}
}
}

Просмотреть файл

@ -106,7 +106,7 @@ namespace Semmle.Extraction
class IdTrapBuilder
{
readonly public List<string> Fragments = new List<string>();
public readonly List<string> Fragments = new List<string>();
public void Append(object arg)
{

Просмотреть файл

@ -90,6 +90,9 @@ namespace Semmle.Extraction
CIL = !value;
Fast = value;
return true;
case "brotli":
TrapCompression = value ? TrapWriter.CompressionMode.Brotli : TrapWriter.CompressionMode.Gzip;
return true;
default:
return false;
}

Просмотреть файл

@ -26,9 +26,11 @@ namespace Semmle.Extraction
{
get
{
var trap = new StringWriter();
Populate(trap);
return trap.ToString();
using (var trap = new StringWriter())
{
Populate(trap);
return trap.ToString();
}
}
}

Просмотреть файл

@ -48,6 +48,12 @@ namespace Semmle.Extraction
return new NextParam(Writer);
}
public NextParam Param(Label label)
{
Writer.WriteLabel(label.Value);
return new NextParam(Writer);
}
public void EndTuple()
{
Writer.WriteLine(')');

Просмотреть файл

@ -155,9 +155,11 @@ namespace Semmle.Extraction
public override string ToString()
{
// Only implemented for debugging purposes
var tsb = new StringWriter();
EmitToTrapBuilder(tsb);
return tsb.ToString();
using (var writer = new StringWriter())
{
EmitToTrapBuilder(writer);
return writer.ToString();
}
}
}
}

Просмотреть файл

@ -86,5 +86,19 @@ namespace Semmle.Util
{
items.ForEach(_ => { });
}
/// <summary>
/// Computes a hash of a sequence.
/// </summary>
/// <typeparam name="T">The type of the item.</typeparam>
/// <param name="items">The list of items to hash.</param>
/// <returns>The hash code.</returns>
public static int SequenceHash<T>(this IEnumerable<T> items)
{
int h = 0;
foreach (var i in items)
h = h * 7 + i.GetHashCode();
return h;
}
}
}