2004-06-08 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / gmcs / 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 #if FIXME
518                             } catch (Exception e) {
519                                         Console.WriteLine ("Exception caught by the compiler while compiling:");
520                                         Console.WriteLine ("   Block that caused the problem begin at: " + loc);
521                                         
522                                         if (CurrentBlock != null){
523                                                 Console.WriteLine ("                     Block being compiled: [{0},{1}]",
524                                                                    CurrentBlock.StartLocation, CurrentBlock.EndLocation);
525                                         }
526                                         Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
527                                         Console.WriteLine (Report.FriendlyStackTrace (e));
528                                         
529                                         Environment.Exit (1);
530 #else
531                             } finally {
532 #endif
533                             }
534                         }
535
536                         if (ReturnType != null && !unreachable){
537                                 if (!InIterator){
538                                         Report.Error (161, loc, "Not all code paths return a value");
539                                         return;
540                                 }
541                         }
542
543                         if (HasReturnLabel)
544                                 ig.MarkLabel (ReturnLabel);
545                         if (return_value != null){
546                                 ig.Emit (OpCodes.Ldloc, return_value);
547                                 ig.Emit (OpCodes.Ret);
548                         } else {
549                                 //
550                                 // If `HasReturnLabel' is set, then we already emitted a
551                                 // jump to the end of the method, so we must emit a `ret'
552                                 // there.
553                                 //
554                                 // Unfortunately, System.Reflection.Emit automatically emits
555                                 // a leave to the end of a finally block.  This is a problem
556                                 // if no code is following the try/finally block since we may
557                                 // jump to a point after the end of the method.
558                                 // As a workaround, we're always creating a return label in
559                                 // this case.
560                                 //
561
562                                 if ((block != null) && block.IsDestructor) {
563                                         // Nothing to do; S.R.E automatically emits a leave.
564                                 } else if (HasReturnLabel || (!unreachable && !InIterator)) {
565                                         if (ReturnType != null)
566                                                 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
567                                         ig.Emit (OpCodes.Ret);
568                                 }
569                         }
570                 }
571
572                 /// <summary>
573                 ///   This is called immediately before emitting an IL opcode to tell the symbol
574                 ///   writer to which source line this opcode belongs.
575                 /// </summary>
576                 public void Mark (Location loc, bool check_file)
577                 {
578                         if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
579                                 return;
580
581                         if (check_file && (CurrentFile != loc.File))
582                                 return;
583
584                         ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
585                 }
586
587                 /// <summary>
588                 ///   Returns a temporary storage for a variable of type t as 
589                 ///   a local variable in the current body.
590                 /// </summary>
591                 public LocalBuilder GetTemporaryLocal (Type t)
592                 {
593                         LocalBuilder location = null;
594                         
595                         if (temporary_storage != null){
596                                 object o = temporary_storage [t];
597                                 if (o != null){
598                                         if (o is ArrayList){
599                                                 ArrayList al = (ArrayList) o;
600                                                 
601                                                 for (int i = 0; i < al.Count; i++){
602                                                         if (al [i] != null){
603                                                                 location = (LocalBuilder) al [i];
604                                                                 al [i] = null;
605                                                                 break;
606                                                         }
607                                                 }
608                                         } else
609                                                 location = (LocalBuilder) o;
610                                         if (location != null)
611                                                 return location;
612                                 }
613                         }
614                         
615                         return ig.DeclareLocal (t);
616                 }
617
618                 public void FreeTemporaryLocal (LocalBuilder b, Type t)
619                 {
620                         if (temporary_storage == null){
621                                 temporary_storage = new Hashtable ();
622                                 temporary_storage [t] = b;
623                                 return;
624                         }
625                         object o = temporary_storage [t];
626                         if (o == null){
627                                 temporary_storage [t] = b;
628                                 return;
629                         }
630                         if (o is ArrayList){
631                                 ArrayList al = (ArrayList) o;
632                                 for (int i = 0; i < al.Count; i++){
633                                         if (al [i] == null){
634                                                 al [i] = b;
635                                                 return;
636                                         }
637                                 }
638                                 al.Add (b);
639                                 return;
640                         }
641                         ArrayList replacement = new ArrayList ();
642                         replacement.Add (o);
643                         temporary_storage.Remove (t);
644                         temporary_storage [t] = replacement;
645                 }
646
647                 /// <summary>
648                 ///   Current loop begin and end labels.
649                 /// </summary>
650                 public Label LoopBegin, LoopEnd;
651
652                 /// <summary>
653                 ///   Default target in a switch statement.   Only valid if
654                 ///   InSwitch is true
655                 /// </summary>
656                 public Label DefaultTarget;
657
658                 /// <summary>
659                 ///   If this is non-null, points to the current switch statement
660                 /// </summary>
661                 public Switch Switch;
662
663                 /// <summary>
664                 ///   ReturnValue creates on demand the LocalBuilder for the
665                 ///   return value from the function.  By default this is not
666                 ///   used.  This is only required when returns are found inside
667                 ///   Try or Catch statements.
668                 /// </summary>
669                 public LocalBuilder TemporaryReturn ()
670                 {
671                         if (return_value == null){
672                                 return_value = ig.DeclareLocal (ReturnType);
673                                 ReturnLabel = ig.DefineLabel ();
674                                 HasReturnLabel = true;
675                         }
676
677                         return return_value;
678                 }
679
680                 public void NeedReturnLabel ()
681                 {
682                         if (!HasReturnLabel) {
683                                 ReturnLabel = ig.DefineLabel ();
684                                 HasReturnLabel = true;
685                         }
686                 }
687
688                 //
689                 // Creates a field `name' with the type `t' on the proxy class
690                 //
691                 public FieldBuilder MapVariable (string name, Type t)
692                 {
693                         if (InIterator){
694                                 return IteratorHandler.Current.MapVariable ("v_", name, t);
695                         }
696
697                         throw new Exception ("MapVariable for an unknown state");
698                 }
699
700                 //
701                 // Invoke this routine to remap a VariableInfo into the
702                 // proper MemberAccess expression
703                 //
704                 public Expression RemapLocal (LocalInfo local_info)
705                 {
706                         FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
707                         fe.InstanceExpression = new ProxyInstance ();
708                         return fe.DoResolve (this);
709                 }
710
711                 public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side)
712                 {
713                         FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
714                         fe.InstanceExpression = new ProxyInstance ();
715                         return fe.DoResolveLValue (this, right_side);
716                 }
717
718                 public Expression RemapParameter (int idx)
719                 {
720                         FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
721                         fe.InstanceExpression = new ProxyInstance ();
722                         return fe.DoResolve (this);
723                 }
724
725                 public Expression RemapParameterLValue (int idx, Expression right_side)
726                 {
727                         FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
728                         fe.InstanceExpression = new ProxyInstance ();
729                         return fe.DoResolveLValue (this, right_side);
730                 }
731                 
732                 //
733                 // Emits the proper object to address fields on a remapped
734                 // variable/parameter to field in anonymous-method/iterator proxy classes.
735                 //
736                 public void EmitThis ()
737                 {
738                         if (InIterator){
739                                 ig.Emit (OpCodes.Ldarg_0);
740                                 if (!IsStatic){
741                                         FieldBuilder this_field = IteratorHandler.Current.this_field;
742                                         if (TypeManager.IsValueType (this_field.FieldType))
743                                                 ig.Emit (OpCodes.Ldflda, this_field);
744                                         else
745                                                 ig.Emit (OpCodes.Ldfld, this_field);
746                                 } 
747                         } else
748                                 ig.Emit (OpCodes.Ldarg_0);
749                 }
750
751                 public Expression GetThis (Location loc)
752                 {
753                         This my_this;
754                         if (CurrentBlock != null)
755                                 my_this = new This (CurrentBlock, loc);
756                         else
757                                 my_this = new This (loc);
758
759                         if (!my_this.ResolveBase (this))
760                                 my_this = null;
761
762                         return my_this;
763                 }
764         }
765
766
767         public abstract class CommonAssemblyModulClass: Attributable {
768                 static string[] attribute_targets = new string [] { "assembly", "module" };
769
770                 protected CommonAssemblyModulClass ():
771                         base (null)
772                 {
773                 }
774
775                 public void AddAttributes (ArrayList attrs)
776                 {
777                         if (OptAttributes == null) {
778                                 OptAttributes = new Attributes (attrs);
779                                 return;
780                         }
781                         OptAttributes.AddAttributes (attrs);
782                         OptAttributes.CheckTargets (ValidAttributeTargets);
783                 }
784
785                 public virtual void Emit (TypeContainer tc) 
786                 {
787                         if (OptAttributes == null)
788                                 return;
789
790                         EmitContext ec = new EmitContext (tc, Mono.CSharp.Location.Null, null, null, 0, false);
791                         OptAttributes.Emit (ec, this);
792                 }
793
794                 protected Attribute GetClsCompliantAttribute ()
795                 {
796                         if (OptAttributes == null)
797                                 return null;
798
799                         EmitContext temp_ec = new EmitContext (new TypeContainer (), Mono.CSharp.Location.Null, null, null, 0, false);
800                         Attribute a = OptAttributes.Search (TypeManager.cls_compliant_attribute_type, temp_ec);
801                         if (a != null) {
802                                 a.Resolve (temp_ec);
803                         }
804                         return a;
805                 }
806
807                 protected override string[] ValidAttributeTargets {
808                         get {
809                                 return attribute_targets;
810                         }
811                 }
812         }
813
814         public class AssemblyClass: CommonAssemblyModulClass {
815                 // TODO: make it private and move all builder based methods here
816                 public AssemblyBuilder Builder;
817                     
818                 bool is_cls_compliant;
819
820                 public AssemblyClass (): base ()
821                 {
822                         is_cls_compliant = false;
823                 }
824
825                 public bool IsClsCompliant {
826                         get {
827                                 return is_cls_compliant;
828                         }
829                         }
830
831                 public override AttributeTargets AttributeTargets {
832                         get {
833                                 return AttributeTargets.Assembly;
834                         }
835                 }
836
837                 public override bool IsClsCompliaceRequired(DeclSpace ds)
838                 {
839                         return is_cls_compliant;
840                 }
841
842                 public void ResolveClsCompliance ()
843                 {
844                         Attribute a = GetClsCompliantAttribute ();
845                         if (a == null)
846                                 return;
847
848                         is_cls_compliant = a.GetClsCompliantAttributeValue (null);
849                 }
850
851                 public AssemblyName GetAssemblyName (string name, string output) 
852                 {
853                         if (OptAttributes != null) {
854                                foreach (Attribute a in OptAttributes.Attrs) {
855                                         if (a.Target != "assembly")
856                                                 continue;
857                                        // TODO: This code is buggy: comparing Attribute name without resolving it is wrong.
858                                        //       However, this is invoked by CodeGen.Init, at which time none of the namespaces
859                                        //       are loaded yet.
860                                         switch (a.Name) {
861                                                 case "AssemblyKeyFile":
862                                                case "AssemblyKeyFileAttribute":
863                                                case "System.Reflection.AssemblyKeyFileAttribute":
864                                                         if (RootContext.StrongNameKeyFile != null) {
865                                                                 Report.Warning (1616, "Compiler option -keyfile overrides " +
866                                                                         "AssemblyKeyFileAttribute");
867                                                         }
868                                                         else {
869                                                                 string value = a.GetString ();
870                                                                 if (value != String.Empty)
871                                                                         RootContext.StrongNameKeyFile = value;
872                                                         }
873                                                         break;
874                                                 case "AssemblyKeyName":
875                                                case "AssemblyKeyNameAttribute":
876                                                case "System.Reflection.AssemblyKeyNameAttribute":
877                                                         if (RootContext.StrongNameKeyContainer != null) {
878                                                                 Report.Warning (1616, "Compiler option -keycontainer overrides " +
879                                                                         "AssemblyKeyNameAttribute");
880                                                         }
881                                                         else {
882                                                                 string value = a.GetString ();
883                                                                 if (value != String.Empty)
884                                                                         RootContext.StrongNameKeyContainer = value;
885                                                         }
886                                                         break;
887                                                 case "AssemblyDelaySign":
888                                                case "AssemblyDelaySignAttribute":
889                                                case "System.Reflection.AssemblyDelaySignAttribute":
890                                                         RootContext.StrongNameDelaySign = a.GetBoolean ();
891                                                         break;
892                                         }
893                                 }
894                         }
895
896                         AssemblyName an = new AssemblyName ();
897                         an.Name = Path.GetFileNameWithoutExtension (name);
898
899                         // note: delay doesn't apply when using a key container
900                         if (RootContext.StrongNameKeyContainer != null) {
901                                 an.KeyPair = new StrongNameKeyPair (RootContext.StrongNameKeyContainer);
902                                 return an;
903                         }
904
905                         // strongname is optional
906                         if (RootContext.StrongNameKeyFile == null)
907                                 return an;
908
909                         string AssemblyDir = Path.GetDirectoryName (output);
910
911                         // the StrongName key file may be relative to (a) the compiled
912                         // file or (b) to the output assembly. See bugzilla #55320
913                         // http://bugzilla.ximian.com/show_bug.cgi?id=55320
914
915                         // (a) relative to the compiled file
916                         string filename = Path.GetFullPath (RootContext.StrongNameKeyFile);
917                         bool exist = File.Exists (filename);
918                         if ((!exist) && (AssemblyDir != null) && (AssemblyDir != String.Empty)) {
919                                 // (b) relative to the outputed assembly
920                                 filename = Path.GetFullPath (Path.Combine (AssemblyDir, RootContext.StrongNameKeyFile));
921                                 exist = File.Exists (filename);
922                         }
923
924                         if (exist) {
925                                 using (FileStream fs = new FileStream (filename, FileMode.Open, FileAccess.Read)) {
926                                         byte[] snkeypair = new byte [fs.Length];
927                                         fs.Read (snkeypair, 0, snkeypair.Length);
928
929                                         if (RootContext.StrongNameDelaySign) {
930                                                 // delayed signing - DO NOT include private key
931                                                 try {
932                                                         // check for possible ECMA key
933                                                         if (snkeypair.Length == 16) {
934                                                                 // will be rejected if not "the" ECMA key
935                                                                 an.KeyPair = new StrongNameKeyPair (snkeypair);
936                                                         }
937                                                         else {
938                                                                 // take it, with or without, a private key
939                                                                 RSA rsa = CryptoConvert.FromCapiKeyBlob (snkeypair);
940                                                                 // and make sure we only feed the public part to Sys.Ref
941                                                                 byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa);
942                                                                 an.KeyPair = new StrongNameKeyPair (publickey);
943                                                         }
944                                                 }
945                                                 catch (Exception) {
946                                                         Report.Error (1548, "Could not strongname the assembly. File `" +
947                                                                 RootContext.StrongNameKeyFile + "' incorrectly encoded.");
948                                                         Environment.Exit (1);
949                                                 }
950                                         }
951                                         else {
952                                                 // no delay so we make sure we have the private key
953                                                 try {
954                                                         CryptoConvert.FromCapiPrivateKeyBlob (snkeypair);
955                                                         an.KeyPair = new StrongNameKeyPair (snkeypair);
956                                                 }
957                                                 catch (CryptographicException) {
958                                                         if (snkeypair.Length == 16) {
959                                                                 // error # is different for ECMA key
960                                                                 Report.Error (1606, "Could not strongname the assembly. " + 
961                                                                         "ECMA key can only be used to delay-sign assemblies");
962                                                         }
963                                                         else {
964                                                                 Report.Error (1548, "Could not strongname the assembly. File `" +
965                                                                         RootContext.StrongNameKeyFile +
966                                                                         "' doesn't have a private key.");
967                                                         }
968                                                         Environment.Exit (1);
969                                                 }
970                                         }
971                                 }
972                         }
973                         else {
974                                 Report.Error (1548, "Could not strongname the assembly. File `" +
975                                         RootContext.StrongNameKeyFile + "' not found.");
976                                 Environment.Exit (1);
977                         }
978                         return an;
979                 }
980
981                 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
982                 {
983                         Builder.SetCustomAttribute (customBuilder);
984                 }
985         }
986
987         public class ModuleClass: CommonAssemblyModulClass {
988                 // TODO: make it private and move all builder based methods here
989                 public ModuleBuilder Builder;
990                 bool m_module_is_unsafe;
991
992                 public ModuleClass (bool is_unsafe)
993                 {
994                         m_module_is_unsafe = is_unsafe;
995                 }
996
997                 public override AttributeTargets AttributeTargets {
998                         get {
999                                 return AttributeTargets.Module;
1000                         }
1001                 }
1002
1003                 public override bool IsClsCompliaceRequired(DeclSpace ds)
1004                 {
1005                         return CodeGen.Assembly.IsClsCompliant;
1006                         }
1007
1008                 public override void Emit (TypeContainer tc) 
1009                 {
1010                         base.Emit (tc);
1011
1012                         if (!m_module_is_unsafe)
1013                                 return;
1014
1015                         if (TypeManager.unverifiable_code_ctor == null) {
1016                                 Console.WriteLine ("Internal error ! Cannot set unverifiable code attribute.");
1017                                 return;
1018                         }
1019                                 
1020                         ApplyAttributeBuilder (null, new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0]));
1021                 }
1022                 
1023                 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
1024                 {
1025                         if (a != null && a.Type == TypeManager.cls_compliant_attribute_type) {
1026                                 Report.Warning_T (3012, a.Location);
1027                                 return;
1028                         }
1029
1030                         Builder.SetCustomAttribute (customBuilder);
1031                 }
1032         }
1033
1034 }