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;
16 using MetaType = IKVM.Reflection.Type;
17 using IKVM.Reflection;
18 using IKVM.Reflection.Emit;
20 using MetaType = System.Type;
21 using System.Reflection;
22 using System.Reflection.Emit;
28 /// An Emit Context is created for each body of code (from methods,
29 /// properties bodies, indexer bodies or constructor bodies)
31 public class EmitContext : BuilderContext
33 // TODO: Has to be private
34 public readonly ILGenerator ig;
37 /// The value that is allowed to be returned or NULL if there is no
40 readonly TypeSpec return_type;
43 /// Keeps track of the Type to LocalBuilder temporary storage created
44 /// to store structures (used to compute the address of the structure
45 /// value on structure method invocations)
47 Dictionary<TypeSpec, object> temporary_storage;
50 /// The location where we store the return value.
52 public LocalBuilder return_value;
55 /// The location where return has to jump to return the
58 public Label ReturnLabel;
61 /// If we already defined the ReturnLabel
63 public bool HasReturnLabel;
66 /// Current loop begin and end labels.
68 public Label LoopBegin, LoopEnd;
71 /// Default target in a switch statement. Only valid if
74 public Label DefaultTarget;
77 /// If this is non-null, points to the current switch statement
82 /// Whether we are inside an anonymous method.
84 public AnonymousExpression CurrentAnonymousMethod;
86 readonly IMemberContext member_context;
88 DynamicSiteClass dynamic_site_container;
90 public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type)
92 this.member_context = rc;
95 this.return_type = return_type;
98 ig.__CleverExceptionBlockAssistance ();
104 public BuildinTypes BuildinTypes {
106 return MemberContext.Module.Compiler.BuildinTypes;
110 public TypeSpec CurrentType {
111 get { return member_context.CurrentType; }
114 public TypeParameter[] CurrentTypeParameters {
115 get { return member_context.CurrentTypeParameters; }
118 public MemberCore CurrentTypeDefinition {
119 get { return member_context.CurrentMemberDefinition; }
122 public bool IsStatic {
123 get { return member_context.IsStatic; }
126 bool IsAnonymousStoreyMutateRequired {
128 return CurrentAnonymousMethod != null &&
129 CurrentAnonymousMethod.Storey != null &&
130 CurrentAnonymousMethod.Storey.Mutator != null;
134 public IMemberContext MemberContext {
136 return member_context;
140 public ModuleContainer Module {
142 return member_context.Module;
146 // Has to be used for specific emitter errors only any
147 // possible resolver errors have to be reported during Resolve
148 public Report Report {
150 return member_context.Module.Compiler.Report;
154 public TypeSpec ReturnType {
162 /// This is called immediately before emitting an IL opcode to tell the symbol
163 /// writer to which source line this opcode belongs.
165 public void Mark (Location loc)
167 if (!SymbolWriter.HasSymbolWriter || HasSet (Options.OmitDebugInfo) || loc.IsNull)
170 SymbolWriter.MarkSequencePoint (ig, loc);
173 public void DefineLocalVariable (string name, LocalBuilder builder)
175 SymbolWriter.DefineLocalVariable (name, builder);
178 public void BeginCatchBlock (TypeSpec type)
180 ig.BeginCatchBlock (type.GetMetaInfo ());
183 public void BeginExceptionBlock ()
185 ig.BeginExceptionBlock ();
188 public void BeginFinallyBlock ()
190 ig.BeginFinallyBlock ();
193 public void BeginScope ()
195 SymbolWriter.OpenScope(ig);
198 public void EndExceptionBlock ()
200 ig.EndExceptionBlock ();
203 public void EndScope ()
205 SymbolWriter.CloseScope(ig);
209 // Creates a nested container in this context for all dynamic compiler generated stuff
211 public DynamicSiteClass CreateDynamicSite ()
213 if (dynamic_site_container == null) {
214 var mc = member_context.CurrentMemberDefinition as MemberBase;
215 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, CurrentTypeParameters);
217 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
218 dynamic_site_container.CreateType ();
219 dynamic_site_container.DefineType ();
220 dynamic_site_container.ResolveTypeParameters ();
221 dynamic_site_container.Define ();
224 return dynamic_site_container;
227 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
229 if (IsAnonymousStoreyMutateRequired)
230 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
232 return ig.DeclareLocal (type.GetMetaInfo (), pinned);
235 public Label DefineLabel ()
237 return ig.DefineLabel ();
240 public void MarkLabel (Label label)
242 ig.MarkLabel (label);
245 public void Emit (OpCode opcode)
250 public void Emit (OpCode opcode, LocalBuilder local)
252 ig.Emit (opcode, local);
255 public void Emit (OpCode opcode, string arg)
257 ig.Emit (opcode, arg);
260 public void Emit (OpCode opcode, double arg)
262 ig.Emit (opcode, arg);
265 public void Emit (OpCode opcode, float arg)
267 ig.Emit (opcode, arg);
270 public void Emit (OpCode opcode, int arg)
272 ig.Emit (opcode, arg);
275 public void Emit (OpCode opcode, byte arg)
277 ig.Emit (opcode, arg);
280 public void Emit (OpCode opcode, Label label)
282 ig.Emit (opcode, label);
285 public void Emit (OpCode opcode, Label[] labels)
287 ig.Emit (opcode, labels);
290 public void Emit (OpCode opcode, TypeSpec type)
292 if (IsAnonymousStoreyMutateRequired)
293 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
295 ig.Emit (opcode, type.GetMetaInfo ());
298 public void Emit (OpCode opcode, FieldSpec field)
300 if (IsAnonymousStoreyMutateRequired)
301 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
303 ig.Emit (opcode, field.GetMetaInfo ());
306 public void Emit (OpCode opcode, MethodSpec method)
308 if (IsAnonymousStoreyMutateRequired)
309 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
311 if (method.IsConstructor)
312 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
314 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
317 // TODO: REMOVE breaks mutator
318 public void Emit (OpCode opcode, MethodInfo method)
320 ig.Emit (opcode, method);
323 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
325 // TODO MemberCache: This should mutate too
326 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
329 public void EmitArrayNew (ArrayContainer ac)
332 Emit (OpCodes.Newarr, ac.Element);
334 if (IsAnonymousStoreyMutateRequired)
335 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
337 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
341 public void EmitArrayAddress (ArrayContainer ac)
343 if (ac.Element.IsGenericParameter)
344 ig.Emit (OpCodes.Readonly);
347 if (IsAnonymousStoreyMutateRequired)
348 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
350 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
352 Emit (OpCodes.Ldelema, ac.Element);
357 // Emits the right opcode to load from an array
359 public void EmitArrayLoad (ArrayContainer ac)
362 if (IsAnonymousStoreyMutateRequired)
363 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
365 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
369 var type = ac.Element;
370 if (type.Kind == MemberKind.Enum)
371 type = EnumSpec.GetUnderlyingType (type);
373 switch (type.BuildinType) {
374 case BuildinTypeSpec.Type.Byte:
375 case BuildinTypeSpec.Type.Bool:
376 Emit (OpCodes.Ldelem_U1);
378 case BuildinTypeSpec.Type.SByte:
379 Emit (OpCodes.Ldelem_I1);
381 case BuildinTypeSpec.Type.Short:
382 Emit (OpCodes.Ldelem_I2);
384 case BuildinTypeSpec.Type.UShort:
385 case BuildinTypeSpec.Type.Char:
386 Emit (OpCodes.Ldelem_U2);
388 case BuildinTypeSpec.Type.Int:
389 Emit (OpCodes.Ldelem_I4);
391 case BuildinTypeSpec.Type.UInt:
392 Emit (OpCodes.Ldelem_U4);
394 case BuildinTypeSpec.Type.ULong:
395 case BuildinTypeSpec.Type.Long:
396 Emit (OpCodes.Ldelem_I8);
398 case BuildinTypeSpec.Type.Float:
399 Emit (OpCodes.Ldelem_R4);
401 case BuildinTypeSpec.Type.Double:
402 Emit (OpCodes.Ldelem_R8);
404 case BuildinTypeSpec.Type.IntPtr:
405 Emit (OpCodes.Ldelem_I);
410 case MemberKind.Struct:
411 Emit (OpCodes.Ldelema, type);
412 Emit (OpCodes.Ldobj, type);
414 case MemberKind.TypeParameter:
415 Emit (OpCodes.Ldelem, type);
417 case MemberKind.PointerType:
418 Emit (OpCodes.Ldelem_I);
421 Emit (OpCodes.Ldelem_Ref);
427 // Emits the right opcode to store to an array
429 public void EmitArrayStore (ArrayContainer ac)
432 if (IsAnonymousStoreyMutateRequired)
433 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
435 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
439 var type = ac.Element;
441 if (type.Kind == MemberKind.Enum)
442 type = EnumSpec.GetUnderlyingType (type);
444 switch (type.BuildinType) {
445 case BuildinTypeSpec.Type.Byte:
446 case BuildinTypeSpec.Type.SByte:
447 case BuildinTypeSpec.Type.Bool:
448 Emit (OpCodes.Stelem_I1);
450 case BuildinTypeSpec.Type.Short:
451 case BuildinTypeSpec.Type.UShort:
452 case BuildinTypeSpec.Type.Char:
453 Emit (OpCodes.Stelem_I2);
455 case BuildinTypeSpec.Type.Int:
456 case BuildinTypeSpec.Type.UInt:
457 Emit (OpCodes.Stelem_I4);
459 case BuildinTypeSpec.Type.Long:
460 case BuildinTypeSpec.Type.ULong:
461 Emit (OpCodes.Stelem_I8);
463 case BuildinTypeSpec.Type.Float:
464 Emit (OpCodes.Stelem_R4);
466 case BuildinTypeSpec.Type.Double:
467 Emit (OpCodes.Stelem_R8);
472 case MemberKind.Struct:
473 Emit (OpCodes.Stobj, type);
475 case MemberKind.TypeParameter:
476 Emit (OpCodes.Stelem, type);
478 case MemberKind.PointerType:
479 Emit (OpCodes.Stelem_I);
482 Emit (OpCodes.Stelem_Ref);
487 public void EmitInt (int i)
491 ig.Emit (OpCodes.Ldc_I4_M1);
495 ig.Emit (OpCodes.Ldc_I4_0);
499 ig.Emit (OpCodes.Ldc_I4_1);
503 ig.Emit (OpCodes.Ldc_I4_2);
507 ig.Emit (OpCodes.Ldc_I4_3);
511 ig.Emit (OpCodes.Ldc_I4_4);
515 ig.Emit (OpCodes.Ldc_I4_5);
519 ig.Emit (OpCodes.Ldc_I4_6);
523 ig.Emit (OpCodes.Ldc_I4_7);
527 ig.Emit (OpCodes.Ldc_I4_8);
531 if (i >= -128 && i <= 127) {
532 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
534 ig.Emit (OpCodes.Ldc_I4, i);
539 public void EmitLong (long l)
541 if (l >= int.MinValue && l <= int.MaxValue) {
542 EmitInt (unchecked ((int) l));
543 ig.Emit (OpCodes.Conv_I8);
547 if (l >= 0 && l <= uint.MaxValue) {
548 EmitInt (unchecked ((int) l));
549 ig.Emit (OpCodes.Conv_U8);
553 ig.Emit (OpCodes.Ldc_I8, l);
557 // Load the object from the pointer.
559 public void EmitLoadFromPtr (TypeSpec type)
561 if (type.Kind == MemberKind.Enum)
562 type = EnumSpec.GetUnderlyingType (type);
564 switch (type.BuildinType) {
565 case BuildinTypeSpec.Type.Int:
566 ig.Emit (OpCodes.Ldind_I4);
568 case BuildinTypeSpec.Type.UInt:
569 ig.Emit (OpCodes.Ldind_U4);
571 case BuildinTypeSpec.Type.Short:
572 ig.Emit (OpCodes.Ldind_I2);
574 case BuildinTypeSpec.Type.UShort:
575 case BuildinTypeSpec.Type.Char:
576 ig.Emit (OpCodes.Ldind_U2);
578 case BuildinTypeSpec.Type.Byte:
579 ig.Emit (OpCodes.Ldind_U1);
581 case BuildinTypeSpec.Type.SByte:
582 case BuildinTypeSpec.Type.Bool:
583 ig.Emit (OpCodes.Ldind_I1);
585 case BuildinTypeSpec.Type.ULong:
586 case BuildinTypeSpec.Type.Long:
587 ig.Emit (OpCodes.Ldind_I8);
589 case BuildinTypeSpec.Type.Float:
590 ig.Emit (OpCodes.Ldind_R4);
592 case BuildinTypeSpec.Type.Double:
593 ig.Emit (OpCodes.Ldind_R8);
595 case BuildinTypeSpec.Type.IntPtr:
596 ig.Emit (OpCodes.Ldind_I);
601 case MemberKind.Struct:
602 case MemberKind.TypeParameter:
603 Emit (OpCodes.Ldobj, type);
605 case MemberKind.PointerType:
606 ig.Emit (OpCodes.Ldind_I);
609 ig.Emit (OpCodes.Ldind_Ref);
615 // The stack contains the pointer and the value of type `type'
617 public void EmitStoreFromPtr (TypeSpec type)
620 type = EnumSpec.GetUnderlyingType (type);
622 switch (type.BuildinType) {
623 case BuildinTypeSpec.Type.Int:
624 case BuildinTypeSpec.Type.UInt:
625 ig.Emit (OpCodes.Stind_I4);
627 case BuildinTypeSpec.Type.Long:
628 case BuildinTypeSpec.Type.ULong:
629 ig.Emit (OpCodes.Stind_I8);
631 case BuildinTypeSpec.Type.Char:
632 case BuildinTypeSpec.Type.Short:
633 case BuildinTypeSpec.Type.UShort:
634 ig.Emit (OpCodes.Stind_I2);
636 case BuildinTypeSpec.Type.Float:
637 ig.Emit (OpCodes.Stind_R4);
639 case BuildinTypeSpec.Type.Double:
640 ig.Emit (OpCodes.Stind_R8);
642 case BuildinTypeSpec.Type.Byte:
643 case BuildinTypeSpec.Type.SByte:
644 case BuildinTypeSpec.Type.Bool:
645 ig.Emit (OpCodes.Stind_I1);
647 case BuildinTypeSpec.Type.IntPtr:
648 ig.Emit (OpCodes.Stind_I);
652 if (type.IsStruct || TypeManager.IsGenericParameter (type))
653 Emit (OpCodes.Stobj, type);
655 ig.Emit (OpCodes.Stind_Ref);
659 /// Returns a temporary storage for a variable of type t as
660 /// a local variable in the current body.
662 public LocalBuilder GetTemporaryLocal (TypeSpec t)
664 if (temporary_storage != null) {
666 if (temporary_storage.TryGetValue (t, out o)) {
667 if (o is Stack<LocalBuilder>) {
668 var s = (Stack<LocalBuilder>) o;
669 o = s.Count == 0 ? null : s.Pop ();
671 temporary_storage.Remove (t);
675 return (LocalBuilder) o;
677 return DeclareLocal (t, false);
680 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
682 if (temporary_storage == null) {
683 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
684 temporary_storage.Add (t, b);
689 if (!temporary_storage.TryGetValue (t, out o)) {
690 temporary_storage.Add (t, b);
693 var s = o as Stack<LocalBuilder>;
695 s = new Stack<LocalBuilder> ();
696 s.Push ((LocalBuilder)o);
697 temporary_storage [t] = s;
703 /// ReturnValue creates on demand the LocalBuilder for the
704 /// return value from the function. By default this is not
705 /// used. This is only required when returns are found inside
706 /// Try or Catch statements.
708 /// This method is typically invoked from the Emit phase, so
709 /// we allow the creation of a return label if it was not
710 /// requested during the resolution phase. Could be cleaned
711 /// up, but it would replicate a lot of logic in the Emit phase
712 /// of the code that uses it.
714 public LocalBuilder TemporaryReturn ()
716 if (return_value == null){
717 return_value = DeclareLocal (return_type, false);
718 if (!HasReturnLabel){
719 ReturnLabel = DefineLabel ();
720 HasReturnLabel = true;