Skip to content

Commit 45893ab

Browse files
authored
Merge pull request #4775 from tamasvajk/feature/cil-attribute-decoding2
C#: Improve CIL attribute decoding
2 parents 65c58ed + 1bc65a6 commit 45893ab

23 files changed

+1106
-607
lines changed

csharp/extractor/Semmle.Extraction.CIL/Context.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.IO;
3-
using System.Linq;
43
using System.Reflection.Metadata;
54
using System.Reflection.PortableExecutable;
65

@@ -83,7 +82,7 @@ public void WriteAssemblyPrefix(TextWriter trapFile)
8382
trapFile.Write(GetString(def.Name));
8483
trapFile.Write('_');
8584
trapFile.Write(def.Version.ToString());
86-
trapFile.Write("::");
85+
trapFile.Write(Entities.Type.AssemblyTypeNameSeparator);
8786
}
8887

8988
public Entities.TypeSignatureDecoder TypeSignatureDecoder { get; }

csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,26 @@ public override bool Equals(object? obj)
2727
return obj is ArrayType array && elementType.Equals(array.elementType) && rank == array.rank;
2828
}
2929

30-
public override int GetHashCode()
31-
{
32-
return elementType.GetHashCode() * 5 + rank;
33-
}
30+
public override int GetHashCode() => HashCode.Combine(elementType, rank);
3431

3532
public override void WriteId(TextWriter trapFile, bool inContext)
3633
{
37-
elementType.GetId(trapFile, inContext);
34+
elementType.WriteId(trapFile, inContext);
3835
trapFile.Write('[');
3936
for (var i = 1; i < rank; ++i)
37+
{
4038
trapFile.Write(',');
39+
}
4140
trapFile.Write(']');
4241
}
4342

4443
public override string Name => elementType.Name + "[]";
4544

46-
public override Namespace Namespace => Cx.SystemNamespace;
45+
public override Namespace ContainingNamespace => Cx.SystemNamespace;
4746

4847
public override Type? ContainingType => null;
4948

50-
public override int ThisTypeParameters => elementType.ThisTypeParameters;
49+
public override int ThisTypeParameterCount => elementType.ThisTypeParameterCount;
5150

5251
public override CilTypeKind Kind => CilTypeKind.Array;
5352

@@ -71,7 +70,5 @@ public override IEnumerable<IExtractionProduct> Contents
7170
public override IEnumerable<Type> GenericArguments => elementType.GenericArguments;
7271

7372
public override IEnumerable<Type> TypeParameters => elementType.TypeParameters;
74-
75-
public override IEnumerable<Type> MethodParameters => throw new NotImplementedException();
7673
}
7774
}

csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,33 +43,38 @@ public override IEnumerable<IExtractionProduct> Contents
4343
{
4444
decoded = attrib.DecodeValue(new CustomAttributeDecoder(Cx));
4545
}
46-
catch (NotImplementedException)
46+
catch
4747
{
48-
// Attribute decoding is only partial at this stage.
48+
Cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Info,
49+
$"Attribute decoding is partial. Decoding attribute {constructor.DeclaringType.GetQualifiedName()} failed on {@object}.");
4950
yield break;
5051
}
5152

5253
for (var index = 0; index < decoded.FixedArguments.Length; ++index)
5354
{
54-
var value = decoded.FixedArguments[index].Value;
55-
var stringValue = GetStringValue(value);
55+
var stringValue = GetStringValue(decoded.FixedArguments[index].Type, decoded.FixedArguments[index].Value);
5656
yield return Tuples.cil_attribute_positional_argument(this, index, stringValue);
5757
}
5858

5959
foreach (var p in decoded.NamedArguments)
6060
{
61-
var value = p.Value;
62-
var stringValue = GetStringValue(value);
63-
yield return Tuples.cil_attribute_named_argument(this, p.Name, stringValue);
61+
var stringValue = GetStringValue(p.Type, p.Value);
62+
yield return Tuples.cil_attribute_named_argument(this, p.Name!, stringValue);
6463
}
6564
}
6665
}
6766

