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