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 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
305 // TODO MemberCache: This should mutate too
306 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
309 public void EmitArrayNew (ArrayContainer ac)
312 Emit (OpCodes.Newarr, ac.Element);
314 if (IsAnonymousStoreyMutateRequired)
315 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
317 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
321 public void EmitArrayAddress (ArrayContainer ac)
323 if (ac.Element.IsGenericParameter)
324 ig.Emit (OpCodes.Readonly);
327 if (IsAnonymousStoreyMutateRequired)
328 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
330 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
332 Emit (OpCodes.Ldelema, ac.Element);
337 // Emits the right opcode to load from an array
339 public void EmitArrayLoad (ArrayContainer ac)
342 if (IsAnonymousStoreyMutateRequired)
343 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
345 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
349 var type = ac.Element;
350 if (TypeManager.IsEnumType (type))
351 type = EnumSpec.GetUnderlyingType (type);
353 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
354 Emit (OpCodes.Ldelem_U1);
355 else if (type == TypeManager.sbyte_type)
356 Emit (OpCodes.Ldelem_I1);
357 else if (type == TypeManager.short_type)
358 Emit (OpCodes.Ldelem_I2);
359 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
360 Emit (OpCodes.Ldelem_U2);
361 else if (type == TypeManager.int32_type)
362 Emit (OpCodes.Ldelem_I4);
363 else if (type == TypeManager.uint32_type)
364 Emit (OpCodes.Ldelem_U4);
365 else if (type == TypeManager.uint64_type)
366 Emit (OpCodes.Ldelem_I8);
367 else if (type == TypeManager.int64_type)
368 Emit (OpCodes.Ldelem_I8);
369 else if (type == TypeManager.float_type)
370 Emit (OpCodes.Ldelem_R4);
371 else if (type == TypeManager.double_type)
372 Emit (OpCodes.Ldelem_R8);
373 else if (type == TypeManager.intptr_type)
374 Emit (OpCodes.Ldelem_I);
375 else if (TypeManager.IsStruct (type)) {
376 Emit (OpCodes.Ldelema, type);
377 Emit (OpCodes.Ldobj, type);
378 } else if (type.IsGenericParameter) {
379 Emit (OpCodes.Ldelem, type);
380 } else if (type.IsPointer)
381 Emit (OpCodes.Ldelem_I);
383 Emit (OpCodes.Ldelem_Ref);
387 // Emits the right opcode to store to an array
389 public void EmitArrayStore (ArrayContainer ac)
392 if (IsAnonymousStoreyMutateRequired)
393 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
395 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
399 var type = ac.Element;
402 type = EnumSpec.GetUnderlyingType (type);
404 if (type == TypeManager.byte_type || type == TypeManager.sbyte_type || type == TypeManager.bool_type)
405 Emit (OpCodes.Stelem_I1);
406 else if (type == TypeManager.short_type || type == TypeManager.ushort_type || type == TypeManager.char_type)
407 Emit (OpCodes.Stelem_I2);
408 else if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
409 Emit (OpCodes.Stelem_I4);
410 else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
411 Emit (OpCodes.Stelem_I8);
412 else if (type == TypeManager.float_type)
413 Emit (OpCodes.Stelem_R4);
414 else if (type == TypeManager.double_type)
415 Emit (OpCodes.Stelem_R8);
416 else if (type == TypeManager.intptr_type)
417 Emit (OpCodes.Stobj, type);
418 else if (TypeManager.IsStruct (type))
419 Emit (OpCodes.Stobj, type);
420 else if (type.IsGenericParameter)
421 Emit (OpCodes.Stelem, type);
422 else if (type.IsPointer)
423 Emit (OpCodes.Stelem_I);
425 Emit (OpCodes.Stelem_Ref);
428 public void EmitInt (int i)
432 ig.Emit (OpCodes.Ldc_I4_M1);
436 ig.Emit (OpCodes.Ldc_I4_0);
440 ig.Emit (OpCodes.Ldc_I4_1);
444 ig.Emit (OpCodes.Ldc_I4_2);
448 ig.Emit (OpCodes.Ldc_I4_3);
452 ig.Emit (OpCodes.Ldc_I4_4);
456 ig.Emit (OpCodes.Ldc_I4_5);
460 ig.Emit (OpCodes.Ldc_I4_6);
464 ig.Emit (OpCodes.Ldc_I4_7);
468 ig.Emit (OpCodes.Ldc_I4_8);
472 if (i >= -128 && i <= 127) {
473 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
475 ig.Emit (OpCodes.Ldc_I4, i);
480 public void EmitLong (long l)
482 if (l >= int.MinValue && l <= int.MaxValue) {
483 EmitInt (unchecked ((int) l));
484 ig.Emit (OpCodes.Conv_I8);
488 if (l >= 0 && l <= uint.MaxValue) {
489 EmitInt (unchecked ((int) l));
490 ig.Emit (OpCodes.Conv_U8);
494 ig.Emit (OpCodes.Ldc_I8, l);
498 // Load the object from the pointer.
500 public void EmitLoadFromPtr (TypeSpec t)
502 if (t == TypeManager.int32_type)
503 ig.Emit (OpCodes.Ldind_I4);
504 else if (t == TypeManager.uint32_type)
505 ig.Emit (OpCodes.Ldind_U4);
506 else if (t == TypeManager.short_type)
507 ig.Emit (OpCodes.Ldind_I2);
508 else if (t == TypeManager.ushort_type)
509 ig.Emit (OpCodes.Ldind_U2);
510 else if (t == TypeManager.char_type)
511 ig.Emit (OpCodes.Ldind_U2);
512 else if (t == TypeManager.byte_type)
513 ig.Emit (OpCodes.Ldind_U1);
514 else if (t == TypeManager.sbyte_type)
515 ig.Emit (OpCodes.Ldind_I1);
516 else if (t == TypeManager.uint64_type)
517 ig.Emit (OpCodes.Ldind_I8);
518 else if (t == TypeManager.int64_type)
519 ig.Emit (OpCodes.Ldind_I8);
520 else if (t == TypeManager.float_type)
521 ig.Emit (OpCodes.Ldind_R4);
522 else if (t == TypeManager.double_type)
523 ig.Emit (OpCodes.Ldind_R8);
524 else if (t == TypeManager.bool_type)
525 ig.Emit (OpCodes.Ldind_I1);
526 else if (t == TypeManager.intptr_type)
527 ig.Emit (OpCodes.Ldind_I);
529 if (t == TypeManager.enum_type)
530 ig.Emit (OpCodes.Ldind_Ref);
532 EmitLoadFromPtr (EnumSpec.GetUnderlyingType (t));
533 } else if (TypeManager.IsStruct (t) || TypeManager.IsGenericParameter (t))
534 Emit (OpCodes.Ldobj, t);
535 else if (t.IsPointer)
536 ig.Emit (OpCodes.Ldind_I);
538 ig.Emit (OpCodes.Ldind_Ref);
542 // The stack contains the pointer and the value of type `type'
544 public void EmitStoreFromPtr (TypeSpec type)
547 type = EnumSpec.GetUnderlyingType (type);
549 if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
550 ig.Emit (OpCodes.Stind_I4);
551 else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
552 ig.Emit (OpCodes.Stind_I8);
553 else if (type == TypeManager.char_type || type == TypeManager.short_type ||
554 type == TypeManager.ushort_type)
555 ig.Emit (OpCodes.Stind_I2);
556 else if (type == TypeManager.float_type)
557 ig.Emit (OpCodes.Stind_R4);
558 else if (type == TypeManager.double_type)
559 ig.Emit (OpCodes.Stind_R8);
560 else if (type == TypeManager.byte_type || type == TypeManager.sbyte_type ||
561 type == TypeManager.bool_type)
562 ig.Emit (OpCodes.Stind_I1);
563 else if (type == TypeManager.intptr_type)
564 ig.Emit (OpCodes.Stind_I);
565 else if (TypeManager.IsStruct (type) || TypeManager.IsGenericParameter (type))
566 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
568 ig.Emit (OpCodes.Stind_Ref);
572 /// Returns a temporary storage for a variable of type t as
573 /// a local variable in the current body.
575 public LocalBuilder GetTemporaryLocal (TypeSpec t)
577 if (temporary_storage != null) {
579 if (temporary_storage.TryGetValue (t, out o)) {
580 if (o is Stack<LocalBuilder>) {
581 var s = (Stack<LocalBuilder>) o;
582 o = s.Count == 0 ? null : s.Pop ();
584 temporary_storage.Remove (t);
588 return (LocalBuilder) o;
590 return DeclareLocal (t, false);
593 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
595 if (temporary_storage == null) {
596 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
597 temporary_storage.Add (t, b);
602 if (!temporary_storage.TryGetValue (t, out o)) {
603 temporary_storage.Add (t, b);
606 var s = o as Stack<LocalBuilder>;
608 s = new Stack<LocalBuilder> ();
609 s.Push ((LocalBuilder)o);
610 temporary_storage [t] = s;
616 /// ReturnValue creates on demand the LocalBuilder for the
617 /// return value from the function. By default this is not
618 /// used. This is only required when returns are found inside
619 /// Try or Catch statements.
621 /// This method is typically invoked from the Emit phase, so
622 /// we allow the creation of a return label if it was not
623 /// requested during the resolution phase. Could be cleaned
624 /// up, but it would replicate a lot of logic in the Emit phase
625 /// of the code that uses it.
627 public LocalBuilder TemporaryReturn ()
629 if (return_value == null){
630 return_value = DeclareLocal (return_type, false);
631 if (!HasReturnLabel){
632 ReturnLabel = DefineLabel ();
633 HasReturnLabel = true;