68-
private static string GetStringValue(object? value)
67+
private static string GetStringValue(Type type, object? value)
6968
{
7069
if (value is System.Collections.Immutable.ImmutableArray<CustomAttributeTypedArgument<Type>> values)
7170
{
72-
return "[" + string.Join(",", values.Select(v => GetStringValue(v.Value))) + "]";
71+
return "[" + string.Join(",", values.Select(v => GetStringValue(v.Type, v.Value))) + "]";
72+
}
73+
74+
if (type.GetQualifiedName() == "System.Type" &&
75+
value is Type t)
76+
{
77+
return t.GetQualifiedName();
7378
}
7479

7580
return value?.ToString() ?? "null";

csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs

Lines changed: 30 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -17,36 +17,18 @@ public sealed class ConstructedType : Type
1717
// Either null or notEmpty
1818
private readonly Type[]? thisTypeArguments;
1919

20-
public override IEnumerable<Type> ThisTypeArguments => thisTypeArguments.EnumerateNull();
21-
22-
public override IEnumerable<Type> ThisGenericArguments => thisTypeArguments.EnumerateNull();
23-
24-
public override IEnumerable<IExtractionProduct> Contents
25-
{
26-
get
27-
{
28-
foreach (var c in base.Contents)
29-
yield return c;
30-
31-
var i = 0;
32-
foreach (var type in ThisGenericArguments)
33-
{
34-
yield return type;
35-
yield return Tuples.cil_type_argument(this, i++, type);
36-
}
37-
}
38-
}
39-
40-
public override Type SourceDeclaration => unboundGenericType;
20+
private readonly Type? containingType;
21+
private readonly NamedTypeIdWriter idWriter;
4122

4223
public ConstructedType(Context cx, Type unboundType, IEnumerable<Type> typeArguments) : base(cx)
4324
{
25+
idWriter = new NamedTypeIdWriter(this);
4426
var suppliedArgs = typeArguments.Count();
45-
if (suppliedArgs != unboundType.TotalTypeParametersCheck)
27+
if (suppliedArgs != unboundType.TotalTypeParametersCount)
4628
throw new InternalError("Unexpected number of type arguments in ConstructedType");
4729

4830
unboundGenericType = unboundType;
49-
var thisParams = unboundType.ThisTypeParameters;
31+
var thisParams = unboundType.ThisTypeParameterCount;
5032

5133
if (typeArguments.Count() == thisParams)
5234
{
@@ -88,14 +70,29 @@ public override int GetHashCode()
8870
return h;
8971
}
9072

91-
private readonly Type? containingType;
73+
public override IEnumerable<IExtractionProduct> Contents
74+
{
75+
get
76+
{
77+
foreach (var c in base.Contents)
78+
yield return c;
79+
80+
var i = 0;
81+
foreach (var type in ThisTypeArguments)
82+
{
83+
yield return type;
84+
yield return Tuples.cil_type_argument(this, i++, type);
85+
}
86+
}
87+
}
88+
89+
public override Type SourceDeclaration => unboundGenericType;
90+
9291
public override Type? ContainingType => containingType;
9392

9493
public override string Name => unboundGenericType.Name;
9594

96-
public override Namespace Namespace => unboundGenericType.Namespace!;
97-
98-
public override int ThisTypeParameters => thisTypeArguments == null ? 0 : thisTypeArguments.Length;
95+
public override Namespace ContainingNamespace => unboundGenericType.ContainingNamespace!;
9996

10097
public override CilTypeKind Kind => unboundGenericType.Kind;
10198

@@ -106,40 +103,17 @@ public override Type Construct(IEnumerable<Type> typeArguments)
106103

107104
public override void WriteId(TextWriter trapFile, bool inContext)
108105
{
109-
if (ContainingType != null)
110-
{
111-
ContainingType.GetId(trapFile, inContext);
112-
trapFile.Write('.');
113-
}
114-
else
115-
{
116-
WriteAssemblyPrefix(trapFile);
117-
118-
if (!Namespace.IsGlobalNamespace)
119-
{
120-
Namespace.WriteId(trapFile);
121-
trapFile.Write('.');
122-
}
123-
}
124-
trapFile.Write(unboundGenericType.Name);
125-
126-
if (thisTypeArguments != null && thisTypeArguments.Any())
127-
{
128-
trapFile.Write('<');
129-
var index = 0;
130-
foreach (var t in thisTypeArguments)
131-
{
132-
trapFile.WriteSeparator(",", ref index);
133-
t.WriteId(trapFile);
134-
}
135-
trapFile.Write('>');
136-
}
106+
idWriter.WriteId(trapFile, inContext);
137107
}
138108

139109
public override void WriteAssemblyPrefix(TextWriter trapFile) => unboundGenericType.WriteAssemblyPrefix(trapFile);
140110

111+
public override int ThisTypeParameterCount => thisTypeArguments?.Length ?? 0;
112+
141113
public override IEnumerable<Type> TypeParameters => GenericArguments;
142114

143-
public override IEnumerable<Type> MethodParameters => throw new NotImplementedException();
115+
public override IEnumerable<Type> ThisTypeArguments => thisTypeArguments.EnumerateNull();
116+
117+
public override IEnumerable<Type> ThisGenericArguments => thisTypeArguments.EnumerateNull();
144118
}
145119
}

