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