2004-03-25 Sebastien Pouliot <sebastien@ximian.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 // (C) 2001 Ximian, Inc.
8 //
9
10 using System;
11 using System.IO;
12 using System.Collections;
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Security.Cryptography;
16
17 using Mono.Security.Cryptography;
18
19 namespace Mono.CSharp {
20
21         /// <summary>
22         ///    Code generator class.
23         /// </summary>
24         public class CodeGen {
25                 static AppDomain current_domain;
26                 static public SymbolWriter SymbolWriter;
27
28                 public static AssemblyClass Assembly;
29                 public static ModuleClass Module;
30
31                 static CodeGen ()
32                 {
33                         Assembly = new AssemblyClass ();
34                         Module = new ModuleClass (RootContext.Unsafe);
35                 }
36
37                 public static string Basename (string name)
38                 {
39                         int pos = name.LastIndexOf ('/');
40
41                         if (pos != -1)
42                                 return name.Substring (pos + 1);
43
44                         pos = name.LastIndexOf ('\\');
45                         if (pos != -1)
46                                 return name.Substring (pos + 1);
47
48                         return name;
49                 }
50
51                 public static string Dirname (string name)
52                 {
53                         int pos = name.LastIndexOf ('/');
54
55                         if (pos != -1)
56                                 return name.Substring (0, pos);
57
58                         pos = name.LastIndexOf ('\\');
59                         if (pos != -1)
60                                 return name.Substring (0, pos);
61
62                         return ".";
63                 }
64
65                 static string TrimExt (string name)
66                 {
67                         int pos = name.LastIndexOf ('.');
68
69                         return name.Substring (0, pos);
70                 }
71
72                 static public string FileName;
73
74                 //
75                 // Initializes the symbol writer
76                 //
77                 static void InitializeSymbolWriter ()
78                 {
79                         SymbolWriter = SymbolWriter.GetSymbolWriter (Module.Builder);
80
81                         //
82                         // If we got an ISymbolWriter instance, initialize it.
83                         //
84                         if (SymbolWriter == null) {
85                                 Report.Warning (
86                                         -18, "Could not find the symbol writer assembly (Mono.CSharp.Debugger.dll). This is normally an installation problem. Please make sure to compile and install the mcs/class/Mono.CSharp.Debugger directory.");
87                                 return;
88                         }
89                 }
90
91                 //
92                 // Initializes the code generator variables
93                 //
94                 static public void Init (string name, string output, bool want_debugging_support)
95                 {
96                         FileName = output;
97                         AssemblyName an = Assembly.GetAssemblyName (name, output);
98                         
99                         current_domain = AppDomain.CurrentDomain;
100
101                         try {
102                                 Assembly.Builder = current_domain.DefineDynamicAssembly (an,
103                                         AssemblyBuilderAccess.Save, Dirname (name));
104                         }
105                         catch (ArgumentException) {
106                                 // specified key may not be exportable outside it's container
107                                 if (RootContext.StrongNameKeyContainer != null) {
108                                         Report.Error (1548, "Could not access the key inside the container `" +
109                                                 RootContext.StrongNameKeyContainer + "'.");
110                                         Environment.Exit (1);
111                                 }
112                                 throw;
113                         }
114                         catch (CryptographicException) {
115                                 if ((RootContext.StrongNameKeyContainer != null) || (RootContext.StrongNameKeyFile != null)) {
116                                         Report.Error (1548, "Could not use the specified key to strongname the assembly.");
117                                         Environment.Exit (1);
118                                 }
119                                 throw;
120                         }
121
122                         //
123                         // Pass a path-less name to DefineDynamicModule.  Wonder how
124                         // this copes with output in different directories then.
125                         // FIXME: figure out how this copes with --output /tmp/blah
126                         //
127                         // If the third argument is true, the ModuleBuilder will dynamically
128                         // load the default symbol writer.
129                         //
130                         Module.Builder = Assembly.Builder.DefineDynamicModule (
131                                 Basename (name), Basename (output), want_debugging_support);
132
133                         if (want_debugging_support)
134                                 InitializeSymbolWriter ();
135                 }
136
137                 static public void Save (string name)
138                 {
139                         try {
140                                 Assembly.Builder.Save (Basename (name));
141                         } catch (System.IO.IOException io){
142                                 Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
143                         }
144                 }
145         }
146
147         //
148         // Provides "local" store across code that can yield: locals
149         // or fields, notice that this should not be used by anonymous
150         // methods to create local storage, those only require
151         // variable mapping.
152         //
153         public class VariableStorage {
154                 ILGenerator ig;
155                 FieldBuilder fb;
156                 LocalBuilder local;
157                 
158                 static int count;
159                 
160                 public VariableStorage (EmitContext ec, Type t)
161                 {
162                         count++;
163                         if (ec.InIterator)
164                                 fb = IteratorHandler.Current.MapVariable ("s_", count.ToString (), t);
165                         else
166                                 local = ec.ig.DeclareLocal (t);
167                         ig = ec.ig;
168                 }
169
170                 public void EmitThis ()
171                 {
172                         if (fb != null)
173                                 ig.Emit (OpCodes.Ldarg_0);
174                 }
175
176                 public void EmitStore ()
177                 {
178                         if (fb == null)
179                                 ig.Emit (OpCodes.Stloc, local);
180                         else
181                                 ig.Emit (OpCodes.Stfld, fb);
182                 }
183
184                 public void EmitLoad ()
185                 {
186                         if (fb == null)
187                                 ig.Emit (OpCodes.Ldloc, local);
188                         else 
189                                 ig.Emit (OpCodes.Ldfld, fb);
190                 }
191                 
192                 public void EmitCall (MethodInfo mi)
193                 {
194                         // FIXME : we should handle a call like tostring
195                         // here, where boxing is needed. However, we will
196                         // never encounter that with the current usage.
197                         
198                         bool value_type_call;
199                         EmitThis ();
200                         if (fb == null) {
201                                 value_type_call = local.LocalType.IsValueType;
202                                 
203                                 if (value_type_call)
204                                         ig.Emit (OpCodes.Ldloca, local);
205                                 else
206                                         ig.Emit (OpCodes.Ldloc, local);
207                         } else {
208                                 value_type_call = fb.FieldType.IsValueType;
209                                 
210                                 if (value_type_call)
211                                         ig.Emit (OpCodes.Ldflda, fb);
212                                 else
213                                         ig.Emit (OpCodes.Ldfld, fb);
214                         }
215                         
216                         ig.Emit (value_type_call ? OpCodes.Call : OpCodes.Callvirt, mi);
217                 }
218         }
219         
220         /// <summary>
221         ///   An Emit Context is created for each body of code (from methods,
222         ///   properties bodies, indexer bodies or constructor bodies)
223         /// </summary>
224         public class EmitContext {
225                 public DeclSpace DeclSpace;
226                 public DeclSpace TypeContainer;
227                 public ILGenerator   ig;
228
229                 /// <summary>
230                 ///   This variable tracks the `checked' state of the compilation,
231                 ///   it controls whether we should generate code that does overflow
232                 ///   checking, or if we generate code that ignores overflows.
233                 ///
234                 ///   The default setting comes from the command line option to generate
235                 ///   checked or unchecked code plus any source code changes using the
236                 ///   checked/unchecked statements or expressions.   Contrast this with
237                 ///   the ConstantCheckState flag.
238                 /// </summary>
239                 
240                 public bool CheckState;
241
242                 /// <summary>
243                 ///   The constant check state is always set to `true' and cant be changed
244                 ///   from the command line.  The source code can change this setting with
245                 ///   the `checked' and `unchecked' statements and expressions. 
246                 /// </summary>
247                 public bool ConstantCheckState;
248
249                 /// <summary>
250                 ///   Whether we are emitting code inside a static or instance method
251                 /// </summary>
252                 public bool IsStatic;
253
254                 /// <summary>
255                 ///   Whether we are emitting a field initializer
256                 /// </summary>
257                 public bool IsFieldInitializer;
258
259                 /// <summary>
260                 ///   The value that is allowed to be returned or NULL if there is no
261                 ///   return type.
262                 /// </summary>
263                 public Type ReturnType;
264
265                 /// <summary>
266                 ///   Points to the Type (extracted from the TypeContainer) that
267                 ///   declares this body of code
268                 /// </summary>
269                 public Type ContainerType;
270                 
271                 /// <summary>
272                 ///   Whether this is generating code for a constructor
273                 /// </summary>
274                 public bool IsConstructor;
275
276                 /// <summary>
277                 ///   Whether we're control flow analysis enabled
278                 /// </summary>
279                 public bool DoFlowAnalysis;
280                 
281                 /// <summary>
282                 ///   Keeps track of the Type to LocalBuilder temporary storage created
283                 ///   to store structures (used to compute the address of the structure
284                 ///   value on structure method invocations)
285                 /// </summary>
286                 public Hashtable temporary_storage;
287
288                 public Block CurrentBlock;
289
290                 public int CurrentFile;
291
292                 /// <summary>
293                 ///   The location where we store the return value.
294                 /// </summary>
295                 LocalBuilder return_value;
296
297                 /// <summary>
298                 ///   The location where return has to jump to return the
299                 ///   value
300                 /// </summary>
301                 public Label ReturnLabel;
302
303                 /// <summary>
304                 ///   If we already defined the ReturnLabel
305                 /// </summary>
306                 public bool HasReturnLabel;
307
308                 /// <summary>
309                 ///   Whether we are inside an iterator block.
310                 /// </summary>
311                 public bool InIterator;
312
313                 public bool IsLastStatement;
314
315                 /// <summary>
316                 ///   Whether remapping of locals, parameters and fields is turned on.
317                 ///   Used by iterators and anonymous methods.
318                 /// </summary>
319                 public bool RemapToProxy;
320
321                 /// <summary>
322                 ///  Whether we are inside an unsafe block
323                 /// </summary>
324                 public bool InUnsafe;
325
326                 /// <summary>
327                 ///  Whether we are in a `fixed' initialization
328                 /// </summary>
329                 public bool InFixedInitializer;
330
331                 /// <summary>
332                 ///  Whether we are inside an anonymous method.
333                 /// </summary>
334                 public bool InAnonymousMethod;
335                 
336                 /// <summary>
337                 ///   Location for this EmitContext
338                 /// </summary>
339                 public Location loc;
340
341                 /// <summary>
342                 ///   Used to flag that it is ok to define types recursively, as the
343                 ///   expressions are being evaluated as part of the type lookup
344                 ///   during the type resolution process
345                 /// </summary>
346                 public bool ResolvingTypeTree;
347                 
348                 /// <summary>
349                 ///   Inside an enum definition, we do not resolve enumeration values
350                 ///   to their enumerations, but rather to the underlying type/value
351                 ///   This is so EnumVal + EnumValB can be evaluated.
352                 ///
353                 ///   There is no "E operator + (E x, E y)", so during an enum evaluation
354                 ///   we relax the rules
355                 /// </summary>
356                 public bool InEnumContext;
357
358                 FlowBranching current_flow_branching;
359                 
360                 public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
361                                     Type return_type, int code_flags, bool is_constructor)
362                 {
363                         this.ig = ig;
364
365                         TypeContainer = parent;
366                         DeclSpace = ds;
367                         CheckState = RootContext.Checked;
368                         ConstantCheckState = true;
369                         
370                         IsStatic = (code_flags & Modifiers.STATIC) != 0;
371                         InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
372                         RemapToProxy = InIterator;
373                         ReturnType = return_type;
374                         IsConstructor = is_constructor;
375                         CurrentBlock = null;
376                         CurrentFile = 0;
377                         
378                         if (parent != null){
379                                 // Can only be null for the ResolveType contexts.
380                                 ContainerType = parent.TypeBuilder;
381                                 if (parent.UnsafeContext)
382                                         InUnsafe = true;
383                                 else
384                                         InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
385                         }
386                         loc = l;
387                         
388                         if (ReturnType == TypeManager.void_type)
389                                 ReturnType = null;
390                 }
391
392                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
393                                     Type return_type, int code_flags, bool is_constructor)
394                         : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
395                 {
396                 }
397
398                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
399                                     Type return_type, int code_flags)
400                         : this (tc, tc, l, ig, return_type, code_flags, false)
401                 {
402                 }
403
404                 public FlowBranching CurrentBranching {
405                         get {
406                                 return current_flow_branching;
407                         }
408                 }
409
410                 // <summary>
411                 //   Starts a new code branching.  This inherits the state of all local
412                 //   variables and parameters from the current branching.
413                 // </summary>
414                 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
415                 {
416                         current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
417                         return current_flow_branching;
418                 }
419
420                 // <summary>
421                 //   Starts a new code branching for block `block'.
422                 // </summary>
423                 public FlowBranching StartFlowBranching (Block block)
424                 {
425                         FlowBranching.BranchingType type;
426
427                         if (CurrentBranching.Type == FlowBranching.BranchingType.Switch)
428                                 type = FlowBranching.BranchingType.SwitchSection;
429                         else
430                                 type = FlowBranching.BranchingType.Block;
431
432                         current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, block, block.StartLocation);
433                         return current_flow_branching;
434                 }
435
436                 // <summary>
437                 //   Ends a code branching.  Merges the state of locals and parameters
438                 //   from all the children of the ending branching.
439                 // </summary>
440                 public FlowBranching.UsageVector DoEndFlowBranching ()
441                 {
442                         FlowBranching old = current_flow_branching;
443                         current_flow_branching = current_flow_branching.Parent;
444
445                         return current_flow_branching.MergeChild (old);
446                 }
447
448                 // <summary>
449                 //   Ends a code branching.  Merges the state of locals and parameters
450                 //   from all the children of the ending branching.
451                 // </summary>
452                 public FlowBranching.Reachability EndFlowBranching ()
453                 {
454                         FlowBranching.UsageVector vector = DoEndFlowBranching ();
455
456                         return vector.Reachability;
457                 }
458
459                 // <summary>
460                 //   Kills the current code branching.  This throws away any changed state
461                 //   information and should only be used in case of an error.
462                 // </summary>
463                 public void KillFlowBranching ()
464                 {
465                         current_flow_branching = current_flow_branching.Parent;
466                 }
467
468                 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
469                 {
470                         bool unreachable = false;
471
472                         if (!Location.IsNull (loc))
473                                 CurrentFile = loc.File;
474
475                         if (block != null){
476                             try {
477                                 int errors = Report.Errors;
478
479                                 block.EmitMeta (this, ip);
480
481                                 if (Report.Errors == errors){
482                                         bool old_do_flow_analysis = DoFlowAnalysis;
483                                         DoFlowAnalysis = true;
484
485                                         current_flow_branching = FlowBranching.CreateBranching (
486                                                 null, FlowBranching.BranchingType.Block, block, loc);
487
488                                         if (!block.Resolve (this)) {
489                                                 current_flow_branching = null;
490                                                 DoFlowAnalysis = old_do_flow_analysis;
491                                                 return;
492                                         }
493
494                                         FlowBranching.Reachability reachability = current_flow_branching.MergeTopBlock ();
495                                         current_flow_branching = null;
496                                         
497                                         DoFlowAnalysis = old_do_flow_analysis;
498
499                                         block.Emit (this);
500
501                                         if (reachability.AlwaysReturns ||
502                                             reachability.AlwaysThrows ||
503                                             reachability.IsUnreachable)
504                                                 unreachable = true;
505                                 }
506                             } catch (Exception e) {
507                                         Console.WriteLine ("Exception caught by the compiler while compiling:");
508                                         Console.WriteLine ("   Block that caused the problem begin at: " + loc);
509                                         
510                                         if (CurrentBlock != null){
511                                                 Console.WriteLine ("                     Block being compiled: [{0},{1}]",
512                                                                    CurrentBlock.StartLocation, CurrentBlock.EndLocation);
513                                         }
514                                         Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
515                                         Console.WriteLine (Report.FriendlyStackTrace (e));
516                                         
517                                         Environment.Exit (1);
518                             }
519                         }
520
521                         if (ReturnType != null && !unreachable){
522                                 if (!InIterator){
523                                         Report.Error (161, loc, "Not all code paths return a value");
524                                         return;
525                                 }
526                         }
527
528                         if (HasReturnLabel)
529                                 ig.MarkLabel (ReturnLabel);
530                         if (return_value != null){
531                                 ig.Emit (OpCodes.Ldloc, return_value);
532                                 ig.Emit (OpCodes.Ret);
533                         } else {
534                                 //
535                                 // If `HasReturnLabel' is set, then we already emitted a
536                                 // jump to the end of the method, so we must emit a `ret'
537                                 // there.
538                                 //
539                                 // Unfortunately, System.Reflection.Emit automatically emits
540                                 // a leave to the end of a finally block.  This is a problem
541                                 // if no code is following the try/finally block since we may
542                                 // jump to a point after the end of the method.
543                                 // As a workaround, we're always creating a return label in
544                                 // this case.
545                                 //
546
547                                 if ((block != null) && block.IsDestructor) {
548                                         // Nothing to do; S.R.E automatically emits a leave.
549                                 } else if (HasReturnLabel || (!unreachable && !InIterator)) {
550                                         if (ReturnType != null)
551                                                 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
552                                         ig.Emit (OpCodes.Ret);
553                                 }
554                         }
555                 }
556
557                 /// <summary>
558                 ///   This is called immediately before emitting an IL opcode to tell the symbol
559                 ///   writer to which source line this opcode belongs.
560                 /// </summary>
561                 public void Mark (Location loc, bool check_file)
562                 {
563                         if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
564                                 return;
565
566                         if (check_file && (CurrentFile != loc.File))
567                                 return;
568
569                         ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
570                 }
571
572                 /// <summary>
573                 ///   Returns a temporary storage for a variable of type t as 
574                 ///   a local variable in the current body.
575                 /// </summary>
576                 public LocalBuilder GetTemporaryLocal (Type t)
577                 {
578                         LocalBuilder location = null;
579                         
580                         if (temporary_storage != null){
581                                 object o = temporary_storage [t];
582                                 if (o != null){
583                                         if (o is ArrayList){
584                                                 ArrayList al = (ArrayList) o;
585                                                 
586                                                 for (int i = 0; i < al.Count; i++){
587                                                         if (al [i] != null){
588                                                                 location = (LocalBuilder) al [i];
589                                                                 al [i] = null;
590                                                                 break;
591                                                         }
592                                                 }
593                                         } else
594                                                 location = (LocalBuilder) o;
595                                         if (location != null)
596                                                 return location;
597                                 }
598                         }
599                         
600                         return ig.DeclareLocal (t);
601                 }
602
603                 public void FreeTemporaryLocal (LocalBuilder b, Type t)
604                 {
605                         if (temporary_storage == null){
606                                 temporary_storage = new Hashtable ();
607                                 temporary_storage [t] = b;
608                                 return;
609                         }
610                         object o = temporary_storage [t];
611                         if (o == null){
612                                 temporary_storage [t] = b;
613                                 return;
614                         }
615                         if (o is ArrayList){
616                                 ArrayList al = (ArrayList) o;
617                                 for (int i = 0; i < al.Count; i++){
618                                         if (al [i] == null){
619                                                 al [i] = b;
620                                                 return;
621                                         }
622                                 }
623                                 al.Add (b);
624                                 return;
625                         }
626                         ArrayList replacement = new ArrayList ();
627                         replacement.Add (o);
628                         temporary_storage.Remove (t);
629                         temporary_storage [t] = replacement;
630                 }
631
632                 /// <summary>
633                 ///   Current loop begin and end labels.
634                 /// </summary>
635                 public Label LoopBegin, LoopEnd;
636
637                 /// <summary>
638                 ///   Default target in a switch statement.   Only valid if
639                 ///   InSwitch is true
640                 /// </summary>
641                 public Label DefaultTarget;
642
643                 /// <summary>
644                 ///   If this is non-null, points to the current switch statement
645                 /// </summary>
646                 public Switch Switch;
647
648                 /// <summary>
649                 ///   ReturnValue creates on demand the LocalBuilder for the
650                 ///   return value from the function.  By default this is not
651                 ///   used.  This is only required when returns are found inside
652                 ///   Try or Catch statements.
653                 /// </summary>
654                 public LocalBuilder TemporaryReturn ()
655                 {
656                         if (return_value == null){
657                                 return_value = ig.DeclareLocal (ReturnType);
658                                 ReturnLabel = ig.DefineLabel ();
659                                 HasReturnLabel = true;
660                         }
661
662                         return return_value;
663                 }
664
665                 public void NeedReturnLabel ()
666                 {
667                         if (!HasReturnLabel) {
668                                 ReturnLabel = ig.DefineLabel ();
669                                 HasReturnLabel = true;
670                         }
671                 }
672
673                 //
674                 // Creates a field `name' with the type `t' on the proxy class
675                 //
676                 public FieldBuilder MapVariable (string name, Type t)
677                 {
678                         if (InIterator){
679                                 return IteratorHandler.Current.MapVariable ("v_", name, t);
680                         }
681
682                         throw new Exception ("MapVariable for an unknown state");
683                 }
684
685                 //
686                 // Invoke this routine to remap a VariableInfo into the
687                 // proper MemberAccess expression
688                 //
689                 public Expression RemapLocal (LocalInfo local_info)
690                 {
691                         FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
692                         fe.InstanceExpression = new ProxyInstance ();
693                         return fe.DoResolve (this);
694                 }
695
696                 public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side)
697                 {
698                         FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
699                         fe.InstanceExpression = new ProxyInstance ();
700                         return fe.DoResolveLValue (this, right_side);
701                 }
702
703                 public Expression RemapParameter (int idx)
704                 {
705                         FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
706                         fe.InstanceExpression = new ProxyInstance ();
707                         return fe.DoResolve (this);
708                 }
709
710                 public Expression RemapParameterLValue (int idx, Expression right_side)
711                 {
712                         FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
713                         fe.InstanceExpression = new ProxyInstance ();
714                         return fe.DoResolveLValue (this, right_side);
715                 }
716                 
717                 //
718                 // Emits the proper object to address fields on a remapped
719                 // variable/parameter to field in anonymous-method/iterator proxy classes.
720                 //
721                 public void EmitThis ()
722                 {
723                         ig.Emit (OpCodes.Ldarg_0);
724
725                         if (!IsStatic){
726                                 if (InIterator)
727                                         ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
728                                 else
729                                         throw new Exception ("EmitThis for an unknown state");
730                         }
731                 }
732
733                 public Expression GetThis (Location loc)
734                 {
735                         This my_this;
736                         if (CurrentBlock != null)
737                                 my_this = new This (CurrentBlock, loc);
738                         else
739                                 my_this = new This (loc);
740
741                         if (!my_this.ResolveBase (this))
742                                 my_this = null;
743
744                         return my_this;
745                 }
746         }
747
748
749         public abstract class CommonAssemblyModulClass: IAttributeSupport {
750                 protected Hashtable m_attributes;
751
752                 protected CommonAssemblyModulClass () 
753                 {
754                         m_attributes = new Hashtable ();
755                 }
756
757                 //
758                 // Adds a global attribute that was declared in `container', 
759                 // the attribute is in `attr', and it was defined at `loc'
760                 //                
761                 public void AddAttribute (TypeContainer container, AttributeSection attr)
762                 {
763                         NamespaceEntry ns = container.NamespaceEntry;
764                         Attributes a = (Attributes) m_attributes [ns];
765
766                         if (a == null) {
767                                 m_attributes [ns] = new Attributes (attr);
768                                 return;
769                         }
770
771                         a.AddAttributeSection (attr);
772                 }
773
774                 public virtual void Emit () 
775                 {
776                         if (m_attributes.Count < 1)
777                                 return;
778
779                         TypeContainer dummy = new TypeContainer ();
780                         EmitContext temp_ec = new EmitContext (dummy, Mono.CSharp.Location.Null, null, null, 0, false);
781                         
782                         foreach (DictionaryEntry de in m_attributes)
783                         {
784                                 NamespaceEntry ns = (NamespaceEntry) de.Key;
785                                 Attributes attrs = (Attributes) de.Value;
786                                 
787                                 dummy.NamespaceEntry = ns;
788                                 Attribute.ApplyAttributes (temp_ec, null, this, attrs);
789                         }
790                 }
791                 
792                 #region IAttributeSupport Members
793                 public abstract void SetCustomAttribute(CustomAttributeBuilder customBuilder);
794                 #endregion
795
796         }
797         
798
799         public class AssemblyClass: CommonAssemblyModulClass {
800                 // TODO: make it private and move all builder based methods here
801                 public AssemblyBuilder Builder;
802                     
803                 bool m_is_cls_compliant;
804
805                 public AssemblyClass (): base ()
806                 {
807                         m_is_cls_compliant = false;
808                 }
809
810                 public bool IsClsCompliant {
811                         get {
812                                 return m_is_cls_compliant;
813                         }
814                 }
815
816                 public AssemblyName GetAssemblyName (string name, string output) 
817                 {
818                         // scan assembly attributes for strongname related attr
819                         foreach (DictionaryEntry nsattr in m_attributes) {
820                                 ArrayList list = ((Attributes)nsattr.Value).AttributeSections;
821                                 for (int i=0; i < list.Count; i++) {
822                                         AttributeSection asect = (AttributeSection) list [i];
823                                         if (asect.Target != "assembly")
824                                                 continue;
825                                         // strongname attributes don't support AllowMultiple
826                                         Attribute a = (Attribute) asect.Attributes [0];
827                                         switch (a.Name) {
828                                                 case "AssemblyKeyFile":
829                                                         if (RootContext.StrongNameKeyFile != null) {
830                                                                 Report.Warning (1616, "Compiler option -keyfile overrides " +
831                                                                         "AssemblyKeyFileAttribute");
832                                                         }
833                                                         else {
834                                                                 string value = a.GetString ();
835                                                                 if (value != String.Empty)
836                                                                         RootContext.StrongNameKeyFile = value;
837                                                         }
838                                                         break;
839                                                 case "AssemblyKeyName":
840                                                         if (RootContext.StrongNameKeyContainer != null) {
841                                                                 Report.Warning (1616, "Compiler option -keycontainer overrides " +
842                                                                         "AssemblyKeyNameAttribute");
843                                                         }
844                                                         else {
845                                                                 string value = a.GetString ();
846                                                                 if (value != String.Empty)
847                                                                         RootContext.StrongNameKeyContainer = value;
848                                                         }
849                                                         break;
850                                                 case "AssemblyDelaySign":
851                                                         RootContext.StrongNameDelaySign = a.GetBoolean ();
852                                                         break;
853                                         }
854                                 }
855                         }
856
857                         AssemblyName an = new AssemblyName ();
858                         an.Name = Path.GetFileNameWithoutExtension (name);
859
860                         // note: delay doesn't apply when using a key container
861                         if (RootContext.StrongNameKeyContainer != null) {
862                                 an.KeyPair = new StrongNameKeyPair (RootContext.StrongNameKeyContainer);
863                                 return an;
864                         }
865
866                         // strongname is optional
867                         if (RootContext.StrongNameKeyFile == null)
868                                 return an;
869
870                         string AssemblyDir = Path.GetDirectoryName (output);
871
872                         // the StrongName key file may be relative to (a) the compiled
873                         // file or (b) to the output assembly. See bugzilla #55320
874                         // http://bugzilla.ximian.com/show_bug.cgi?id=55320
875
876                         // (a) relative to the compiled file
877                         string filename = Path.GetFullPath (RootContext.StrongNameKeyFile);
878                         bool exist = File.Exists (filename);
879                         if ((!exist) && (AssemblyDir != null) && (AssemblyDir != String.Empty)) {
880                                 // (b) relative to the outputed assembly
881                                 filename = Path.GetFullPath (Path.Combine (AssemblyDir, RootContext.StrongNameKeyFile));
882                                 exist = File.Exists (filename);
883                         }
884
885                         if (exist) {
886                                 using (FileStream fs = new FileStream (filename, FileMode.Open)) {
887                                         byte[] snkeypair = new byte [fs.Length];
888                                         fs.Read (snkeypair, 0, snkeypair.Length);
889
890                                         try {
891                                                 // this will import public or private/public keys
892                                                 RSA rsa = CryptoConvert.FromCapiKeyBlob (snkeypair);
893                                                 // only the public part must be supplied if signature is delayed
894                                                 byte[] key = CryptoConvert.ToCapiKeyBlob (rsa, !RootContext.StrongNameDelaySign);
895                                                 an.KeyPair = new StrongNameKeyPair (key);
896                                         }
897                                         catch (CryptographicException) {
898                                                 Report.Error (1548, "Could not strongname the assembly. File `" +
899                                                         RootContext.StrongNameKeyFile + "' incorrectly encoded.");
900                                                 Environment.Exit (1);
901                                         }
902                                 }
903                         }
904                         else {
905                                 Report.Error (1548, "Could not strongname the assembly. File `" +
906                                         RootContext.StrongNameKeyFile + "' not found.");
907                                 Environment.Exit (1);
908                         }
909                         return an;
910                 }
911
912                 public override void SetCustomAttribute(CustomAttributeBuilder customBuilder)
913                 {
914                         Builder.SetCustomAttribute (customBuilder);
915                 }
916         }
917
918         public class ModuleClass: CommonAssemblyModulClass {
919                 // TODO: make it private and move all builder based methods here
920                 public ModuleBuilder Builder;
921             
922                 bool m_module_is_unsafe;
923
924                 public ModuleClass (bool is_unsafe)
925                 {
926                         m_module_is_unsafe = is_unsafe;
927                 }
928
929                 public override void Emit () 
930                 {
931                         base.Emit ();
932
933                         if (!m_module_is_unsafe)
934                                 return;
935
936                         if (TypeManager.unverifiable_code_ctor == null) {
937                                 Console.WriteLine ("Internal error ! Cannot set unverifiable code attribute.");
938                                 return;
939                         }
940                                 
941                         SetCustomAttribute (new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0]));
942                 }
943                 
944                 public override void SetCustomAttribute(CustomAttributeBuilder customBuilder)
945                 {
946                         Builder.SetCustomAttribute (customBuilder);
947                 }
948         }
949
950 }