csharp/extractor/Semmle.Extraction.CIL/Entities/CustomAttributeDecoder.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ internal class CustomAttributeDecoder : ICustomAttributeTypeProvider<Type>
1414

1515
public Type GetPrimitiveType(PrimitiveTypeCode typeCode) => cx.Create(typeCode);
1616

17-
public Type GetSystemType() => throw new NotImplementedException();
17+
public Type GetSystemType() => new NoMetadataHandleType(cx, "System.Type");
1818

1919
public Type GetSZArrayType(Type elementType) =>
2020
cx.Populate(new ArrayType(cx, elementType));
@@ -25,10 +25,23 @@ public Type GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle ha
2525
public Type GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) =>
2626
(Type)cx.Create(handle);
2727

28-
public Type GetTypeFromSerializedName(string name) => throw new NotImplementedException();
28+
public Type GetTypeFromSerializedName(string name) => new NoMetadataHandleType(cx, name);
2929

30-
public PrimitiveTypeCode GetUnderlyingEnumType(Type type) => throw new NotImplementedException();
30+
public PrimitiveTypeCode GetUnderlyingEnumType(Type type)
31+
{
32+
if (type is TypeDefinitionType tdt &&
33+
tdt.GetUnderlyingEnumType() is PrimitiveTypeCode underlying)
34+
{
35+
return underlying;
36+
}
3137

32-
public bool IsSystemType(Type type) => type is PrimitiveType; // ??
38+
var name = type.GetQualifiedName();
39+
cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Info, $"Couldn't get underlying enum type for {name}");
40+
41+
// We can't fall back to Int32, because the type returned here defines how many bytes are read from the
42+
// stream and how those bytes are interpreted.
43+
throw new NotImplementedException();
44+
}
45+
public bool IsSystemType(Type type) => type.GetQualifiedName() == "System.Type";
3346
}
3447
}

csharp/extractor/Semmle.Extraction.CIL/Entities/DefinitionMethod.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ public override IEnumerable<IExtractionProduct> Contents
178178
}
179179
}
180180

