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