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