55using System . Reflection ;
66using System . Security . Cryptography ;
77using System . Text ;
8+ using System . Reflection . Metadata ;
9+ using System . Text . RegularExpressions ;
810
911namespace Semmle . Extraction . CSharp . DependencyFetching
1012{
1113 /// <summary>
1214 /// Stores information about an assembly file (DLL).
1315 /// </summary>
14- internal sealed class AssemblyInfo
16+ internal sealed partial class AssemblyInfo
1517 {
1618 /// <summary>
1719 /// The file containing the assembly.
@@ -28,6 +30,17 @@ internal sealed class AssemblyInfo
2830 /// </summary>
2931 public System . Version ? Version { get ; }
3032
33+ /// <summary>
34+ /// The version number of the .NET Core framework that this assembly targets.
35+ ///
36+ /// This is extracted from the `TargetFrameworkAttribute` of the assembly, e.g.
37+ /// ```
38+ /// [assembly:TargetFramework(".NETCoreApp,Version=v7.0")]
39+ /// ```
40+ /// yields version 7.0.
41+ /// </summary>
42+ public Version ? NetCoreVersion { get ; }
43+
3144 /// <summary>
3245 /// The public key token of the assembly.
3346 /// </summary>
@@ -97,13 +110,14 @@ private AssemblyInfo(string id, string filename)
97110 Filename = filename ;
98111 }
99112
100- private AssemblyInfo ( string filename , string name , Version version , string culture , string publicKeyToken )
113+ private AssemblyInfo ( string filename , string name , Version version , string culture , string publicKeyToken , Version ? netCoreVersion )
101114 {
102115 Filename = filename ;
103116 Name = name ;
104117 Version = version ;
105118 Culture = culture ;
106119 PublicKeyToken = publicKeyToken ;
120+ NetCoreVersion = netCoreVersion ;
107121 }
108122
109123 /// <summary>
@@ -150,7 +164,7 @@ public static AssemblyInfo ReadFromFile(string filename)
150164 var metadata = pereader . GetMetadata ( ) ;
151165 unsafe
152166 {
153- var reader = new System . Reflection . Metadata . MetadataReader ( metadata . Pointer , metadata . Length ) ;
167+ var reader = new MetadataReader ( metadata . Pointer , metadata . Length ) ;
154168 var def = reader . GetAssemblyDefinition ( ) ;
155169
156170 // This is how you compute the public key token from the full public key.
@@ -162,7 +176,40 @@ public static AssemblyInfo ReadFromFile(string filename)
162176 publicKeyString . AppendFormat ( "{0:x2}" , b ) ;
163177
164178 var culture = def . Culture . IsNil ? "neutral" : reader . GetString ( def . Culture ) ;
165- return new AssemblyInfo ( filename , reader . GetString ( def . Name ) , def . Version , culture , publicKeyString . ToString ( ) ) ;
179+ Version ? netCoreVersion = null ;
180+
181+ foreach ( var attrsHandle in def . GetCustomAttributes ( ) )
182+ {
183+ var attrHandle = reader . GetCustomAttribute ( attrsHandle ) ;
184+ var ctorHandle = attrHandle . Constructor ;
185+ if ( ctorHandle . Kind != HandleKind . MemberReference )
186+ {
187+ continue ;
188+ }
189+
190+ var mHandle = reader . GetMemberReference ( ( MemberReferenceHandle ) ctorHandle ) . Parent ;
191+ if ( mHandle . Kind != HandleKind . TypeReference )
192+ {
193+ continue ;
194+ }
195+
196+ var name = reader . GetString ( reader . GetTypeReference ( ( TypeReferenceHandle ) mHandle ) . Name ) ;
197+
198+ if ( name is "TargetFrameworkAttribute" )
199+ {
200+ var decoded = attrHandle . DecodeValue ( new DummyAttributeDecoder ( ) ) ;
201+ if ( decoded . FixedArguments . Length > 0 && decoded . FixedArguments [ 0 ] . Value is string value )
202+ {
203+ if ( NetCoreAppRegex ( ) . Match ( value ) . Groups . TryGetValue ( "version" , out var match ) )
204+ {
205+ netCoreVersion = new Version ( match . Value ) ;
206+ }
207+ }
208+ break ;
209+ }
210+ }
211+
212+ return new AssemblyInfo ( filename , reader . GetString ( def . Name ) , def . Version , culture , publicKeyString . ToString ( ) , netCoreVersion ) ;
166213 }
167214 }
168215 catch ( BadImageFormatException )
@@ -176,5 +223,33 @@ public static AssemblyInfo ReadFromFile(string filename)
176223
177224 throw new AssemblyLoadException ( ) ;
178225 }
226+
227+ [ GeneratedRegex ( @"^\.NETCoreApp,Version=v(?<version>\d+\.\d+)$" , RegexOptions . IgnoreCase | RegexOptions . Compiled | RegexOptions . Singleline ) ]
228+ private static partial Regex NetCoreAppRegex ( ) ;
229+
230+ private class DummyAttributeDecoder : ICustomAttributeTypeProvider < int >
231+ {
232+ public int GetPrimitiveType ( PrimitiveTypeCode typeCode ) => 0 ;
233+
234+ public int GetSystemType ( ) => throw new NotImplementedException ( ) ;
235+
236+ public int GetSZArrayType ( int elementType ) =>
237+ throw new NotImplementedException ( ) ;
238+
239+ public int GetTypeFromDefinition ( MetadataReader reader , TypeDefinitionHandle handle , byte rawTypeKind ) =>
240+ throw new NotImplementedException ( ) ;
241+
242+ public int GetTypeFromReference ( MetadataReader reader , TypeReferenceHandle handle , byte rawTypeKind ) =>
243+ throw new NotImplementedException ( ) ;
244+
245+ public int GetTypeFromSerializedName ( string name ) =>
246+ throw new NotImplementedException ( ) ;
247+
248+ public PrimitiveTypeCode GetUnderlyingEnumType ( int type ) =>
249+ throw new NotImplementedException ( ) ;
250+
251+ public bool IsSystemType ( int type ) => throw new NotImplementedException ( ) ;
252+
253+ }
179254 }
180255}
0 commit comments