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 ILGenerator ig;
37 /// The value that is allowed to be returned or NULL if there is no
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 public readonly IMemberContext MemberContext;
88 DynamicSiteClass dynamic_site_container;
90 // TODO: Replace IMemberContext with MemberCore
91 public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type)
93 this.MemberContext = rc;
96 this.return_type = return_type;
99 //ig.__CleverExceptionBlockAssistance ();
105 public TypeSpec CurrentType {
106 get { return MemberContext.CurrentType; }
109 public TypeParameter[] CurrentTypeParameters {
110 get { return MemberContext.CurrentTypeParameters; }
113 public MemberCore CurrentTypeDefinition {
114 get { return MemberContext.CurrentMemberDefinition; }
117 public bool IsStatic {
118 get { return MemberContext.IsStatic; }
121 bool IsAnonymousStoreyMutateRequired {
123 return CurrentAnonymousMethod != null &&
124 CurrentAnonymousMethod.Storey != null &&
125 CurrentAnonymousMethod.Storey.Mutator != null;
129 // Has to be used for emitter errors only
130 public Report Report {
131 get { return MemberContext.Compiler.Report; }
134 public TypeSpec ReturnType {
142 /// This is called immediately before emitting an IL opcode to tell the symbol
143 /// writer to which source line this opcode belongs.
145 public void Mark (Location loc)
147 if (!SymbolWriter.HasSymbolWriter || HasSet (Options.OmitDebugInfo) || loc.IsNull)
150 SymbolWriter.MarkSequencePoint (ig, loc);
153 public void DefineLocalVariable (string name, LocalBuilder builder)
155 SymbolWriter.DefineLocalVariable (name, builder);
158 public void BeginCatchBlock (TypeSpec type)
160 ig.BeginCatchBlock (type.GetMetaInfo ());
163 public void BeginExceptionBlock ()
165 ig.BeginExceptionBlock ();
168 public void BeginFinallyBlock ()
170 ig.BeginFinallyBlock ();
173 public void BeginScope ()
175 SymbolWriter.OpenScope(ig);
178 public void EndExceptionBlock ()
180 ig.EndExceptionBlock ();
183 public void EndScope ()
185 SymbolWriter.CloseScope(ig);
189 // Creates a nested container in this context for all dynamic compiler generated stuff
191 public DynamicSiteClass CreateDynamicSite ()
193 if (dynamic_site_container == null) {
194 var mc = MemberContext.CurrentMemberDefinition as MemberBase;
195 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, CurrentTypeParameters);
197 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
198 dynamic_site_container.CreateType ();
199 dynamic_site_container.DefineType ();
200 dynamic_site_container.ResolveTypeParameters ();
201 dynamic_site_container.Define ();
204 return dynamic_site_container;
207 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
209 if (IsAnonymousStoreyMutateRequired)
210 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
212 return ig.DeclareLocal (type.GetMetaInfo (), pinned);
215 public Label DefineLabel ()
217 return ig.DefineLabel ();
220 public void MarkLabel (Label label)
222 ig.MarkLabel (label);
225 public void Emit (OpCode opcode)
230 public void Emit (OpCode opcode, LocalBuilder local)
232 ig.Emit (opcode, local);
235 public void Emit (OpCode opcode, string arg)
237 ig.Emit (opcode, arg);
240 public void Emit (OpCode opcode, double arg)
242 ig.Emit (opcode, arg);
245 public void Emit (OpCode opcode, float arg)
247 ig.Emit (opcode, arg);
250 public void Emit (OpCode opcode, int arg)
252 ig.Emit (opcode, arg);
255 public void Emit (OpCode opcode, byte arg)
257 ig.Emit (opcode, arg);
260 public void Emit (OpCode opcode, Label label)
262 ig.Emit (opcode, label);
265 public void Emit (OpCode opcode, Label[] labels)
267 ig.Emit (opcode, labels);
270 public void Emit (OpCode opcode, TypeSpec type)
272 if (IsAnonymousStoreyMutateRequired)
273 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
275 ig.Emit (opcode, type.GetMetaInfo ());
278 public void Emit (OpCode opcode, FieldSpec field)
280 if (IsAnonymousStoreyMutateRequired)
281 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
283 ig.Emit (opcode, field.GetMetaInfo ());
286 public void Emit (OpCode opcode, MethodSpec method)
288 if (IsAnonymousStoreyMutateRequired)
289 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
291 if (method.IsConstructor)
292 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
294 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
297 // TODO: REMOVE breaks mutator
298 public void Emit (OpCode opcode, MethodInfo method)
300 ig.Emit (opcode, method);
303 // TODO: REMOVE breaks mutator
304 public void Emit (OpCode opcode, FieldBuilder field)
306 ig.Emit (opcode, field);
309 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
311 // TODO MemberCache: This should mutate too
312 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
315 public void EmitArrayNew (ArrayContainer ac)
318 Emit (OpCodes.Newarr, ac.Element);
320 if (IsAnonymousStoreyMutateRequired)
321 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
323 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
327 public void EmitArrayAddress (ArrayContainer ac)
329 if (ac.Element.IsGenericParameter)
330 ig.Emit (OpCodes.Readonly);
333 if (IsAnonymousStoreyMutateRequired)
334 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
336 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
338 Emit (OpCodes.Ldelema, ac.Element);
343 // Emits the right opcode to load from an array
345 public void EmitArrayLoad (ArrayContainer ac)
348 if (IsAnonymousStoreyMutateRequired)
349 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
351 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
355 var type = ac.Element;
356 if (TypeManager.IsEnumType (type))
357 type = EnumSpec.GetUnderlyingType (type);
359 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
360 Emit (OpCodes.Ldelem_U1);
361 else if (type == TypeManager.sbyte_type)
362 Emit (OpCodes.Ldelem_I1);
363 else if (type == TypeManager.short_type)
364 Emit (OpCodes.Ldelem_I2);
365 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
366 Emit (OpCodes.Ldelem_U2);
367 else if (type == TypeManager.int32_type)
368 Emit (OpCodes.Ldelem_I4);
369 else if (type == TypeManager.uint32_type)
370 Emit (OpCodes.Ldelem_U4);
371 else if (type == TypeManager.uint64_type)
372 Emit (OpCodes.Ldelem_I8);
373 else if (type == TypeManager.int64_type)
374 Emit (OpCodes.Ldelem_I8);
375 else if (type == TypeManager.float_type)
376 Emit (OpCodes.Ldelem_R4);
377 else if (type == TypeManager.double_type)
378 Emit (OpCodes.Ldelem_R8);
379 else if (type == TypeManager.intptr_type)
380 Emit (OpCodes.Ldelem_I);
381 else if (TypeManager.IsStruct (type)) {
382 Emit (OpCodes.Ldelema, type);
383 Emit (OpCodes.Ldobj, type);
384 } else if (type.IsGenericParameter) {
385 Emit (OpCodes.Ldelem, type);
386 } else if (type.IsPointer)
387 Emit (OpCodes.Ldelem_I);
389 Emit (OpCodes.Ldelem_Ref);
393 // Emits the right opcode to store to an array
395 public void EmitArrayStore (ArrayContainer ac)
398 if (IsAnonymousStoreyMutateRequired)
399 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
401 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
405 var type = ac.Element;
408 type = EnumSpec.GetUnderlyingType (type);
410 if (type == TypeManager.byte_type || type == TypeManager.sbyte_type || type == TypeManager.bool_type)
411 Emit (OpCodes.Stelem_I1);
412 else if (type == TypeManager.short_type || type == TypeManager.ushort_type || type == TypeManager.char_type)
413 Emit (OpCodes.Stelem_I2);
414 else if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
415 Emit (OpCodes.Stelem_I4);
416 else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
417 Emit (OpCodes.Stelem_I8);
418 else if (type == TypeManager.float_type)
419 Emit (OpCodes.Stelem_R4);
420 else if (type == TypeManager.double_type)
421 Emit (OpCodes.Stelem_R8);
422 else if (type == TypeManager.intptr_type)
423 Emit (OpCodes.Stobj, type);
424 else if (TypeManager.IsStruct (type))
425 Emit (OpCodes.Stobj, type);
426 else if (type.IsGenericParameter)
427 Emit (OpCodes.Stelem, type);
428 else if (type.IsPointer)
429 Emit (OpCodes.Stelem_I);
431 Emit (OpCodes.Stelem_Ref);
434 public void EmitInt (int i)
438 ig.Emit (OpCodes.Ldc_I4_M1);
442 ig.Emit (OpCodes.Ldc_I4_0);
446 ig.Emit (OpCodes.Ldc_I4_1);
450 ig.Emit (OpCodes.Ldc_I4_2);
454 ig.Emit (OpCodes.Ldc_I4_3);
458 ig.Emit (OpCodes.Ldc_I4_4);
462 ig.Emit (OpCodes.Ldc_I4_5);
466 ig.Emit (OpCodes.Ldc_I4_6);
470 ig.Emit (OpCodes.Ldc_I4_7);
474 ig.Emit (OpCodes.Ldc_I4_8);
478 if (i >= -128 && i <= 127) {
479 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
481 ig.Emit (OpCodes.Ldc_I4, i);
486 public void EmitLong (long l)
488 if (l >= int.MinValue && l <= int.MaxValue) {
489 EmitInt (unchecked ((int) l));
490 ig.Emit (OpCodes.Conv_I8);
494 if (l >= 0 && l <= uint.MaxValue) {
495 EmitInt (unchecked ((int) l));
496 ig.Emit (OpCodes.Conv_U8);
500 ig.Emit (OpCodes.Ldc_I8, l);
504 // Load the object from the pointer.
506 public void EmitLoadFromPtr (TypeSpec t)
508 if (t == TypeManager.int32_type)
509 ig.Emit (OpCodes.Ldind_I4);
510 else if (t == TypeManager.uint32_type)
511 ig.Emit (OpCodes.Ldind_U4);
512 else if (t == TypeManager.short_type)
513 ig.Emit (OpCodes.Ldind_I2);
514 else if (t == TypeManager.ushort_type)
515 ig.Emit (OpCodes.Ldind_U2);
516 else if (t == TypeManager.char_type)
517 ig.Emit (OpCodes.Ldind_U2);
518 else if (t == TypeManager.byte_type)
519 ig.Emit (OpCodes.Ldind_U1);
520 else if (t == TypeManager.sbyte_type)
521 ig.Emit (OpCodes.Ldind_I1);
522 else if (t == TypeManager.uint64_type)
523 ig.Emit (OpCodes.Ldind_I8);
524 else if (t == TypeManager.int64_type)
525 ig.Emit (OpCodes.Ldind_I8);
526 else if (t == TypeManager.float_type)
527 ig.Emit (OpCodes.Ldind_R4);
528 else if (t == TypeManager.double_type)
529 ig.Emit (OpCodes.Ldind_R8);
530 else if (t == TypeManager.bool_type)
531 ig.Emit (OpCodes.Ldind_I1);
532 else if (t == TypeManager.intptr_type)
533 ig.Emit (OpCodes.Ldind_I);
535 if (t == TypeManager.enum_type)
536 ig.Emit (OpCodes.Ldind_Ref);
538 EmitLoadFromPtr (EnumSpec.GetUnderlyingType (t));
539 } else if (TypeManager.IsStruct (t) || TypeManager.IsGenericParameter (t))
540 Emit (OpCodes.Ldobj, t);
541 else if (t.IsPointer)
542 ig.Emit (OpCodes.Ldind_I);
544 ig.Emit (OpCodes.Ldind_Ref);
548 // The stack contains the pointer and the value of type `type'
550 public void EmitStoreFromPtr (TypeSpec type)
553 type = EnumSpec.GetUnderlyingType (type);
555 if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
556 ig.Emit (OpCodes.Stind_I4);
557 else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
558 ig.Emit (OpCodes.Stind_I8);
559 else if (type == TypeManager.char_type || type == TypeManager.short_type ||
560 type == TypeManager.ushort_type)
561 ig.Emit (OpCodes.Stind_I2);
562 else if (type == TypeManager.float_type)
563 ig.Emit (OpCodes.Stind_R4);
564 else if (type == TypeManager.double_type)
565 ig.Emit (OpCodes.Stind_R8);
566 else if (type == TypeManager.byte_type || type == TypeManager.sbyte_type ||
567 type == TypeManager.bool_type)
568 ig.Emit (OpCodes.Stind_I1);
569 else if (type == TypeManager.intptr_type)
570 ig.Emit (OpCodes.Stind_I);
571 else if (TypeManager.IsStruct (type) || TypeManager.IsGenericParameter (type))
572 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
574 ig.Emit (OpCodes.Stind_Ref);
578 /// Returns a temporary storage for a variable of type t as
579 /// a local variable in the current body.
581 public LocalBuilder GetTemporaryLocal (TypeSpec t)
583 if (temporary_storage != null) {
585 if (temporary_storage.TryGetValue (t, out o)) {
586 if (o is Stack<LocalBuilder>) {
587 var s = (Stack<LocalBuilder>) o;
588 o = s.Count == 0 ? null : s.Pop ();
590 temporary_storage.Remove (t);
594 return (LocalBuilder) o;
596 return DeclareLocal (t, false);
599 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
601 if (temporary_storage == null) {
602 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
603 temporary_storage.Add (t, b);
608 if (!temporary_storage.TryGetValue (t, out o)) {
609 temporary_storage.Add (t, b);
612 var s = o as Stack<LocalBuilder>;
614 s = new Stack<LocalBuilder> ();
615 s.Push ((LocalBuilder)o);
616 temporary_storage [t] = s;
622 /// ReturnValue creates on demand the LocalBuilder for the
623 /// return value from the function. By default this is not
624 /// used. This is only required when returns are found inside
625 /// Try or Catch statements.
627 /// This method is typically invoked from the Emit phase, so
628 /// we allow the creation of a return label if it was not
629 /// requested during the resolution phase. Could be cleaned
630 /// up, but it would replicate a lot of logic in the Emit phase
631 /// of the code that uses it.
633 public LocalBuilder TemporaryReturn ()
635 if (return_value == null){
636 return_value = DeclareLocal (return_type, false);
637 if (!HasReturnLabel){
638 ReturnLabel = DefineLabel ();
639 HasReturnLabel = true;