2 // codegen.cs: The code generator
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2004 Novell, Inc.
13 using System.Collections.Generic;
14 using System.Reflection;
15 using System.Reflection.Emit;
16 using System.Runtime.InteropServices;
17 using System.Security.Cryptography;
19 namespace Mono.CSharp {
22 /// Code generator class.
24 public class CodeGen {
25 static AppDomain current_domain;
27 // Breaks dynamic and repl
28 public static AssemblyClass Assembly;
35 public static void Reset ()
37 Assembly = new AssemblyClass ();
40 public static string Basename (string name)
42 int pos = name.LastIndexOf ('/');
45 return name.Substring (pos + 1);
47 pos = name.LastIndexOf ('\\');
49 return name.Substring (pos + 1);
54 public static string Dirname (string name)
56 int pos = name.LastIndexOf ('/');
59 return name.Substring (0, pos);
61 pos = name.LastIndexOf ('\\');
63 return name.Substring (0, pos);
68 static public string FileName;
71 // Initializes the code generator variables for interactive use (repl)
73 static public void InitDynamic (CompilerContext ctx, string name)
75 current_domain = AppDomain.CurrentDomain;
76 AssemblyName an = Assembly.GetAssemblyName (name, name);
78 Assembly.Builder = current_domain.DefineDynamicAssembly (an, AssemblyBuilderAccess.Run);
79 RootContext.ToplevelTypes = new ModuleCompiled (ctx, true);
80 RootContext.ToplevelTypes.Builder = Assembly.Builder.DefineDynamicModule (Basename (name), false);
81 Assembly.Name = Assembly.Builder.GetName ();
85 // Initializes the code generator variables
87 static public bool Init (string name, string output, bool want_debugging_support, CompilerContext ctx)
90 AssemblyName an = Assembly.GetAssemblyName (name, output);
94 if (an.KeyPair != null) {
95 // If we are going to strong name our assembly make
96 // sure all its refs are strong named
97 foreach (Assembly a in ctx.GlobalRootNamespace.Assemblies) {
98 AssemblyName ref_name = a.GetName ();
99 byte [] b = ref_name.GetPublicKeyToken ();
100 if (b == null || b.Length == 0) {
101 ctx.Report.Error (1577, "Assembly generation failed " +
102 "-- Referenced assembly '" +
104 "' does not have a strong name.");
105 //Environment.Exit (1);
110 current_domain = AppDomain.CurrentDomain;
113 Assembly.Builder = current_domain.DefineDynamicAssembly (an,
114 AssemblyBuilderAccess.RunAndSave, Dirname (name));
116 catch (ArgumentException) {
117 // specified key may not be exportable outside it's container
118 if (RootContext.StrongNameKeyContainer != null) {
119 ctx.Report.Error (1548, "Could not access the key inside the container `" +
120 RootContext.StrongNameKeyContainer + "'.");
121 Environment.Exit (1);
125 catch (CryptographicException) {
126 if ((RootContext.StrongNameKeyContainer != null) || (RootContext.StrongNameKeyFile != null)) {
127 ctx.Report.Error (1548, "Could not use the specified key to strongname the assembly.");
128 Environment.Exit (1);
133 // Get the complete AssemblyName from the builder
134 // (We need to get the public key and token)
135 Assembly.Name = Assembly.Builder.GetName ();
138 // Pass a path-less name to DefineDynamicModule. Wonder how
139 // this copes with output in different directories then.
140 // FIXME: figure out how this copes with --output /tmp/blah
142 // If the third argument is true, the ModuleBuilder will dynamically
143 // load the default symbol writer.
146 RootContext.ToplevelTypes.Builder = Assembly.Builder.DefineDynamicModule (
147 Basename (name), Basename (output), want_debugging_support);
150 // TODO: We should use SymbolWriter from DefineDynamicModule
151 if (want_debugging_support && !SymbolWriter.Initialize (RootContext.ToplevelTypes.Builder, output)) {
152 ctx.Report.Error (40, "Unexpected debug information initialization error `{0}'",
153 "Could not find the symbol writer assembly (Mono.CompilerServices.SymbolWriter.dll)");
157 } catch (ExecutionEngineException e) {
158 ctx.Report.Error (40, "Unexpected debug information initialization error `{0}'",
166 public static void Save (string name, Report Report)
168 PortableExecutableKinds pekind;
169 ImageFileMachine machine;
171 switch (RootContext.Platform) {
173 pekind = PortableExecutableKinds.Required32Bit | PortableExecutableKinds.ILOnly;
174 machine = ImageFileMachine.I386;
177 pekind = PortableExecutableKinds.ILOnly;
178 machine = ImageFileMachine.AMD64;
181 pekind = PortableExecutableKinds.ILOnly;
182 machine = ImageFileMachine.IA64;
184 case Platform.AnyCPU:
186 pekind = PortableExecutableKinds.ILOnly;
187 machine = ImageFileMachine.I386;
191 Assembly.Builder.Save (Basename (name), pekind, machine);
193 catch (COMException) {
194 if ((RootContext.StrongNameKeyFile == null) || (!RootContext.StrongNameDelaySign))
197 // FIXME: it seems Microsoft AssemblyBuilder doesn't like to delay sign assemblies
198 Report.Error (1548, "Couldn't delay-sign the assembly with the '" +
199 RootContext.StrongNameKeyFile +
200 "', Use MCS with the Mono runtime or CSC to compile this assembly.");
202 catch (System.IO.IOException io) {
203 Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
206 catch (System.UnauthorizedAccessException ua) {
207 Report.Error (16, "Could not write to file `"+name+"', cause: " + ua.Message);
210 catch (System.NotImplementedException nie) {
211 Report.RuntimeMissingSupport (Location.Null, nie.Message);
218 /// An Emit Context is created for each body of code (from methods,
219 /// properties bodies, indexer bodies or constructor bodies)
221 public class EmitContext : BuilderContext
223 // TODO: Has to be private
224 public ILGenerator ig;
227 /// The value that is allowed to be returned or NULL if there is no
230 TypeSpec return_type;
233 /// Keeps track of the Type to LocalBuilder temporary storage created
234 /// to store structures (used to compute the address of the structure
235 /// value on structure method invocations)
237 Dictionary<TypeSpec, object> temporary_storage;
240 /// The location where we store the return value.
242 public LocalBuilder return_value;
245 /// The location where return has to jump to return the
248 public Label ReturnLabel;
251 /// If we already defined the ReturnLabel
253 public bool HasReturnLabel;
256 /// Current loop begin and end labels.
258 public Label LoopBegin, LoopEnd;
261 /// Default target in a switch statement. Only valid if
264 public Label DefaultTarget;
267 /// If this is non-null, points to the current switch statement
269 public Switch Switch;
272 /// Whether we are inside an anonymous method.
274 public AnonymousExpression CurrentAnonymousMethod;
276 public readonly IMemberContext MemberContext;
278 DynamicSiteClass dynamic_site_container;
280 public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type)
282 this.MemberContext = rc;
285 this.return_type = return_type;
290 public TypeSpec CurrentType {
291 get { return MemberContext.CurrentType; }
294 public TypeParameter[] CurrentTypeParameters {
295 get { return MemberContext.CurrentTypeParameters; }
298 public MemberCore CurrentTypeDefinition {
299 get { return MemberContext.CurrentMemberDefinition; }
302 public bool IsStatic {
303 get { return MemberContext.IsStatic; }
306 bool IsAnonymousStoreyMutateRequired {
308 return CurrentAnonymousMethod != null &&
309 CurrentAnonymousMethod.Storey != null &&
310 CurrentAnonymousMethod.Storey.Mutator != null;
314 // Has to be used for emitter errors only
315 public Report Report {
316 get { return MemberContext.Compiler.Report; }
319 public TypeSpec ReturnType {
327 /// This is called immediately before emitting an IL opcode to tell the symbol
328 /// writer to which source line this opcode belongs.
330 public void Mark (Location loc)
332 if (!SymbolWriter.HasSymbolWriter || HasSet (Options.OmitDebugInfo) || loc.IsNull)
335 SymbolWriter.MarkSequencePoint (ig, loc);
338 public void DefineLocalVariable (string name, LocalBuilder builder)
340 SymbolWriter.DefineLocalVariable (name, builder);
343 public void BeginCatchBlock (TypeSpec type)
345 ig.BeginCatchBlock (type.GetMetaInfo ());
348 public void BeginExceptionBlock ()
350 ig.BeginExceptionBlock ();
353 public void BeginFinallyBlock ()
355 ig.BeginFinallyBlock ();
358 public void BeginScope ()
361 SymbolWriter.OpenScope(ig);
364 public void EndExceptionBlock ()
366 ig.EndExceptionBlock ();
369 public void EndScope ()
372 SymbolWriter.CloseScope(ig);
376 // Creates a nested container in this context for all dynamic compiler generated stuff
378 public DynamicSiteClass CreateDynamicSite ()
380 if (dynamic_site_container == null) {
381 var mc = MemberContext.CurrentMemberDefinition as MemberBase;
382 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, CurrentTypeParameters);
384 RootContext.ToplevelTypes.AddCompilerGeneratedClass (dynamic_site_container);
385 dynamic_site_container.CreateType ();
386 dynamic_site_container.DefineType ();
387 dynamic_site_container.ResolveTypeParameters ();
388 dynamic_site_container.Define ();
391 return dynamic_site_container;
394 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
396 if (IsAnonymousStoreyMutateRequired)
397 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
399 return ig.DeclareLocal (type.GetMetaInfo (), pinned);
402 public Label DefineLabel ()
404 return ig.DefineLabel ();
407 public void MarkLabel (Label label)
409 ig.MarkLabel (label);
412 public void Emit (OpCode opcode)
417 public void Emit (OpCode opcode, LocalBuilder local)
419 ig.Emit (opcode, local);
422 public void Emit (OpCode opcode, string arg)
424 ig.Emit (opcode, arg);
427 public void Emit (OpCode opcode, double arg)
429 ig.Emit (opcode, arg);
432 public void Emit (OpCode opcode, float arg)
434 ig.Emit (opcode, arg);
437 public void Emit (OpCode opcode, int arg)
439 ig.Emit (opcode, arg);
442 public void Emit (OpCode opcode, byte arg)
444 ig.Emit (opcode, arg);
447 public void Emit (OpCode opcode, Label label)
449 ig.Emit (opcode, label);
452 public void Emit (OpCode opcode, Label[] labels)
454 ig.Emit (opcode, labels);
457 public void Emit (OpCode opcode, TypeSpec type)
459 if (IsAnonymousStoreyMutateRequired)
460 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
462 ig.Emit (opcode, type.GetMetaInfo ());
465 public void Emit (OpCode opcode, FieldSpec field)
467 if (IsAnonymousStoreyMutateRequired)
468 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
470 ig.Emit (opcode, field.GetMetaInfo ());
473 public void Emit (OpCode opcode, MethodSpec method)
475 if (IsAnonymousStoreyMutateRequired)
476 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
478 if (method.IsConstructor)
479 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
481 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
484 // TODO: REMOVE breaks mutator
485 public void Emit (OpCode opcode, MethodInfo method)
487 ig.Emit (opcode, method);
490 // TODO: REMOVE breaks mutator
491 public void Emit (OpCode opcode, FieldBuilder field)
493 ig.Emit (opcode, field);
496 public void Emit (OpCode opcode, MethodSpec method, Type[] vargs)
498 // TODO MemberCache: This should mutate too
499 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
502 public void EmitArrayNew (ArrayContainer ac)
505 Emit (OpCodes.Newarr, ac.Element);
507 if (IsAnonymousStoreyMutateRequired)
508 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
510 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
514 public void EmitArrayAddress (ArrayContainer ac)
516 if (ac.Element.IsGenericParameter)
517 ig.Emit (OpCodes.Readonly);
520 if (IsAnonymousStoreyMutateRequired)
521 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
523 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
525 Emit (OpCodes.Ldelema, ac.Element);
530 // Emits the right opcode to load from an array
532 public void EmitArrayLoad (ArrayContainer ac)
535 if (IsAnonymousStoreyMutateRequired)
536 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
538 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
542 var type = ac.Element;
543 if (TypeManager.IsEnumType (type))
544 type = EnumSpec.GetUnderlyingType (type);
546 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
547 Emit (OpCodes.Ldelem_U1);
548 else if (type == TypeManager.sbyte_type)
549 Emit (OpCodes.Ldelem_I1);
550 else if (type == TypeManager.short_type)
551 Emit (OpCodes.Ldelem_I2);
552 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
553 Emit (OpCodes.Ldelem_U2);
554 else if (type == TypeManager.int32_type)
555 Emit (OpCodes.Ldelem_I4);
556 else if (type == TypeManager.uint32_type)
557 Emit (OpCodes.Ldelem_U4);
558 else if (type == TypeManager.uint64_type)
559 Emit (OpCodes.Ldelem_I8);
560 else if (type == TypeManager.int64_type)
561 Emit (OpCodes.Ldelem_I8);
562 else if (type == TypeManager.float_type)
563 Emit (OpCodes.Ldelem_R4);
564 else if (type == TypeManager.double_type)
565 Emit (OpCodes.Ldelem_R8);
566 else if (type == TypeManager.intptr_type)
567 Emit (OpCodes.Ldelem_I);
568 else if (TypeManager.IsStruct (type)) {
569 Emit (OpCodes.Ldelema, type);
570 Emit (OpCodes.Ldobj, type);
571 } else if (type.IsGenericParameter) {
572 Emit (OpCodes.Ldelem, type);
573 } else if (type.IsPointer)
574 Emit (OpCodes.Ldelem_I);
576 Emit (OpCodes.Ldelem_Ref);
580 // Emits the right opcode to store to an array
582 public void EmitArrayStore (ArrayContainer ac)
585 if (IsAnonymousStoreyMutateRequired)
586 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
588 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
592 var type = ac.Element;
595 type = EnumSpec.GetUnderlyingType (type);
597 if (type == TypeManager.byte_type || type == TypeManager.sbyte_type || type == TypeManager.bool_type)
598 Emit (OpCodes.Stelem_I1);
599 else if (type == TypeManager.short_type || type == TypeManager.ushort_type || type == TypeManager.char_type)
600 Emit (OpCodes.Stelem_I2);
601 else if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
602 Emit (OpCodes.Stelem_I4);
603 else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
604 Emit (OpCodes.Stelem_I8);
605 else if (type == TypeManager.float_type)
606 Emit (OpCodes.Stelem_R4);
607 else if (type == TypeManager.double_type)
608 Emit (OpCodes.Stelem_R8);
609 else if (type == TypeManager.intptr_type)
610 Emit (OpCodes.Stobj, type);
611 else if (TypeManager.IsStruct (type))
612 Emit (OpCodes.Stobj, type);
613 else if (type.IsGenericParameter)
614 Emit (OpCodes.Stelem, type);
615 else if (type.IsPointer)
616 Emit (OpCodes.Stelem_I);
618 Emit (OpCodes.Stelem_Ref);
621 public void EmitInt (int i)
625 ig.Emit (OpCodes.Ldc_I4_M1);
629 ig.Emit (OpCodes.Ldc_I4_0);
633 ig.Emit (OpCodes.Ldc_I4_1);
637 ig.Emit (OpCodes.Ldc_I4_2);
641 ig.Emit (OpCodes.Ldc_I4_3);
645 ig.Emit (OpCodes.Ldc_I4_4);
649 ig.Emit (OpCodes.Ldc_I4_5);
653 ig.Emit (OpCodes.Ldc_I4_6);
657 ig.Emit (OpCodes.Ldc_I4_7);
661 ig.Emit (OpCodes.Ldc_I4_8);
665 if (i >= -128 && i <= 127) {
666 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
668 ig.Emit (OpCodes.Ldc_I4, i);
673 public void EmitLong (long l)
675 if (l >= int.MinValue && l <= int.MaxValue) {
676 EmitInt (unchecked ((int) l));
677 ig.Emit (OpCodes.Conv_I8);
681 if (l >= 0 && l <= uint.MaxValue) {
682 EmitInt (unchecked ((int) l));
683 ig.Emit (OpCodes.Conv_U8);
687 ig.Emit (OpCodes.Ldc_I8, l);
691 // Load the object from the pointer.
693 public void EmitLoadFromPtr (TypeSpec t)
695 if (t == TypeManager.int32_type)
696 ig.Emit (OpCodes.Ldind_I4);
697 else if (t == TypeManager.uint32_type)
698 ig.Emit (OpCodes.Ldind_U4);
699 else if (t == TypeManager.short_type)
700 ig.Emit (OpCodes.Ldind_I2);
701 else if (t == TypeManager.ushort_type)
702 ig.Emit (OpCodes.Ldind_U2);
703 else if (t == TypeManager.char_type)
704 ig.Emit (OpCodes.Ldind_U2);
705 else if (t == TypeManager.byte_type)
706 ig.Emit (OpCodes.Ldind_U1);
707 else if (t == TypeManager.sbyte_type)
708 ig.Emit (OpCodes.Ldind_I1);
709 else if (t == TypeManager.uint64_type)
710 ig.Emit (OpCodes.Ldind_I8);
711 else if (t == TypeManager.int64_type)
712 ig.Emit (OpCodes.Ldind_I8);
713 else if (t == TypeManager.float_type)
714 ig.Emit (OpCodes.Ldind_R4);
715 else if (t == TypeManager.double_type)
716 ig.Emit (OpCodes.Ldind_R8);
717 else if (t == TypeManager.bool_type)
718 ig.Emit (OpCodes.Ldind_I1);
719 else if (t == TypeManager.intptr_type)
720 ig.Emit (OpCodes.Ldind_I);
722 if (t == TypeManager.enum_type)
723 ig.Emit (OpCodes.Ldind_Ref);
725 EmitLoadFromPtr (EnumSpec.GetUnderlyingType (t));
726 } else if (TypeManager.IsStruct (t) || TypeManager.IsGenericParameter (t))
727 Emit (OpCodes.Ldobj, t);
728 else if (t.IsPointer)
729 ig.Emit (OpCodes.Ldind_I);
731 ig.Emit (OpCodes.Ldind_Ref);
735 // The stack contains the pointer and the value of type `type'
737 public void EmitStoreFromPtr (TypeSpec type)
740 type = EnumSpec.GetUnderlyingType (type);
742 if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
743 ig.Emit (OpCodes.Stind_I4);
744 else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
745 ig.Emit (OpCodes.Stind_I8);
746 else if (type == TypeManager.char_type || type == TypeManager.short_type ||
747 type == TypeManager.ushort_type)
748 ig.Emit (OpCodes.Stind_I2);
749 else if (type == TypeManager.float_type)
750 ig.Emit (OpCodes.Stind_R4);
751 else if (type == TypeManager.double_type)
752 ig.Emit (OpCodes.Stind_R8);
753 else if (type == TypeManager.byte_type || type == TypeManager.sbyte_type ||
754 type == TypeManager.bool_type)
755 ig.Emit (OpCodes.Stind_I1);
756 else if (type == TypeManager.intptr_type)
757 ig.Emit (OpCodes.Stind_I);
758 else if (TypeManager.IsStruct (type) || TypeManager.IsGenericParameter (type))
759 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
761 ig.Emit (OpCodes.Stind_Ref);
765 /// Returns a temporary storage for a variable of type t as
766 /// a local variable in the current body.
768 public LocalBuilder GetTemporaryLocal (TypeSpec t)
770 if (temporary_storage != null) {
772 if (temporary_storage.TryGetValue (t, out o)) {
773 if (o is Stack<LocalBuilder>) {
774 var s = (Stack<LocalBuilder>) o;
775 o = s.Count == 0 ? null : s.Pop ();
777 temporary_storage.Remove (t);
781 return (LocalBuilder) o;
783 return DeclareLocal (t, false);
786 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
788 if (temporary_storage == null) {
789 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
790 temporary_storage.Add (t, b);
795 if (!temporary_storage.TryGetValue (t, out o)) {
796 temporary_storage.Add (t, b);
799 var s = o as Stack<LocalBuilder>;
801 s = new Stack<LocalBuilder> ();
802 s.Push ((LocalBuilder)o);
803 temporary_storage [t] = s;
809 /// ReturnValue creates on demand the LocalBuilder for the
810 /// return value from the function. By default this is not
811 /// used. This is only required when returns are found inside
812 /// Try or Catch statements.
814 /// This method is typically invoked from the Emit phase, so
815 /// we allow the creation of a return label if it was not
816 /// requested during the resolution phase. Could be cleaned
817 /// up, but it would replicate a lot of logic in the Emit phase
818 /// of the code that uses it.
820 public LocalBuilder TemporaryReturn ()
822 if (return_value == null){
823 return_value = DeclareLocal (return_type, false);
824 if (!HasReturnLabel){
825 ReturnLabel = DefineLabel ();
826 HasReturnLabel = true;