2010-01-14 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / codegen.cs
1 //
2 // codegen.cs: The code generator
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // Copyright 2001, 2002, 2003 Ximian, Inc.
8 // Copyright 2004 Novell, Inc.
9 //
10
11 //
12 // Please leave this defined on SVN: The idea is that when we ship the
13 // compiler to end users, if the compiler crashes, they have a chance
14 // to narrow down the problem.   
15 //
16 // Only remove it if you need to debug locally on your tree.
17 //
18 //#define PRODUCTION
19
20 using System;
21 using System.IO;
22 using System.Collections.Generic;
23 using System.Globalization;
24 using System.Reflection;
25 using System.Reflection.Emit;
26 using System.Runtime.InteropServices;
27 using System.Security;
28 using System.Security.Cryptography;
29 using System.Security.Permissions;
30
31 using Mono.Security.Cryptography;
32
33 namespace Mono.CSharp {
34
35         /// <summary>
36         ///    Code generator class.
37         /// </summary>
38         public class CodeGen {
39                 static AppDomain current_domain;
40
41                 public static AssemblyClass Assembly;
42
43                 static CodeGen ()
44                 {
45                         Reset ();
46                 }
47
48                 public static void Reset ()
49                 {
50                         Assembly = new AssemblyClass ();
51                 }
52
53                 public static string Basename (string name)
54                 {
55                         int pos = name.LastIndexOf ('/');
56
57                         if (pos != -1)
58                                 return name.Substring (pos + 1);
59
60                         pos = name.LastIndexOf ('\\');
61                         if (pos != -1)
62                                 return name.Substring (pos + 1);
63
64                         return name;
65                 }
66
67                 public static string Dirname (string name)
68                 {
69                         int pos = name.LastIndexOf ('/');
70
71                         if (pos != -1)
72                                 return name.Substring (0, pos);
73
74                         pos = name.LastIndexOf ('\\');
75                         if (pos != -1)
76                                 return name.Substring (0, pos);
77
78                         return ".";
79                 }
80
81                 static public string FileName;
82
83 #if MS_COMPATIBLE
84                 const AssemblyBuilderAccess COMPILER_ACCESS = 0;
85 #else
86                 /* Keep this in sync with System.Reflection.Emit.AssemblyBuilder */
87                 const AssemblyBuilderAccess COMPILER_ACCESS = (AssemblyBuilderAccess) 0x800;
88 #endif
89                                 
90                 //
91                 // Initializes the code generator variables for interactive use (repl)
92                 //
93                 static public void InitDynamic (CompilerContext ctx, string name)
94                 {
95                         current_domain = AppDomain.CurrentDomain;
96                         AssemblyName an = Assembly.GetAssemblyName (name, name);
97                         
98                         Assembly.Builder = current_domain.DefineDynamicAssembly (an, AssemblyBuilderAccess.Run | COMPILER_ACCESS);
99                         RootContext.ToplevelTypes = new ModuleCompiled (ctx, true);
100                         RootContext.ToplevelTypes.Builder = Assembly.Builder.DefineDynamicModule (Basename (name), false);
101                         Assembly.Name = Assembly.Builder.GetName ();
102                 }
103                 
104                 //
105                 // Initializes the code generator variables
106                 //
107                 static public bool Init (string name, string output, bool want_debugging_support, CompilerContext ctx)
108                 {
109                         FileName = output;
110                         AssemblyName an = Assembly.GetAssemblyName (name, output);
111                         if (an == null)
112                                 return false;
113
114                         if (an.KeyPair != null) {
115                                 // If we are going to strong name our assembly make
116                                 // sure all its refs are strong named
117                                 foreach (Assembly a in GlobalRootNamespace.Instance.Assemblies) {
118                                         AssemblyName ref_name = a.GetName ();
119                                         byte [] b = ref_name.GetPublicKeyToken ();
120                                         if (b == null || b.Length == 0) {
121                                                 ctx.Report.Error (1577, "Assembly generation failed " +
122                                                                 "-- Referenced assembly '" +
123                                                                 ref_name.Name +
124                                                                 "' does not have a strong name.");
125                                                 //Environment.Exit (1);
126                                         }
127                                 }
128                         }
129                         
130                         current_domain = AppDomain.CurrentDomain;
131
132                         try {
133                                 Assembly.Builder = current_domain.DefineDynamicAssembly (an,
134                                         AssemblyBuilderAccess.RunAndSave | COMPILER_ACCESS, Dirname (name));
135                         }
136                         catch (ArgumentException) {
137                                 // specified key may not be exportable outside it's container
138                                 if (RootContext.StrongNameKeyContainer != null) {
139                                         ctx.Report.Error (1548, "Could not access the key inside the container `" +
140                                                 RootContext.StrongNameKeyContainer + "'.");
141                                         Environment.Exit (1);
142                                 }
143                                 throw;
144                         }
145                         catch (CryptographicException) {
146                                 if ((RootContext.StrongNameKeyContainer != null) || (RootContext.StrongNameKeyFile != null)) {
147                                         ctx.Report.Error (1548, "Could not use the specified key to strongname the assembly.");
148                                         Environment.Exit (1);
149                                 }
150                                 return false;
151                         }
152
153                         // Get the complete AssemblyName from the builder
154                         // (We need to get the public key and token)
155                         Assembly.Name = Assembly.Builder.GetName ();
156
157                         //
158                         // Pass a path-less name to DefineDynamicModule.  Wonder how
159                         // this copes with output in different directories then.
160                         // FIXME: figure out how this copes with --output /tmp/blah
161                         //
162                         // If the third argument is true, the ModuleBuilder will dynamically
163                         // load the default symbol writer.
164                         //
165                         try {
166                                 RootContext.ToplevelTypes.Builder = Assembly.Builder.DefineDynamicModule (
167                                         Basename (name), Basename (output), want_debugging_support);
168
169 #if !MS_COMPATIBLE
170                                 // TODO: We should use SymbolWriter from DefineDynamicModule
171                                 if (want_debugging_support && !SymbolWriter.Initialize (RootContext.ToplevelTypes.Builder, output)) {
172                                         ctx.Report.Error (40, "Unexpected debug information initialization error `{0}'",
173                                                 "Could not find the symbol writer assembly (Mono.CompilerServices.SymbolWriter.dll)");
174                                         return false;
175                                 }
176 #endif
177                         } catch (ExecutionEngineException e) {
178                                 ctx.Report.Error (40, "Unexpected debug information initialization error `{0}'",
179                                         e.Message);
180                                 return false;
181                         }
182
183                         return true;
184                 }
185
186                 static public void Save (string name, bool saveDebugInfo, Report Report)
187                 {
188                         PortableExecutableKinds pekind;
189                         ImageFileMachine machine;
190
191                         switch (RootContext.Platform) {
192                         case Platform.X86:
193                                 pekind = PortableExecutableKinds.Required32Bit;
194                                 machine = ImageFileMachine.I386;
195                                 break;
196                         case Platform.X64:
197                                 pekind = PortableExecutableKinds.PE32Plus;
198                                 machine = ImageFileMachine.AMD64;
199                                 break;
200                         case Platform.IA64:
201                                 pekind = PortableExecutableKinds.PE32Plus;
202                                 machine = ImageFileMachine.IA64;
203                                 break;
204                         case Platform.AnyCPU:
205                         default:
206                                 pekind = PortableExecutableKinds.ILOnly;
207                                 machine = ImageFileMachine.I386;
208                                 break;
209                         }
210                         try {
211                                 Assembly.Builder.Save (Basename (name), pekind, machine);
212                         }
213                         catch (COMException) {
214                                 if ((RootContext.StrongNameKeyFile == null) || (!RootContext.StrongNameDelaySign))
215                                         throw;
216
217                                 // FIXME: it seems Microsoft AssemblyBuilder doesn't like to delay sign assemblies 
218                                 Report.Error (1548, "Couldn't delay-sign the assembly with the '" +
219                                         RootContext.StrongNameKeyFile +
220                                         "', Use MCS with the Mono runtime or CSC to compile this assembly.");
221                         }
222                         catch (System.IO.IOException io) {
223                                 Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
224                                 return;
225                         }
226                         catch (System.UnauthorizedAccessException ua) {
227                                 Report.Error (16, "Could not write to file `"+name+"', cause: " + ua.Message);
228                                 return;
229                         }
230                         catch (System.NotImplementedException nie) {
231                                 Report.RuntimeMissingSupport (Location.Null, nie.Message);
232                                 return;
233                         }
234
235                         //
236                         // Write debuger symbol file
237                         //
238                         if (saveDebugInfo)
239                                 SymbolWriter.WriteSymbolFile ();
240                         }
241         }
242
243         /// <summary>
244         ///   An Emit Context is created for each body of code (from methods,
245         ///   properties bodies, indexer bodies or constructor bodies)
246         /// </summary>
247         public class EmitContext : BuilderContext
248         {
249                 public ILGenerator ig;
250
251                 /// <summary>
252                 ///   The value that is allowed to be returned or NULL if there is no
253                 ///   return type.
254                 /// </summary>
255                 Type return_type;
256
257                 /// <summary>
258                 ///   Keeps track of the Type to LocalBuilder temporary storage created
259                 ///   to store structures (used to compute the address of the structure
260                 ///   value on structure method invocations)
261                 /// </summary>
262                 Dictionary<Type, object> temporary_storage;
263
264                 /// <summary>
265                 ///   The location where we store the return value.
266                 /// </summary>
267                 public LocalBuilder return_value;
268
269                 /// <summary>
270                 ///   The location where return has to jump to return the
271                 ///   value
272                 /// </summary>
273                 public Label ReturnLabel;
274
275                 /// <summary>
276                 ///   If we already defined the ReturnLabel
277                 /// </summary>
278                 public bool HasReturnLabel;
279
280                 /// <summary>
281                 ///  Whether we are inside an anonymous method.
282                 /// </summary>
283                 public AnonymousExpression CurrentAnonymousMethod;
284                 
285                 public readonly IMemberContext MemberContext;
286
287                 public EmitContext (IMemberContext rc, ILGenerator ig, Type return_type)
288                 {
289                         this.MemberContext = rc;
290                         this.ig = ig;
291
292                         this.return_type = return_type;
293                 }
294
295                 public Type CurrentType {
296                         get { return MemberContext.CurrentType; }
297                 }
298
299                 public TypeParameter[] CurrentTypeParameters {
300                         get { return MemberContext.CurrentTypeParameters; }
301                 }
302
303                 public TypeContainer CurrentTypeDefinition {
304                         get { return MemberContext.CurrentTypeDefinition; }
305                 }
306
307                 public bool IsStatic {
308                         get { return MemberContext.IsStatic; }
309                 }
310
311                 // Has to be used for emitter errors only
312                 public Report Report {
313                         get { return MemberContext.Compiler.Report; }
314                 }
315
316                 public Type ReturnType {
317                         get {
318                                 return return_type;
319                         }
320                 }
321
322                 /// <summary>
323                 ///   This is called immediately before emitting an IL opcode to tell the symbol
324                 ///   writer to which source line this opcode belongs.
325                 /// </summary>
326                 public void Mark (Location loc)
327                 {
328                         if (!SymbolWriter.HasSymbolWriter || HasSet (Options.OmitDebugInfo) || loc.IsNull)
329                                 return;
330
331                         SymbolWriter.MarkSequencePoint (ig, loc);
332                 }
333
334                 public void DefineLocalVariable (string name, LocalBuilder builder)
335                 {
336                         SymbolWriter.DefineLocalVariable (name, builder);
337                 }
338
339                 public void BeginScope ()
340                 {
341                         ig.BeginScope();
342                         SymbolWriter.OpenScope(ig);
343                 }
344
345                 public void EndScope ()
346                 {
347                         ig.EndScope();
348                         SymbolWriter.CloseScope(ig);
349                 }
350
351                 /// <summary>
352                 ///   Returns a temporary storage for a variable of type t as 
353                 ///   a local variable in the current body.
354                 /// </summary>
355                 public LocalBuilder GetTemporaryLocal (Type t)
356                 {
357                         if (temporary_storage != null) {
358                                 object o;
359                                 if (temporary_storage.TryGetValue (t, out o)) {
360                                         if (o is Stack<LocalBuilder>) {
361                                                 var s = (Stack<LocalBuilder>) o;
362                                                 o = s.Count == 0 ? null : s.Pop ();
363                                         } else {
364                                                 temporary_storage.Remove (t);
365                                         }
366                                 }
367                                 if (o != null)
368                                         return (LocalBuilder) o;
369                         }
370                         return ig.DeclareLocal (TypeManager.TypeToReflectionType (t));
371                 }
372
373                 public void FreeTemporaryLocal (LocalBuilder b, Type t)
374                 {
375                         if (temporary_storage == null) {
376                                 temporary_storage = new Dictionary<Type, object> (ReferenceEquality<Type>.Default);
377                                 temporary_storage.Add (t, b);
378                                 return;
379                         }
380                         object o;
381                         
382                         if (!temporary_storage.TryGetValue (t, out o)) {
383                                 temporary_storage.Add (t, b);
384                                 return;
385                         }
386                         var s = o as Stack<LocalBuilder>;
387                         if (s == null) {
388                                 s = new Stack<LocalBuilder> ();
389                                 s.Push ((LocalBuilder)o);
390                                 temporary_storage [t] = s;
391                         }
392                         s.Push (b);
393                 }
394
395                 /// <summary>
396                 ///   Current loop begin and end labels.
397                 /// </summary>
398                 public Label LoopBegin, LoopEnd;
399
400                 /// <summary>
401                 ///   Default target in a switch statement.   Only valid if
402                 ///   InSwitch is true
403                 /// </summary>
404                 public Label DefaultTarget;
405
406                 /// <summary>
407                 ///   If this is non-null, points to the current switch statement
408                 /// </summary>
409                 public Switch Switch;
410
411                 /// <summary>
412                 ///   ReturnValue creates on demand the LocalBuilder for the
413                 ///   return value from the function.  By default this is not
414                 ///   used.  This is only required when returns are found inside
415                 ///   Try or Catch statements.
416                 ///
417                 ///   This method is typically invoked from the Emit phase, so
418                 ///   we allow the creation of a return label if it was not
419                 ///   requested during the resolution phase.   Could be cleaned
420                 ///   up, but it would replicate a lot of logic in the Emit phase
421                 ///   of the code that uses it.
422                 /// </summary>
423                 public LocalBuilder TemporaryReturn ()
424                 {
425                         if (return_value == null){
426                                 return_value = ig.DeclareLocal (return_type);
427                                 if (!HasReturnLabel){
428                                         ReturnLabel = ig.DefineLabel ();
429                                         HasReturnLabel = true;
430                                 }
431                         }
432
433                         return return_value;
434                 }
435         }
436
437         public abstract class CommonAssemblyModulClass : Attributable, IMemberContext
438         {
439                 public void AddAttributes (List<Attribute> attrs, IMemberContext context)
440                 {
441                         foreach (Attribute a in attrs)
442                                 a.AttachTo (this, context);
443
444                         if (attributes == null) {
445                                 attributes = new Attributes (attrs);
446                                 return;
447                         }
448                         attributes.AddAttributes (attrs);
449                 }
450
451                 public virtual void Emit (TypeContainer tc) 
452                 {
453                         if (OptAttributes == null)
454                                 return;
455
456                         OptAttributes.Emit ();
457                 }
458
459                 protected Attribute ResolveAttribute (PredefinedAttribute a_type)
460                 {
461                         Attribute a = OptAttributes.Search (a_type);
462                         if (a != null) {
463                                 a.Resolve ();
464                         }
465                         return a;
466                 }
467
468                 #region IMemberContext Members
469
470                 public CompilerContext Compiler {
471                         get { return RootContext.ToplevelTypes.Compiler; }
472                 }
473
474                 public Type CurrentType {
475                         get { return null; }
476                 }
477
478                 public TypeParameter[] CurrentTypeParameters {
479                         get { return null; }
480                 }
481
482                 public TypeContainer CurrentTypeDefinition {
483                         get { return RootContext.ToplevelTypes; }
484                 }
485
486                 public string GetSignatureForError ()
487                 {
488                         return "<module>";
489                 }
490
491                 public bool IsObsolete {
492                         get { return false; }
493                 }
494
495                 public bool IsUnsafe {
496                         get { return false; }
497                 }
498
499                 public bool IsStatic {
500                         get { return false; }
501                 }
502
503                 public ExtensionMethodGroupExpr LookupExtensionMethod (Type extensionType, string name, Location loc)
504                 {
505                         throw new NotImplementedException ();
506                 }
507
508                 public FullNamedExpression LookupNamespaceOrType (string name, Location loc, bool ignore_cs0104)
509                 {
510                         return RootContext.ToplevelTypes.LookupNamespaceOrType (name, loc, ignore_cs0104);
511                 }
512
513                 public FullNamedExpression LookupNamespaceAlias (string name)
514                 {
515                         return null;
516                 }
517
518                 #endregion
519         }
520                 
521         public class AssemblyClass : CommonAssemblyModulClass {
522                 // TODO: make it private and move all builder based methods here
523                 public AssemblyBuilder Builder;
524                 bool is_cls_compliant;
525                 bool wrap_non_exception_throws;
526
527                 public Attribute ClsCompliantAttribute;
528
529                 Dictionary<SecurityAction, PermissionSet> declarative_security;
530                 bool has_extension_method;              
531                 public AssemblyName Name;
532                 MethodInfo add_type_forwarder;
533                 Dictionary<Type, Attribute> emitted_forwarders;
534
535                 // Module is here just because of error messages
536                 static string[] attribute_targets = new string [] { "assembly", "module" };
537
538                 public AssemblyClass ()
539                 {
540                         wrap_non_exception_throws = true;
541                 }
542
543                 public bool HasExtensionMethods {
544                         set {
545                                 has_extension_method = value;
546                         }
547                 }
548
549                 public bool IsClsCompliant {
550                         get {
551                                 return is_cls_compliant;
552                         }
553                 }
554
555                 public bool WrapNonExceptionThrows {
556                         get {
557                                 return wrap_non_exception_throws;
558                         }
559                 }
560
561                 public override AttributeTargets AttributeTargets {
562                         get {
563                                 return AttributeTargets.Assembly;
564                         }
565                 }
566
567                 public override bool IsClsComplianceRequired ()
568                 {
569                         return is_cls_compliant;
570                 }
571
572                 Report Report {
573                         get { return Compiler.Report; }
574                 }
575
576                 public void Resolve ()
577                 {
578                         if (RootContext.Unsafe) {
579                                 //
580                                 // Emits [assembly: SecurityPermissionAttribute (SecurityAction.RequestMinimum, SkipVerification = true)]
581                                 // when -unsafe option was specified
582                                 //
583                                 
584                                 Location loc = Location.Null;
585
586                                 MemberAccess system_security_permissions = new MemberAccess (new MemberAccess (
587                                         new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Security", loc), "Permissions", loc);
588
589                                 Arguments pos = new Arguments (1);
590                                 pos.Add (new Argument (new MemberAccess (new MemberAccess (system_security_permissions, "SecurityAction", loc), "RequestMinimum")));
591
592                                 Arguments named = new Arguments (1);
593                                 named.Add (new NamedArgument ("SkipVerification", loc, new BoolLiteral (true, loc)));
594
595                                 GlobalAttribute g = new GlobalAttribute (new NamespaceEntry (null, null, null), "assembly",
596                                         new MemberAccess (system_security_permissions, "SecurityPermissionAttribute"),
597                                         new Arguments[] { pos, named }, loc, false);
598                                 g.AttachTo (this, this);
599
600                                 if (g.Resolve () != null) {
601                                         declarative_security = new Dictionary<SecurityAction, PermissionSet> ();
602                                         g.ExtractSecurityPermissionSet (declarative_security);
603                                 }
604                         }
605
606                         if (OptAttributes == null)
607                                 return;
608
609                         // Ensure that we only have GlobalAttributes, since the Search isn't safe with other types.
610                         if (!OptAttributes.CheckTargets())
611                                 return;
612
613                         ClsCompliantAttribute = ResolveAttribute (PredefinedAttributes.Get.CLSCompliant);
614
615                         if (ClsCompliantAttribute != null) {
616                                 is_cls_compliant = ClsCompliantAttribute.GetClsCompliantAttributeValue ();
617                         }
618
619                         Attribute a = ResolveAttribute (PredefinedAttributes.Get.RuntimeCompatibility);
620                         if (a != null) {
621                                 object val = a.GetPropertyValue ("WrapNonExceptionThrows");
622                                 if (val != null)
623                                         wrap_non_exception_throws = (bool) val;
624                         }
625                 }
626
627                 // fix bug #56621
628                 private void SetPublicKey (AssemblyName an, byte[] strongNameBlob) 
629                 {
630                         try {
631                                 // check for possible ECMA key
632                                 if (strongNameBlob.Length == 16) {
633                                         // will be rejected if not "the" ECMA key
634                                         an.SetPublicKey (strongNameBlob);
635                                 }
636                                 else {
637                                         // take it, with or without, a private key
638                                         RSA rsa = CryptoConvert.FromCapiKeyBlob (strongNameBlob);
639                                         // and make sure we only feed the public part to Sys.Ref
640                                         byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa);
641                                         
642                                         // AssemblyName.SetPublicKey requires an additional header
643                                         byte[] publicKeyHeader = new byte [12] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00 };
644
645                                         byte[] encodedPublicKey = new byte [12 + publickey.Length];
646                                         Buffer.BlockCopy (publicKeyHeader, 0, encodedPublicKey, 0, 12);
647                                         Buffer.BlockCopy (publickey, 0, encodedPublicKey, 12, publickey.Length);
648                                         an.SetPublicKey (encodedPublicKey);
649                                 }
650                         }
651                         catch (Exception) {
652                                 Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' is incorrectly encoded");
653                                 Environment.Exit (1);
654                         }
655                 }
656
657                 // TODO: rewrite this code (to kill N bugs and make it faster) and use standard ApplyAttribute way.
658                 public AssemblyName GetAssemblyName (string name, string output) 
659                 {
660                         if (OptAttributes != null) {
661                                 foreach (Attribute a in OptAttributes.Attrs) {
662                                         // cannot rely on any resolve-based members before you call Resolve
663                                         if (a.ExplicitTarget == null || a.ExplicitTarget != "assembly")
664                                                 continue;
665
666                                         // TODO: This code is buggy: comparing Attribute name without resolving is wrong.
667                                         //       However, this is invoked by CodeGen.Init, when none of the namespaces
668                                         //       are loaded yet.
669                                         // TODO: Does not handle quoted attributes properly
670                                         switch (a.Name) {
671                                         case "AssemblyKeyFile":
672                                         case "AssemblyKeyFileAttribute":
673                                         case "System.Reflection.AssemblyKeyFileAttribute":
674                                                 if (RootContext.StrongNameKeyFile != null) {
675                                                         Report.SymbolRelatedToPreviousError (a.Location, a.GetSignatureForError ());
676                                                         Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module",
677                                                                         "keyfile", "System.Reflection.AssemblyKeyFileAttribute");
678                                                 } else {
679                                                         string value = a.GetString ();
680                                                         if (value != null && value.Length != 0)
681                                                                 RootContext.StrongNameKeyFile = value;
682                                                 }
683                                                 break;
684                                         case "AssemblyKeyName":
685                                         case "AssemblyKeyNameAttribute":
686                                         case "System.Reflection.AssemblyKeyNameAttribute":
687                                                 if (RootContext.StrongNameKeyContainer != null) {
688                                                         Report.SymbolRelatedToPreviousError (a.Location, a.GetSignatureForError ());
689                                                         Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module",
690                                                                         "keycontainer", "System.Reflection.AssemblyKeyNameAttribute");
691                                                 } else {
692                                                         string value = a.GetString ();
693                                                         if (value != null && value.Length != 0)
694                                                                 RootContext.StrongNameKeyContainer = value;
695                                                 }
696                                                 break;
697                                         case "AssemblyDelaySign":
698                                         case "AssemblyDelaySignAttribute":
699                                         case "System.Reflection.AssemblyDelaySignAttribute":
700                                                 RootContext.StrongNameDelaySign = a.GetBoolean ();
701                                                 break;
702                                         }
703                                 }
704                         }
705                         
706                         AssemblyName an = new AssemblyName ();
707                         an.Name = Path.GetFileNameWithoutExtension (name);
708
709                         // note: delay doesn't apply when using a key container
710                         if (RootContext.StrongNameKeyContainer != null) {
711                                 an.KeyPair = new StrongNameKeyPair (RootContext.StrongNameKeyContainer);
712                                 return an;
713                         }
714
715                         // strongname is optional
716                         if (RootContext.StrongNameKeyFile == null)
717                                 return an;
718
719                         string AssemblyDir = Path.GetDirectoryName (output);
720
721                         // the StrongName key file may be relative to (a) the compiled
722                         // file or (b) to the output assembly. See bugzilla #55320
723                         // http://bugzilla.ximian.com/show_bug.cgi?id=55320
724
725                         // (a) relative to the compiled file
726                         string filename = Path.GetFullPath (RootContext.StrongNameKeyFile);
727                         bool exist = File.Exists (filename);
728                         if ((!exist) && (AssemblyDir != null) && (AssemblyDir != String.Empty)) {
729                                 // (b) relative to the outputed assembly
730                                 filename = Path.GetFullPath (Path.Combine (AssemblyDir, RootContext.StrongNameKeyFile));
731                                 exist = File.Exists (filename);
732                         }
733
734                         if (exist) {
735                                 using (FileStream fs = new FileStream (filename, FileMode.Open, FileAccess.Read)) {
736                                         byte[] snkeypair = new byte [fs.Length];
737                                         fs.Read (snkeypair, 0, snkeypair.Length);
738
739                                         if (RootContext.StrongNameDelaySign) {
740                                                 // delayed signing - DO NOT include private key
741                                                 SetPublicKey (an, snkeypair);
742                                         }
743                                         else {
744                                                 // no delay so we make sure we have the private key
745                                                 try {
746                                                         CryptoConvert.FromCapiPrivateKeyBlob (snkeypair);
747                                                         an.KeyPair = new StrongNameKeyPair (snkeypair);
748                                                 }
749                                                 catch (CryptographicException) {
750                                                         if (snkeypair.Length == 16) {
751                                                                 // error # is different for ECMA key
752                                                                 Report.Error (1606, "Could not sign the assembly. " + 
753                                                                         "ECMA key can only be used to delay-sign assemblies");
754                                                         }
755                                                         else {
756                                                                 Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' does not have a private key");
757                                                         }
758                                                         return null;
759                                                 }
760                                         }
761                                 }
762                         }
763                         else {
764                                 Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' does not exist");
765                                 return null;
766                         }
767                         return an;
768                 }
769
770                 void Error_AssemblySigning (string text)
771                 {
772                         Report.Error (1548, "Error during assembly signing. " + text);
773                 }
774
775                 bool CheckInternalsVisibleAttribute (Attribute a)
776                 {
777                         string assembly_name = a.GetString ();
778                         if (assembly_name.Length == 0)
779                                 return false;
780                                 
781                         AssemblyName aname = null;
782                         try {
783                                 aname = new AssemblyName (assembly_name);
784                         } catch (FileLoadException) {
785                         } catch (ArgumentException) {
786                         }
787                                 
788                         // Bad assembly name format
789                         if (aname == null)
790                                 Report.Warning (1700, 3, a.Location, "Assembly reference `" + assembly_name + "' is invalid and cannot be resolved");
791                         // Report error if we have defined Version or Culture
792                         else if (aname.Version != null || aname.CultureInfo != null)
793                                 throw new Exception ("Friend assembly `" + a.GetString () + 
794                                                 "' is invalid. InternalsVisibleTo cannot have version or culture specified.");
795                         else if (aname.GetPublicKey () == null && Name.GetPublicKey () != null && Name.GetPublicKey ().Length != 0) {
796                                 Report.Error (1726, a.Location, "Friend assembly reference `" + aname.FullName + "' is invalid." +
797                                                 " Strong named assemblies must specify a public key in their InternalsVisibleTo declarations");
798                                 return false;
799                         }
800
801                         return true;
802                 }
803
804                 static bool IsValidAssemblyVersion (string version)
805                 {
806                         Version v;
807                         try {
808                                 v = new Version (version);
809                         } catch {
810                                 try {
811                                         int major = int.Parse (version, CultureInfo.InvariantCulture);
812                                         v = new Version (major, 0);
813                                 } catch {
814                                         return false;
815                                 }
816                         }
817
818                         foreach (int candidate in new int [] { v.Major, v.Minor, v.Build, v.Revision }) {
819                                 if (candidate > ushort.MaxValue)
820                                         return false;
821                         }
822
823                         return true;
824                 }
825
826                 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb, PredefinedAttributes pa)
827                 {
828                         if (a.IsValidSecurityAttribute ()) {
829                                 if (declarative_security == null)
830                                         declarative_security = new Dictionary<SecurityAction, PermissionSet> ();
831
832                                 a.ExtractSecurityPermissionSet (declarative_security);
833                                 return;
834                         }
835
836                         if (a.Type == pa.AssemblyCulture) {
837                                 string value = a.GetString ();
838                                 if (value == null || value.Length == 0)
839                                         return;
840
841                                 if (RootContext.Target == Target.Exe) {
842                                         a.Error_AttributeEmitError ("The executables cannot be satelite assemblies, remove the attribute or keep it empty");
843                                         return;
844                                 }
845                         }
846
847                         if (a.Type == pa.AssemblyVersion) {
848                                 string value = a.GetString ();
849                                 if (value == null || value.Length == 0)
850                                         return;
851
852                                 value = value.Replace ('*', '0');
853
854                                 if (!IsValidAssemblyVersion (value)) {
855                                         a.Error_AttributeEmitError (string.Format ("Specified version `{0}' is not valid", value));
856                                         return;
857                                 }
858                         }
859
860                         if (a.Type == pa.InternalsVisibleTo && !CheckInternalsVisibleAttribute (a))
861                                 return;
862
863                         if (a.Type == pa.TypeForwarder) {
864                                 Type t = a.GetArgumentType ();
865                                 if (t == null || TypeManager.HasElementType (t)) {
866                                         Report.Error (735, a.Location, "Invalid type specified as an argument for TypeForwardedTo attribute");
867                                         return;
868                                 }
869
870                                 t = TypeManager.DropGenericTypeArguments (t);
871                                 if (emitted_forwarders == null) {
872                                         emitted_forwarders = new Dictionary<Type, Attribute>  ();
873                                 } else if (emitted_forwarders.ContainsKey (t)) {
874                                         Report.SymbolRelatedToPreviousError(emitted_forwarders[t].Location, null);
875                                         Report.Error(739, a.Location, "A duplicate type forward of type `{0}'",
876                                                 TypeManager.CSharpName(t));
877                                         return;
878                                 }
879
880                                 emitted_forwarders.Add(t, a);
881
882                                 if (TypeManager.LookupDeclSpace (t) != null) {
883                                         Report.SymbolRelatedToPreviousError (t);
884                                         Report.Error (729, a.Location, "Cannot forward type `{0}' because it is defined in this assembly",
885                                                 TypeManager.CSharpName (t));
886                                         return;
887                                 }
888
889                                 if (t.DeclaringType != null) {
890                                         Report.Error (730, a.Location, "Cannot forward type `{0}' because it is a nested type",
891                                                 TypeManager.CSharpName (t));
892                                         return;
893                                 }
894
895                                 if (add_type_forwarder == null) {
896                                         add_type_forwarder = typeof (AssemblyBuilder).GetMethod ("AddTypeForwarder",
897                                                 BindingFlags.NonPublic | BindingFlags.Instance);
898
899                                         if (add_type_forwarder == null) {
900                                                 Report.RuntimeMissingSupport (a.Location, "TypeForwardedTo attribute");
901                                                 return;
902                                         }
903                                 }
904
905                                 add_type_forwarder.Invoke (Builder, new object[] { t });
906                                 return;
907                         }
908                         
909                         if (a.Type == pa.Extension) {
910                                 a.Error_MisusedExtensionAttribute ();
911                                 return;
912                         }
913
914                         Builder.SetCustomAttribute (cb);
915                 }
916
917                 public override void Emit (TypeContainer tc)
918                 {
919                         base.Emit (tc);
920
921                         if (has_extension_method)
922                                 PredefinedAttributes.Get.Extension.EmitAttribute (Builder);
923
924                         // FIXME: Does this belong inside SRE.AssemblyBuilder instead?
925                         PredefinedAttribute pa = PredefinedAttributes.Get.RuntimeCompatibility;
926                         if (pa.IsDefined && (OptAttributes == null || !OptAttributes.Contains (pa))) {
927                                 ConstructorInfo ci = TypeManager.GetPredefinedConstructor (
928                                         pa.Type, Location.Null, Type.EmptyTypes);
929                                 PropertyInfo [] pis = new PropertyInfo [1];
930                                 pis [0] = TypeManager.GetPredefinedProperty (pa.Type,
931                                         "WrapNonExceptionThrows", Location.Null, TypeManager.bool_type).MetaInfo;
932                                 object [] pargs = new object [1];
933                                 pargs [0] = true;
934                                 Builder.SetCustomAttribute (new CustomAttributeBuilder (ci, new object [0], pis, pargs));
935                         }
936
937                         if (declarative_security != null) {
938
939                                 MethodInfo add_permission = typeof (AssemblyBuilder).GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic);
940                                 object builder_instance = Builder;
941
942                                 try {
943                                         // Microsoft runtime hacking
944                                         if (add_permission == null) {
945                                                 Type assembly_builder = typeof (AssemblyBuilder).Assembly.GetType ("System.Reflection.Emit.AssemblyBuilderData");
946                                                 add_permission = assembly_builder.GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic);
947
948                                                 FieldInfo fi = typeof (AssemblyBuilder).GetField ("m_assemblyData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
949                                                 builder_instance = fi.GetValue (Builder);
950                                         }
951
952                                         var args = new PermissionSet [3];
953                                         declarative_security.TryGetValue (SecurityAction.RequestMinimum, out args [0]);
954                                         declarative_security.TryGetValue (SecurityAction.RequestOptional, out args [1]);
955                                         declarative_security.TryGetValue (SecurityAction.RequestRefuse, out args [2]);
956                                         add_permission.Invoke (builder_instance, args);
957                                 }
958                                 catch {
959                                         Report.RuntimeMissingSupport (Location.Null, "assembly permission setting");
960                                 }
961                         }
962                 }
963
964                 public override string[] ValidAttributeTargets {
965                         get {
966                                 return attribute_targets;
967                         }
968                 }
969
970                 // Wrapper for AssemblyBuilder.AddModule
971                 static MethodInfo adder_method;
972                 static public MethodInfo AddModule_Method {
973                         get {
974                                 if (adder_method == null)
975                                         adder_method = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.NonPublic);
976                                 return adder_method;
977                         }
978                 }
979                 public Module AddModule (string module)
980                 {
981                         MethodInfo m = AddModule_Method;
982                         if (m == null) {
983                                 Report.RuntimeMissingSupport (Location.Null, "/addmodule");
984                                 Environment.Exit (1);
985                         }
986
987                         try {
988                                 return (Module) m.Invoke (Builder, new object [] { module });
989                         } catch (TargetInvocationException ex) {
990                                 throw ex.InnerException;
991                         }
992                 }               
993         }
994 }