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