181-
private IEnumerable<IExtractionProduct> Decode(byte[] ilbytes, Dictionary<int, Instruction> jump_table)
181+
private IEnumerable<IExtractionProduct> Decode(byte[]? ilbytes, Dictionary<int, Instruction> jump_table)
182182
{
183183
// Sequence points are stored in order of offset.
184184
// We use an enumerator to locate the correct sequence point for each instruction.
@@ -203,9 +203,9 @@ private IEnumerable<IExtractionProduct> Decode(byte[] ilbytes, Dictionary<int, I
203203
}
204204

205205
var child = 0;
206-
for (var offset = 0; offset < ilbytes.Length;)
206+
for (var offset = 0; offset < (ilbytes?.Length ?? 0);)
207207
{
208-
var instruction = new Instruction(Cx, this, ilbytes, offset, child++);
208+
var instruction = new Instruction(Cx, this, ilbytes!, offset, child++);
209209
yield return instruction;
210210

211211
if (nextSequencePoint != null && offset >= nextSequencePoint.Current.Offset)
@@ -245,12 +245,12 @@ public IEnumerable<Instruction> DebugInstructions
245245
var ilbytes = body.GetILBytes();
246246

247247
var child = 0;
248-
for (var offset = 0; offset < ilbytes.Length;)
248+
for (var offset = 0; offset < (ilbytes?.Length ?? 0);)
249249
{
250250
Instruction decoded;
251251
try
252252
{
253-
decoded = new Instruction(Cx, this, ilbytes, offset, child++);
253+
decoded = new Instruction(Cx, this, ilbytes!, offset, child++);
254254
offset += decoded.Width;
255255
}
256256
catch // lgtm[cs/catch-of-all-exceptions]

csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,16 @@ public ErrorType(Context cx) : base(cx)
1616

1717
public override string Name => "!error";
1818

19-
public override Namespace Namespace => Cx.GlobalNamespace;
19+
public override Namespace ContainingNamespace => Cx.GlobalNamespace;
2020

2121
public override Type? ContainingType => null;
2222

23-
public override int ThisTypeParameters => 0;
23+
public override int ThisTypeParameterCount => 0;
2424

2525
public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException();
2626

2727
public override IEnumerable<Type> TypeParameters => throw new NotImplementedException();
2828

29-
public override IEnumerable<Type> MethodParameters => throw new NotImplementedException();
30-
3129
public override Type Construct(IEnumerable<Type> typeArguments) => throw new NotImplementedException();
3230
}
3331
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System.Collections.Generic;
2+
using System.Reflection.Metadata;
3+
4+
namespace Semmle.Extraction.CIL.Entities
5+
{
6+
internal static class GenericsHelper
7+
{
8+
public static TypeTypeParameter[] MakeTypeParameters(Type container, int count)
9+
{
10+
var newTypeParams = new TypeTypeParameter[count];
11+
for (var i = 0; i < newTypeParams.Length; ++i)
12+
{
13+
newTypeParams[i] = new TypeTypeParameter(container, i);
14+
}
15+
return newTypeParams;
16+
}
17+
18+
public static string GetNonGenericName(StringHandle name, MetadataReader reader)
19+
{
20+
var n = reader.GetString(name);
21+
return GetNonGenericName(n);
22+
}
23+
24+
public static string GetNonGenericName(string name)
25+
{
26+
var tick = name.LastIndexOf('`');
27+
return tick == -1
28+
? name
29+
: name.Substring(0, tick);
30+
}
31+
32+
public static int GetGenericTypeParameterCount(StringHandle name, MetadataReader reader)
33+
{
34+
var n = reader.GetString(name);
35+
return GetGenericTypeParameterCount(n);
36+
}
37+
38+
public static int GetGenericTypeParameterCount(string name)
39+
{
40+
var tick = name.LastIndexOf('`');
41+
return tick == -1
42+
? 0
43+
: int.Parse(name.Substring(tick + 1));
44+
}
45+
46+
public static IEnumerable<Type> GetAllTypeParameters(Type? container, IEnumerable<TypeTypeParameter> thisTypeParameters)
47+
{
48+
if (container != null)
49+
{
50+
foreach (var t in container.TypeParameters)
51+
yield return t;
52+
}
53+
54+
foreach (var t in thisTypeParameters)
55+
yield return t;
56+
57+
}
58+
}
59+
}

csharp/extractor/Semmle.Extraction.CIL/Entities/MethodTypeParameter.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ public override int GetHashCode()
4141

4242
public override IEnumerable<Type> TypeParameters => throw new NotImplementedException();
4343

44-
public override IEnumerable<Type> MethodParameters => throw new NotImplementedException();
45-
4644
public override IEnumerable<IExtractionProduct> Contents
4745
{
4846
get

0 commit comments

Comments
 (0)