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