2003-09-17 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / class / corlib / System.Reflection.Emit / AssemblyBuilder.cs
1 //
2 // System.Reflection.Emit/AssemblyBuilder.cs
3 //
4 // Author:
5 //   Paolo Molaro (lupus@ximian.com)
6 //
7 // (C) 2001 Ximian, Inc.  http://www.ximian.com
8 //
9
10 using System;
11 using System.Reflection;
12 using System.Resources;
13 using System.IO;
14 using System.Security.Policy;
15 using System.Runtime.Serialization;
16 using System.Globalization;
17 using System.Runtime.CompilerServices;
18 using System.Collections;
19 using System.Runtime.InteropServices;
20
21 namespace System.Reflection.Emit {
22
23         internal struct MonoResource {
24                 public byte[] data;
25                 public string name;
26                 public string filename;
27                 public ResourceAttributes attrs;
28         }
29
30         public sealed class AssemblyBuilder : Assembly {
31                 private IntPtr dynamic_assembly;
32                 private MethodInfo entry_point;
33                 private ModuleBuilder[] modules;
34                 private string name;
35                 private string dir;
36                 private CustomAttributeBuilder[] cattrs;
37                 private MonoResource[] resources;
38                 string keyfile;
39                 string version;
40                 string culture;
41                 uint algid;
42                 uint flags;
43                 PEFileKinds pekind = PEFileKinds.Dll;
44                 bool delay_sign;
45                 uint access;
46                 internal Type corlib_object_type = typeof (System.Object);
47                 internal Type corlib_value_type = typeof (System.ValueType);
48                 internal Type corlib_enum_type = typeof (System.Enum);
49                 internal Type corlib_void_type = typeof (void);
50                 private int[] table_indexes;
51                 Hashtable us_string_cache = new Hashtable ();
52                 ArrayList resource_writers = null;
53                 bool created;
54
55                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
56                 private static extern void basic_init (AssemblyBuilder ab);
57                 
58                 internal AssemblyBuilder (AssemblyName n, string directory, AssemblyBuilderAccess access) {
59                         name = n.Name;
60                         dir = directory;
61                         this.access = (uint)access;
62                         basic_init (this);
63                 }
64
65                 internal int get_next_table_index (object obj, int table, bool inc) {
66                         if (table_indexes == null) {
67                                 table_indexes = new int [64];
68                                 for (int i=0; i < 64; ++i)
69                                         table_indexes [i] = 1;
70                                 /* allow room for .<Module> in TypeDef table */
71                                 table_indexes [0x02] = 2;
72                         }
73                         // Console.WriteLine ("getindex for table "+table.ToString()+" got "+table_indexes [table].ToString());
74                         if (inc)
75                                 return table_indexes [table]++;
76                         return table_indexes [table];
77                 }
78
79                 public override string CodeBase {
80                         get {
81                                 throw not_supported ();
82                         }
83                 }
84                 
85                 public override MethodInfo EntryPoint {
86                         get {
87                                 return entry_point;
88                         }
89                 }
90
91                 public override string Location {
92                         get {
93                                 throw not_supported ();
94                         }
95                 }
96
97                 public void AddResourceFile (string name, string fileName)
98                 {
99                         AddResourceFile (name, fileName, ResourceAttributes.Public);
100                 }
101
102                 public void AddResourceFile (string name, string fileName, ResourceAttributes attribute)
103                 {
104                         AddResourceFile (name, fileName, attribute, true);
105                 }
106
107                 private void AddResourceFile (string name, string fileName, ResourceAttributes attribute, bool fileNeedsToExists)
108                 {
109                         check_name_and_filename (name, fileName, fileNeedsToExists);
110
111                         // Resource files are created/searched under the assembly storage
112                         // directory
113                         if (dir != null)
114                                 fileName = Path.Combine (dir, fileName);
115
116                         if (resources != null) {
117                                 MonoResource[] new_r = new MonoResource [resources.Length + 1];
118                                 System.Array.Copy(resources, new_r, resources.Length);
119                                 resources = new_r;
120                         } else {
121                                 resources = new MonoResource [1];
122                         }
123                         int p = resources.Length - 1;
124                         resources [p].name = name;
125                         resources [p].filename = fileName;
126                         resources [p].attrs = attribute;
127                 }
128
129                 public void EmbedResourceFile (string name, string fileName)
130                 {
131                         EmbedResourceFile (name, fileName, ResourceAttributes.Public);
132                 }
133
134                 public void EmbedResourceFile (string name, string fileName, ResourceAttributes attribute)
135                 {
136                         if (resources != null) {
137                                 MonoResource[] new_r = new MonoResource [resources.Length + 1];
138                                 System.Array.Copy(resources, new_r, resources.Length);
139                                 resources = new_r;
140                         } else {
141                                 resources = new MonoResource [1];
142                         }
143                         int p = resources.Length - 1;
144                         resources [p].name = name;
145                         resources [p].attrs = attribute;
146                         try {
147                                 FileStream s = new FileStream (fileName, FileMode.Open, FileAccess.Read);
148                                 long len = s.Length;
149                                 resources [p].data = new byte [len];
150                                 s.Read (resources [p].data, 0, (int)len);
151                                 s.Close ();
152                         } catch {
153                                 /* do something */
154                         }
155                 }
156
157                 internal void EmbedResource (string name, byte[] blob, ResourceAttributes attribute)
158                 {
159                         if (resources != null) {
160                                 MonoResource[] new_r = new MonoResource [resources.Length + 1];
161                                 System.Array.Copy(resources, new_r, resources.Length);
162                                 resources = new_r;
163                         } else {
164                                 resources = new MonoResource [1];
165                         }
166                         int p = resources.Length - 1;
167                         resources [p].name = name;
168                         resources [p].attrs = attribute;
169                         resources [p].data = blob;
170                 }
171
172                 public ModuleBuilder DefineDynamicModule (string name)
173                 {
174                         return DefineDynamicModule (name, name, false, true);
175                 }
176
177                 public ModuleBuilder DefineDynamicModule (string name, bool emitSymbolInfo)
178                 {
179                         return DefineDynamicModule (name, name, emitSymbolInfo, true);
180                 }
181
182                 public ModuleBuilder DefineDynamicModule(string name, string fileName)
183                 {
184                         return DefineDynamicModule (name, fileName, false, false);
185                 }
186
187                 public ModuleBuilder DefineDynamicModule (string name, string fileName,
188                                                           bool emitSymbolInfo)
189                 {
190                         return DefineDynamicModule (name, fileName, emitSymbolInfo, false);
191                 }
192
193                 public ModuleBuilder DefineDynamicModule (string name, string fileName,
194                                                           bool emitSymbolInfo, bool transient)
195                 {
196                         check_name_and_filename (name, fileName, false);
197
198                         if (!transient) {
199                                 if (Path.GetExtension (fileName) == String.Empty)
200                                         throw new ArgumentException ("Module file name '" + fileName + "' must have file extension.");
201                                 if (!IsSave)
202                                         throw new NotSupportedException ("Persistable modules are not supported in a dynamic assembly created with AssemblyBuilderAccess.Run");
203                                 if (created)
204                                         throw new InvalidOperationException ("Assembly was already saved.");
205                         }
206
207                         ModuleBuilder r = new ModuleBuilder (this, name, fileName, emitSymbolInfo, modules == null, transient);
208
209                         if (modules != null) {
210                                 ModuleBuilder[] new_modules = new ModuleBuilder [modules.Length + 1];
211                                 System.Array.Copy(modules, new_modules, modules.Length);
212                                 new_modules [modules.Length] = r;
213                                 modules = new_modules;
214                         } else {
215                                 modules = new ModuleBuilder [1];
216                                 modules [0] = r;
217                         }
218                         return r;
219                 }
220
221                 public IResourceWriter DefineResource (string name, string description, string fileName)
222                 {
223                         return DefineResource (name, description, fileName, ResourceAttributes.Public);
224                 }
225
226                 public IResourceWriter DefineResource (string name, string description,
227                                                        string fileName, ResourceAttributes attribute)
228                 {
229                         IResourceWriter writer;
230
231                         // description seems to be ignored
232                         AddResourceFile (name, fileName, attribute, false);
233                         writer = new ResourceWriter (fileName);
234                         if (resource_writers == null)
235                                 resource_writers = new ArrayList ();
236                         resource_writers.Add (writer);
237                         return writer;
238                 }
239
240                 [MonoTODO]
241                 public void DefineUnmanagedResource (byte[] resource)
242                 {
243                         if (resource == null)
244                                 throw new ArgumentNullException ("resource");
245
246                         throw new NotImplementedException ();
247                 }
248
249                 [MonoTODO]
250                 public void DefineUnmanagedResource (string resourceFileName)
251                 {
252                         if (resourceFileName == null)
253                                 throw new ArgumentNullException ("resourceFileName");
254                         if (resourceFileName == String.Empty)
255                                 throw new ArgumentException ("resourceFileName");
256                         if (!File.Exists (resourceFileName) || Directory.Exists (resourceFileName))
257                                 throw new FileNotFoundException ("File '" + resourceFileName + "' does not exists or is a directory.");
258
259                         throw new NotImplementedException ();
260                 }
261
262                 [MonoTODO]
263                 public void DefineVersionInfoResource ()
264                 {
265                         throw new NotImplementedException ();
266                 }
267
268                 [MonoTODO]
269                 public void DefineVersionInfoResource (string product, string productVersion,
270                                                        string company, string copyright, string trademark)
271                 {
272                         throw new NotImplementedException ();
273                 }
274
275                 public ModuleBuilder GetDynamicModule (string name)
276                 {
277                         if (name == null)
278                                 throw new ArgumentNullException ("name");
279                         if (name == "")
280                                 throw new ArgumentException ("Name can't be null");
281
282                         if (modules != null)
283                                 for (int i = 0; i < modules.Length; ++i)
284                                         if (modules [i].name == name)
285                                                 return modules [i];
286                         return null;
287                 }
288
289                 public override Type[] GetExportedTypes ()
290                 {
291                         throw not_supported ();
292                 }
293
294                 public override FileStream GetFile (string name)
295                 {
296                         throw not_supported ();
297                 }
298
299                 public override FileStream[] GetFiles() {
300                         throw not_supported ();
301                 }
302
303                 public override FileStream[] GetFiles(bool getResourceModules) {
304                         throw not_supported ();
305                 }
306
307                 public override ManifestResourceInfo GetManifestResourceInfo(string resourceName) {
308                         throw not_supported ();
309                 }
310
311                 public override string[] GetManifestResourceNames() {
312                         throw not_supported ();
313                 }
314
315                 public override Stream GetManifestResourceStream(string name) {
316                         throw not_supported ();
317                 }
318                 public override Stream GetManifestResourceStream(Type type, string name) {
319                         throw not_supported ();
320                 }
321
322                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
323                 private static extern int getUSIndex (AssemblyBuilder ab, string str);
324
325                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
326                 private static extern int getToken (AssemblyBuilder ab, object obj);
327
328                 internal int GetToken (string str) {
329                         if (us_string_cache.Contains (str))
330                                 return (int)us_string_cache [str];
331                         int result = getUSIndex (this, str);
332                         us_string_cache [str] = result;
333                         return result;
334                 }
335                 
336                 internal int GetToken (MemberInfo member) {
337                         return getToken (this, member);
338                 }
339
340                 internal int GetToken (SignatureHelper helper) {
341                         return getToken (this, helper);
342                 }
343
344                 internal bool IsSave {
345                         get {
346                                 return access != (uint)AssemblyBuilderAccess.Run;
347                         }
348                 }
349
350                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
351                 private static extern int getDataChunk (AssemblyBuilder ab, byte[] buf, int offset);
352
353                 public void Save (string assemblyFileName)
354                 {
355                         byte[] buf = new byte [65536];
356                         FileStream file;
357                         int count, offset;
358
359                         if (resource_writers != null) {
360                                 foreach (IResourceWriter writer in resource_writers) {
361                                         writer.Generate ();
362                                         writer.Close ();
363                                 }
364                         }
365
366                         build_metadata (this);
367
368                         foreach (ModuleBuilder module in modules)
369                                 module.Save ();
370
371                         if (dir != null) {
372                                 assemblyFileName = String.Format ("{0}{1}{2}", dir, System.IO.Path.DirectorySeparatorChar, assemblyFileName);
373                         }
374
375                         file = new FileStream (assemblyFileName, FileMode.Create, FileAccess.Write);
376
377                         offset = 0;
378                         while ((count = getDataChunk (this, buf, offset)) != 0) {
379                                 file.Write (buf, 0, count);
380                                 offset += count;
381                         }
382                         file.Close ();
383
384                         created = true;
385
386                         //
387                         // The constant 0x80000000 is internal to Mono, it means `make executable'
388                         //
389                         File.SetAttributes (assemblyFileName, (FileAttributes) (unchecked ((int) 0x80000000)));
390                 }
391
392                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
393                 private static extern void build_metadata (AssemblyBuilder ab);
394
395                 public void SetEntryPoint (MethodInfo entryMethod)
396                 {
397                         SetEntryPoint (entryMethod, PEFileKinds.ConsoleApplication);
398                 }
399
400                 public void SetEntryPoint (MethodInfo entryMethod, PEFileKinds fileKind)
401                 {
402                         if (entryMethod == null)
403                                 throw new ArgumentNullException ("entryMethod");
404                         if (entryMethod.DeclaringType.Assembly != this)
405                                 throw new InvalidOperationException ("Entry method is not defined in the same assembly.");
406
407                         entry_point = entryMethod;
408                         pekind = fileKind;
409                 }
410
411                 public void SetCustomAttribute( CustomAttributeBuilder customBuilder) {
412                         if (customBuilder == null)
413                                 throw new ArgumentNullException ("customBuilder");
414
415                         string attrname = customBuilder.Ctor.ReflectedType.FullName;
416                         byte[] data;
417                         int len, pos;
418                         if (attrname == "System.Reflection.AssemblyVersionAttribute") {
419                                 data = customBuilder.Data;
420                                 pos = 2;
421                                 len = CustomAttributeBuilder.decode_len (data, pos, out pos);
422                                 version = CustomAttributeBuilder.string_from_bytes (data, pos, len);
423                                 return;
424                         } else if (attrname == "System.Reflection.AssemblyKeyFileAttribute") {
425                                 data = customBuilder.Data;
426                                 pos = 2;
427                                 len = CustomAttributeBuilder.decode_len (data, pos, out pos);
428                                 keyfile = CustomAttributeBuilder.string_from_bytes (data, pos, len);
429                         } else if (attrname == "System.Reflection.AssemblyCultureAttribute") {
430                                 data = customBuilder.Data;
431                                 pos = 2;
432                                 len = CustomAttributeBuilder.decode_len (data, pos, out pos);
433                                 culture = CustomAttributeBuilder.string_from_bytes (data, pos, len);
434                         } else if (attrname == "System.Reflection.AssemblyAlgorithmIdAttribute") {
435                                 data = customBuilder.Data;
436                                 pos = 2;
437                                 algid = (uint)data [pos];
438                                 algid |= ((uint)data [pos + 1]) << 8;
439                                 algid |= ((uint)data [pos + 2]) << 16;
440                                 algid |= ((uint)data [pos + 3]) << 24;
441                         } else if (attrname == "System.Reflection.AssemblyFlagsAttribute") {
442                                 data = customBuilder.Data;
443                                 pos = 2;
444                                 flags = (uint)data [pos];
445                                 flags |= ((uint)data [pos + 1]) << 8;
446                                 flags |= ((uint)data [pos + 2]) << 16;
447                                 flags |= ((uint)data [pos + 3]) << 24;
448                                 return;
449                         } else if (attrname == "System.Reflection.AssemblyDelaySignAttribute") {
450                                 data = customBuilder.Data;
451                                 pos = 2;
452                                 delay_sign = data [2] != 0;
453                         }
454                         if (cattrs != null) {
455                                 CustomAttributeBuilder[] new_array = new CustomAttributeBuilder [cattrs.Length + 1];
456                                 cattrs.CopyTo (new_array, 0);
457                                 new_array [cattrs.Length] = customBuilder;
458                                 cattrs = new_array;
459                         } else {
460                                 cattrs = new CustomAttributeBuilder [1];
461                                 cattrs [0] = customBuilder;
462                         }
463                 }
464                 public void SetCustomAttribute( ConstructorInfo con, byte[] binaryAttribute) {
465                         if (con == null)
466                                 throw new ArgumentNullException ("con");
467                         if (binaryAttribute == null)
468                                 throw new ArgumentNullException ("binaryAttribute");
469
470                         SetCustomAttribute (new CustomAttributeBuilder (con, binaryAttribute));
471                 }
472
473                 public void SetCorlibTypeBuilders (Type corlib_object_type, Type corlib_value_type, Type corlib_enum_type) {
474                         this.corlib_object_type = corlib_object_type;
475                         this.corlib_value_type = corlib_value_type;
476                         this.corlib_enum_type = corlib_enum_type;
477                 }
478
479                 public void SetCorlibTypeBuilders (Type corlib_object_type, Type corlib_value_type, Type corlib_enum_type, Type corlib_void_type)
480                 {
481                         SetCorlibTypeBuilders (corlib_object_type, corlib_value_type, corlib_enum_type);
482                         this.corlib_void_type = corlib_void_type;
483                 }
484
485                 private Exception not_supported () {
486                         // Strange message but this is what MS.NET prints...
487                         return new NotSupportedException ("The invoked member is not supported in a dynamic module.");
488                 }
489
490                 private void check_name_and_filename (string name, string fileName,
491                                                                                           bool fileNeedsToExists) {
492                         if (name == null)
493                                 throw new ArgumentNullException ("name");
494                         if (fileName == null)
495                                 throw new ArgumentNullException ("fileName");
496                         if (name == "")
497                                 throw new ArgumentException ("name cannot be empty", "name");
498                         if (fileName == "")
499                                 throw new ArgumentException ("fileName cannot be empty", "fileName");
500                         if (Path.GetFileName (fileName) != fileName)
501                                 throw new ArgumentException ("fileName '" + fileName + "' must not include a path.");
502
503                         // Resource files are created/searched under the assembly storage
504                         // directory
505                         string fullFileName = fileName;
506                         if (dir != null)
507                                 fullFileName = Path.Combine (dir, fileName);
508
509                         if (fileNeedsToExists && !File.Exists (fullFileName))
510                                 throw new FileNotFoundException ("Could not find file '" + fileName + "'");
511
512                         if (resources != null) {
513                                 for (int i = 0; i < resources.Length; ++i) {
514                                         if (resources [i].filename == fullFileName)
515                                                 throw new ArgumentException ("Duplicate file name '" + fileName + "'");
516                                         if (resources [i].name == name)
517                                                 throw new ArgumentException ("Duplicate name '" + name + "'");
518                                 }
519                         }
520
521                         if (modules != null) {
522                                 for (int i = 0; i < modules.Length; ++i) {
523                                         // Use fileName instead of fullFileName here
524                                         if (!modules [i].IsTransient () && (modules [i].FileName == fileName))
525                                                 throw new ArgumentException ("Duplicate file name '" + fileName + "'");
526                                         if (modules [i].Name == name)
527                                                 throw new ArgumentException ("Duplicate name '" + name + "'");
528                                 }
529                         }
530                 }                       
531         }
532 }