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,39 @@ 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 attrHandle in def . GetCustomAttributes ( ) . Select ( reader . GetCustomAttribute ) )
182+ {
183+ var ctorHandle = attrHandle . Constructor ;
184+ if ( ctorHandle . Kind != HandleKind . MemberReference )
185+ {
186+ continue ;
187+ }
188+
189+ var mHandle = reader . GetMemberReference ( ( MemberReferenceHandle ) ctorHandle ) . Parent ;
190+ if ( mHandle . Kind != HandleKind . TypeReference )
191+ {
192+ continue ;
193+ }
194+
195+ var name = reader . GetString ( reader . GetTypeReference ( ( TypeReferenceHandle ) mHandle ) . Name ) ;
196+
197+ if ( name is "TargetFrameworkAttribute" )
198+ {
199+ var decoded = attrHandle . DecodeValue ( new DummyAttributeDecoder ( ) ) ;
200+ if (
201+ decoded . FixedArguments . Length > 0 &&
202+ decoded . FixedArguments [ 0 ] . Value is string value &&
203+ NetCoreAppRegex ( ) . Match ( value ) . Groups . TryGetValue ( "version" , out var match ) )
204+ {
205+ netCoreVersion = new Version ( match . Value ) ;
206+ }
207+ break ;
208+ }
209+ }
210+
211+ return new AssemblyInfo ( filename , reader . GetString ( def . Name ) , def . Version , culture , publicKeyString . ToString ( ) , netCoreVersion ) ;
166212 }
167213 }
168214 catch ( BadImageFormatException )
@@ -176,5 +222,33 @@ public static AssemblyInfo ReadFromFile(string filename)
176222
177223 throw new AssemblyLoadException ( ) ;
178224 }
225+
226+ [ GeneratedRegex ( @"^\.NETCoreApp,Version=v(?<version>\d+\.\d+)$" , RegexOptions . IgnoreCase | RegexOptions . Compiled | RegexOptions . Singleline ) ]
227+ private static partial Regex NetCoreAppRegex ( ) ;
228+
229+ private class DummyAttributeDecoder : ICustomAttributeTypeProvider < int >
230+ {
231+ public int GetPrimitiveType ( PrimitiveTypeCode typeCode ) => 0 ;
232+
233+ public int GetSystemType ( ) => throw new NotImplementedException ( ) ;
234+
235+ public int GetSZArrayType ( int elementType ) =>
236+ throw new NotImplementedException ( ) ;
237+
238+ public int GetTypeFromDefinition ( MetadataReader reader , TypeDefinitionHandle handle , byte rawTypeKind ) =>
239+ throw new NotImplementedException ( ) ;
240+
241+ public int GetTypeFromReference ( MetadataReader reader , TypeReferenceHandle handle , byte rawTypeKind ) =>
242+ throw new NotImplementedException ( ) ;
243+
244+ public int GetTypeFromSerializedName ( string name ) =>
245+ throw new NotImplementedException ( ) ;
246+
247+ public PrimitiveTypeCode GetUnderlyingEnumType ( int type ) =>
248+ throw new NotImplementedException ( ) ;
249+
250+ public bool IsSystemType ( int type ) => throw new NotImplementedException ( ) ;
251+
252+ }
179253 }
180254}
0 commit comments