1+ using Microsoft . Scripting . Utils ;
2+ using System ;
3+ using System . Collections . Generic ;
4+ using System . Diagnostics ;
5+ using System . IO ;
6+ using System . Reflection ;
7+ using System . Reflection . Emit ;
8+ using System . Security ;
9+ using System . Text ;
10+ using System . Threading ;
11+
12+
13+ #if ! NETFRAMEWORK
14+ using System . Reflection . Metadata ;
15+ using System . Reflection . Metadata . Ecma335 ;
16+ using System . Reflection . PortableExecutable ;
17+ using System . Diagnostics . SymbolStore ;
18+ using System . Collections . Immutable ;
19+ #endif
20+
21+ // This class is based on the original Microsoft code for Microsoft.Scripting.Gneeration.AssemblyGen.
22+ // Lots of code copied here.
23+ //
24+ // // Licensed to the .NET Foundation under one or more agreements.
25+ // // The .NET Foundation licenses this file to you under the Apache 2.0 License.
26+ // // See the LICENSE file in the project root for more information.
27+ //
28+
29+ // Even before rolling my own version here, I had made my own versions of some its methods, such as MakeDelegateType.
30+ // The main adaptation here is to allow for creating a PersistedAssemblyBuilder for when we are in creating a persisted assembly in the context of .NET 9 or later.
31+ // To help make the code clearer to the outside and easier to implement, I created two constructors, one for persisted assemblies and one for non-persisted assemblies.
32+ // The constructor for persisted assemblies is only visible in .NET Framework and .NET 9 or later.
33+
34+ namespace clojure . lang . CljCompiler . Context ;
35+
36+ public sealed class MyAssemblyGen
37+ {
38+ private readonly AssemblyBuilder _myAssembly ;
39+ private readonly ModuleBuilder _myModule ;
40+ private readonly bool _isDebuggable ;
41+ private readonly bool _isPersistable ;
42+
43+ private int _index ;
44+
45+
46+ #if NETFRAMEWORK || NET9_0_OR_GREATER
47+ private readonly string _outFileName ; // can be null iff not saveable
48+ private readonly string _outDir ; // null means the current directory
49+ #endif
50+
51+ #if NET9_0_OR_GREATER
52+ MethodBuilder _entryPointMethodBuilder ; // non-null means we have an entry point
53+ ISymbolDocumentWriter _docWriter = null ; // non-null means we are writing debug info
54+ #endif
55+
56+ internal AssemblyBuilder AssemblyBuilder => _myAssembly ;
57+ internal ModuleBuilder ModuleBuilder => _myModule ;
58+ internal bool IsDebuggable => _isDebuggable ;
59+
60+ // This is the constructor for the non-persisted assembly.
61+ public MyAssemblyGen ( AssemblyName name , bool isDebuggable )
62+ {
63+ ContractUtils . RequiresNotNull ( name , nameof ( name ) ) ;
64+
65+ _myAssembly = AssemblyBuilder . DefineDynamicAssembly ( name , AssemblyBuilderAccess . Run ) ;
66+ _myModule = _myAssembly . DefineDynamicModule ( name . Name , isDebuggable ) ;
67+ _isDebuggable = isDebuggable ;
68+ _isPersistable = false ;
69+
70+ #if NETFRAMEWORK || NET9_0_OR_GREATER
71+ _outFileName = null ;
72+ _outDir = null ;
73+ #endif
74+
75+ if ( isDebuggable )
76+ {
77+ SetDebuggableAttributes ( ) ;
78+ }
79+ }
80+
81+ #if NETFRAMEWORK || NET9_0_OR_GREATER
82+
83+ // This is the constructor for the persisted assembly.
84+ public MyAssemblyGen ( AssemblyName name , string outDir , string outFileExtension , bool isDebuggable , IDictionary < string , object > attrs = null )
85+ {
86+ ContractUtils . RequiresNotNull ( name , nameof ( name ) ) ;
87+
88+ if ( outFileExtension == null )
89+ {
90+ outFileExtension = ".dll" ;
91+ }
92+
93+ if ( outDir != null )
94+ {
95+ try
96+ {
97+ outDir = Path . GetFullPath ( outDir ) ;
98+ }
99+ catch ( Exception )
100+ {
101+ throw new InvalidOperationException ( "Invalid directory name" ) ;
102+ }
103+ try
104+ {
105+ Path . Combine ( outDir , name . Name + outFileExtension ) ;
106+ }
107+ catch ( ArgumentException )
108+ {
109+ throw new InvalidOperationException ( "Invalid assembly name or extension" ) ;
110+ }
111+
112+ _outFileName = name . Name + outFileExtension ;
113+ _outDir = outDir ;
114+ }
115+
116+
117+ // mark the assembly transparent so that it works in partial trust:
118+ var attributes = new List < CustomAttributeBuilder > {
119+ new CustomAttributeBuilder ( typeof ( SecurityTransparentAttribute ) . GetConstructor ( ReflectionUtils . EmptyTypes ) , ArrayUtils . EmptyObjects ) } ;
120+
121+ if ( attrs != null )
122+ {
123+ foreach ( var attr in attrs )
124+ {
125+ if ( ! ( attr . Value is string a ) || string . IsNullOrWhiteSpace ( a ) )
126+ {
127+ continue ;
128+ }
129+
130+ ConstructorInfo ctor = null ;
131+ switch ( attr . Key )
132+ {
133+ case "assemblyFileVersion" :
134+ ctor = typeof ( AssemblyFileVersionAttribute ) . GetConstructor ( new [ ] { typeof ( string ) } ) ;
135+ break ;
136+ case "copyright" :
137+ ctor = typeof ( AssemblyCopyrightAttribute ) . GetConstructor ( new [ ] { typeof ( string ) } ) ;
138+ break ;
139+ case "productName" :
140+ ctor = typeof ( AssemblyProductAttribute ) . GetConstructor ( new [ ] { typeof ( string ) } ) ;
141+ break ;
142+ case "productVersion" :
143+ ctor = typeof ( AssemblyInformationalVersionAttribute ) . GetConstructor ( new [ ] { typeof ( string ) } ) ;
144+ break ;
145+ }
146+
147+ if ( ctor != null )
148+ {
149+ attributes . Add ( new CustomAttributeBuilder ( ctor , new object [ ] { a } ) ) ;
150+ }
151+ }
152+ }
153+
154+ #if NETFRAMEWORK
155+ _myAssembly = AppDomain . CurrentDomain . DefineDynamicAssembly ( name , AssemblyBuilderAccess . RunAndSave , outDir , false , attributes ) ;
156+ _myModule = _myAssembly . DefineDynamicModule ( name . Name , _outFileName , isDebuggable ) ;
157+ _myAssembly . DefineVersionInfoResource ( ) ;
158+ #elif NET9_0_OR_GREATER
159+ PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder ( name , typeof ( object ) . Assembly , attributes ) ;
160+ _myAssembly = ab ;
161+ _myModule = ab . DefineDynamicModule ( name . Name , isDebuggable ) ;
162+ #endif
163+ _isPersistable = true ;
164+ _isDebuggable = isDebuggable ;
165+
166+ if ( isDebuggable ) {
167+ SetDebuggableAttributes ( ) ;
168+ }
169+
170+
171+ }
172+ #endif
173+
174+ internal void SetDebuggableAttributes ( )
175+ {
176+ DebuggableAttribute . DebuggingModes attrs =
177+ DebuggableAttribute . DebuggingModes . Default |
178+ DebuggableAttribute . DebuggingModes . IgnoreSymbolStoreSequencePoints |
179+ DebuggableAttribute . DebuggingModes . DisableOptimizations ;
180+
181+ Type [ ] argTypes = new Type [ ] { typeof ( DebuggableAttribute . DebuggingModes ) } ;
182+ Object [ ] argValues = new Object [ ] { attrs } ;
183+
184+ var debuggableCtor = typeof ( DebuggableAttribute ) . GetConstructor ( argTypes ) ;
185+
186+ _myAssembly . SetCustomAttribute ( new CustomAttributeBuilder ( debuggableCtor , argValues ) ) ;
187+ _myModule . SetCustomAttribute ( new CustomAttributeBuilder ( debuggableCtor , argValues ) ) ;
188+ }
189+
190+
191+ public string SaveAssembly ( )
192+ {
193+ if ( ! _isPersistable )
194+ {
195+ throw new InvalidOperationException ( "Assembly is not persistable" ) ;
196+ }
197+
198+ #if NETFRAMEWORK
199+ _myAssembly . Save ( _outFileName , PortableExecutableKinds . ILOnly , ImageFileMachine . I386 ) ;
200+ return Path . Combine ( _outDir , _outFileName ) ;
201+ #elif NET9_0_OR_GREATER
202+ if ( _entryPointMethodBuilder is not null || _docWriter is not null )
203+ SavePersistedAssemblyHard ( ) ;
204+ else
205+ ( ( PersistedAssemblyBuilder ) _myAssembly ) . Save ( _outFileName ) ;
206+ return Path . Combine ( _outDir , _outFileName ) ;
207+ #else
208+ return null ;
209+ #endif
210+
211+ }
212+
213+ #if NET9_0_OR_GREATER
214+ private void SavePersistedAssemblyHard ( )
215+ {
216+ PersistedAssemblyBuilder ab = ( PersistedAssemblyBuilder ) _myAssembly ;
217+ MetadataBuilder metadataBuilder = ab . GenerateMetadata ( out BlobBuilder ilStream , out BlobBuilder fieldData , out MetadataBuilder pdbBuilder ) ;
218+
219+ MethodDefinitionHandle entryPointHandle =
220+ _entryPointMethodBuilder is null
221+ ? default ( MethodDefinitionHandle )
222+ : MetadataTokens . MethodDefinitionHandle ( _entryPointMethodBuilder . MetadataToken ) ;
223+ DebugDirectoryBuilder debugDirectoryBuilder = GeneratePdb ( pdbBuilder , metadataBuilder . GetRowCounts ( ) , entryPointHandle ) ;
224+
225+ ManagedPEBuilder peBuilder = new (
226+ header : PEHeaderBuilder . CreateExecutableHeader ( ) ,
227+ metadataRootBuilder : new MetadataRootBuilder ( metadataBuilder ) ,
228+ ilStream : ilStream ,
229+ mappedFieldData : fieldData ,
230+ debugDirectoryBuilder : debugDirectoryBuilder ,
231+ entryPoint : entryPointHandle ) ;
232+
233+ BlobBuilder peBlob = new ( ) ;
234+ peBuilder . Serialize ( peBlob ) ;
235+
236+ // Create the executable:
237+ using FileStream fileStream = new ( _outFileName , FileMode . Create , FileAccess . Write ) ;
238+ peBlob . WriteContentTo ( fileStream ) ;
239+ }
240+
241+ static DebugDirectoryBuilder GeneratePdb ( MetadataBuilder pdbBuilder , ImmutableArray < int > rowCounts , MethodDefinitionHandle entryPointHandle )
242+ {
243+ BlobBuilder portablePdbBlob = new BlobBuilder ( ) ;
244+ PortablePdbBuilder portablePdbBuilder = new PortablePdbBuilder ( pdbBuilder , rowCounts , entryPointHandle ) ;
245+ BlobContentId pdbContentId = portablePdbBuilder . Serialize ( portablePdbBlob ) ;
246+ // In case saving PDB to a file
247+ using FileStream fileStream = new FileStream ( "MyAssemblyEmbeddedSource.pdb" , FileMode . Create , FileAccess . Write ) ;
248+ portablePdbBlob . WriteContentTo ( fileStream ) ;
249+
250+ DebugDirectoryBuilder debugDirectoryBuilder = new DebugDirectoryBuilder ( ) ;
251+ debugDirectoryBuilder . AddCodeViewEntry ( "MyAssemblyEmbeddedSource.pdb" , pdbContentId , portablePdbBuilder . FormatVersion ) ;
252+ // In case embedded in PE:
253+ // debugDirectoryBuilder.AddEmbeddedPortablePdbEntry(portablePdbBlob, portablePdbBuilder.FormatVersion);
254+ return debugDirectoryBuilder ;
255+ }
256+ #endif
257+
258+
259+ #if NETFRAMEWORK
260+ internal void SetEntryPoint ( MethodInfo mi , PEFileKinds kind )
261+ {
262+ _myAssembly . SetEntryPoint ( mi , kind ) ;
263+ }
264+ #elif NET9_0_OR_GREATER
265+ internal void SetEntryPoint ( MethodBuilder mb )
266+ {
267+ _entryPointMethodBuilder = mb ;
268+ }
269+ #endif
270+
271+
272+ public TypeBuilder DefinePublicType ( string name , Type parent , bool preserveName )
273+ {
274+ return DefineType ( name , parent , TypeAttributes . Public , preserveName ) ;
275+ }
276+
277+ internal TypeBuilder DefineType ( string name , Type parent , TypeAttributes attr , bool preserveName )
278+ {
279+ ContractUtils . RequiresNotNull ( name , nameof ( name ) ) ;
280+ ContractUtils . RequiresNotNull ( parent , nameof ( parent ) ) ;
281+
282+ StringBuilder sb = new StringBuilder ( name ) ;
283+ if ( ! preserveName )
284+ {
285+ int index = Interlocked . Increment ( ref _index ) ;
286+ sb . Append ( "$" ) ;
287+ sb . Append ( index ) ;
288+ }
289+
290+ // There is a bug in Reflection.Emit that leads to
291+ // Unhandled Exception: System.Runtime.InteropServices.COMException (0x80131130): Record not found on lookup.
292+ // if there is any of the characters []*&+,\ in the type name and a method defined on the type is called.
293+ sb . Replace ( '+' , '_' ) . Replace ( '[' , '_' ) . Replace ( ']' , '_' ) . Replace ( '*' , '_' ) . Replace ( '&' , '_' ) . Replace ( ',' , '_' ) . Replace ( '\\ ' , '_' ) ;
294+
295+ name = sb . ToString ( ) ;
296+
297+ return _myModule . DefineType ( name , attr , parent ) ;
298+ }
299+
300+
301+ private const MethodAttributes CtorAttributes = MethodAttributes . RTSpecialName | MethodAttributes . HideBySig | MethodAttributes . Public ;
302+ private const MethodImplAttributes ImplAttributes = MethodImplAttributes . Runtime | MethodImplAttributes . Managed ;
303+ private const MethodAttributes InvokeAttributes = MethodAttributes . Public | MethodAttributes . HideBySig | MethodAttributes . NewSlot | MethodAttributes . Virtual ;
304+ private const TypeAttributes DelegateAttributes = TypeAttributes . Class | TypeAttributes . Public | TypeAttributes . Sealed | TypeAttributes . AnsiClass | TypeAttributes . AutoClass ;
305+ private static readonly Type [ ] _DelegateCtorSignature = new Type [ ] { typeof ( object ) , typeof ( IntPtr ) } ;
306+
307+ public Type MakeDelegateType ( string name , Type [ ] parameters , Type returnType )
308+ {
309+ TypeBuilder builder = DefineType ( name , typeof ( MulticastDelegate ) , DelegateAttributes , false ) ;
310+ builder . DefineConstructor ( CtorAttributes , CallingConventions . Standard , _DelegateCtorSignature ) . SetImplementationFlags ( ImplAttributes ) ;
311+ builder . DefineMethod ( "Invoke" , InvokeAttributes , returnType , parameters ) . SetImplementationFlags ( ImplAttributes ) ;
312+ return builder . CreateTypeInfo ( ) ;
313+ }
314+
315+ }
0 commit comments