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;
20 /// An Emit Context is created for each body of code (from methods,
21 /// properties bodies, indexer bodies or constructor bodies)
23 public class EmitContext : BuilderContext
25 // TODO: Has to be private
26 public ILGenerator ig;
29 /// The value that is allowed to be returned or NULL if there is no
35 /// Keeps track of the Type to LocalBuilder temporary storage created
36 /// to store structures (used to compute the address of the structure
37 /// value on structure method invocations)
39 Dictionary<TypeSpec, object> temporary_storage;
42 /// The location where we store the return value.
44 public LocalBuilder return_value;
47 /// The location where return has to jump to return the
50 public Label ReturnLabel;
53 /// If we already defined the ReturnLabel
55 public bool HasReturnLabel;
58 /// Current loop begin and end labels.
60 public Label LoopBegin, LoopEnd;
63 /// Default target in a switch statement. Only valid if
66 public Label DefaultTarget;
69 /// If this is non-null, points to the current switch statement
74 /// Whether we are inside an anonymous method.
76 public AnonymousExpression CurrentAnonymousMethod;
78 public readonly IMemberContext MemberContext;
80 DynamicSiteClass dynamic_site_container;
82 // TODO: Replace IMemberContext with MemberCore
83 public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type)
85 this.MemberContext = rc;
88 this.return_type = return_type;
93 public TypeSpec CurrentType {
94 get { return MemberContext.CurrentType; }
97 public TypeParameter[] CurrentTypeParameters {
98 get { return MemberContext.CurrentTypeParameters; }
101 public MemberCore CurrentTypeDefinition {
102 get { return MemberContext.CurrentMemberDefinition; }
105 public bool IsStatic {
106 get { return MemberContext.IsStatic; }
109 bool IsAnonymousStoreyMutateRequired {
111 return CurrentAnonymousMethod != null &&
112 CurrentAnonymousMethod.Storey != null &&
113 CurrentAnonymousMethod.Storey.Mutator != null;
117 // Has to be used for emitter errors only
118 public Report Report {
119 get { return MemberContext.Compiler.Report; }
122 public TypeSpec ReturnType {
130 /// This is called immediately before emitting an IL opcode to tell the symbol
131 /// writer to which source line this opcode belongs.
133 public void Mark (Location loc)
135 if (!SymbolWriter.HasSymbolWriter || HasSet (Options.OmitDebugInfo) || loc.IsNull)
138 SymbolWriter.MarkSequencePoint (ig, loc);
141 public void DefineLocalVariable (string name, LocalBuilder builder)
143 SymbolWriter.DefineLocalVariable (name, builder);
146 public void BeginCatchBlock (TypeSpec type)
148 ig.BeginCatchBlock (type.GetMetaInfo ());
151 public void BeginExceptionBlock ()
153 ig.BeginExceptionBlock ();
156 public void BeginFinallyBlock ()
158 ig.BeginFinallyBlock ();
161 public void BeginScope ()
164 SymbolWriter.OpenScope(ig);
167 public void EndExceptionBlock ()
169 ig.EndExceptionBlock ();
172 public void EndScope ()
175 SymbolWriter.CloseScope(ig);
179 // Creates a nested container in this context for all dynamic compiler generated stuff
181 public DynamicSiteClass CreateDynamicSite ()
183 if (dynamic_site_container == null) {
184 var mc = MemberContext.CurrentMemberDefinition as MemberBase;
185 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, CurrentTypeParameters);
187 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
188 dynamic_site_container.CreateType ();
189 dynamic_site_container.DefineType ();
190 dynamic_site_container.ResolveTypeParameters ();
191 dynamic_site_container.Define ();
194 return dynamic_site_container;
197 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
199 if (IsAnonymousStoreyMutateRequired)
200 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
202 return ig.DeclareLocal (type.GetMetaInfo (), pinned);
205 public Label DefineLabel ()
207 return ig.DefineLabel ();
210 public void MarkLabel (Label label)
212 ig.MarkLabel (label);
215 public void Emit (OpCode opcode)
220 public void Emit (OpCode opcode, LocalBuilder local)
222 ig.Emit (opcode, local);
225 public void Emit (OpCode opcode, string arg)
227 ig.Emit (opcode, arg);
230 public void Emit (OpCode opcode, double arg)
232 ig.Emit (opcode, arg);
235 public void Emit (OpCode opcode, float arg)
237 ig.Emit (opcode, arg);
240 public void Emit (OpCode opcode, int arg)
242 ig.Emit (opcode, arg);
245 public void Emit (OpCode opcode, byte arg)
247 ig.Emit (opcode, arg);
250 public void Emit (OpCode opcode, Label label)
252 ig.Emit (opcode, label);
255 public void Emit (OpCode opcode, Label[] labels)
257 ig.Emit (opcode, labels);
260 public void Emit (OpCode opcode, TypeSpec type)
262 if (IsAnonymousStoreyMutateRequired)
263 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
265 ig.Emit (opcode, type.GetMetaInfo ());
268 public void Emit (OpCode opcode, FieldSpec field)
270 if (IsAnonymousStoreyMutateRequired)
271 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
273 ig.Emit (opcode, field.GetMetaInfo ());
276 public void Emit (OpCode opcode, MethodSpec method)
278 if (IsAnonymousStoreyMutateRequired)
279 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
281 if (method.IsConstructor)
282 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
284 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
287 // TODO: REMOVE breaks mutator
288 public void Emit (OpCode opcode, MethodInfo method)
290 ig.Emit (opcode, method);
293 // TODO: REMOVE breaks mutator
294 public void Emit (OpCode opcode, FieldBuilder field)
296 ig.Emit (opcode, field);
299 public void Emit (OpCode opcode, MethodSpec method, Type[] vargs)
301 // TODO MemberCache: This should mutate too
302 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
305 public void EmitArrayNew (ArrayContainer ac)
308 Emit (OpCodes.Newarr, ac.Element);
310 if (IsAnonymousStoreyMutateRequired)
311 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
313 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
317 public void EmitArrayAddress (ArrayContainer ac)
319 if (ac.Element.IsGenericParameter)
320 ig.Emit (OpCodes.Readonly);
323 if (IsAnonymousStoreyMutateRequired)
324 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
326 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
328 Emit (OpCodes.Ldelema, ac.Element);
333 // Emits the right opcode to load from an array
335 public void EmitArrayLoad (ArrayContainer ac)
338 if (IsAnonymousStoreyMutateRequired)
339 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
341 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
345 var type = ac.Element;
346 if (TypeManager.IsEnumType (type))
347 type = EnumSpec.GetUnderlyingType (type);
349 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
350 Emit (OpCodes.Ldelem_U1);
351 else if (type == TypeManager.sbyte_type)
352 Emit (OpCodes.Ldelem_I1);
353 else if (type == TypeManager.short_type)
354 Emit (OpCodes.Ldelem_I2);
355 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
356 Emit (OpCodes.Ldelem_U2);
357 else if (type == TypeManager.int32_type)
358 Emit (OpCodes.Ldelem_I4);
359 else if (type == TypeManager.uint32_type)
360 Emit (OpCodes.Ldelem_U4);
361 else if (type == TypeManager.uint64_type)
362 Emit (OpCodes.Ldelem_I8);
363 else if (type == TypeManager.int64_type)
364 Emit (OpCodes.Ldelem_I8);
365 else if (type == TypeManager.float_type)
366 Emit (OpCodes.Ldelem_R4);
367 else if (type == TypeManager.double_type)
368 Emit (OpCodes.Ldelem_R8);
369 else if (type == TypeManager.intptr_type)
370 Emit (OpCodes.Ldelem_I);
371 else if (TypeManager.IsStruct (type)) {
372 Emit (OpCodes.Ldelema, type);
373 Emit (OpCodes.Ldobj, type);
374 } else if (type.IsGenericParameter) {
375 Emit (OpCodes.Ldelem, type);
376 } else if (type.IsPointer)
377 Emit (OpCodes.Ldelem_I);
379 Emit (OpCodes.Ldelem_Ref);
383 // Emits the right opcode to store to an array
385 public void EmitArrayStore (ArrayContainer ac)
388 if (IsAnonymousStoreyMutateRequired)
389 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
391 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
395 var type = ac.Element;
398 type = EnumSpec.GetUnderlyingType (type);
400 if (type == TypeManager.byte_type || type == TypeManager.sbyte_type || type == TypeManager.bool_type)
401 Emit (OpCodes.Stelem_I1);
402 else if (type == TypeManager.short_type || type == TypeManager.ushort_type || type == TypeManager.char_type)
403 Emit (OpCodes.Stelem_I2);
404 else if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
405 Emit (OpCodes.Stelem_I4);
406 else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
407 Emit (OpCodes.Stelem_I8);
408 else if (type == TypeManager.float_type)
409 Emit (OpCodes.Stelem_R4);
410 else if (type == TypeManager.double_type)
411 Emit (OpCodes.Stelem_R8);
412 else if (type == TypeManager.intptr_type)
413 Emit (OpCodes.Stobj, type);
414 else if (TypeManager.IsStruct (type))
415 Emit (OpCodes.Stobj, type);
416 else if (type.IsGenericParameter)
417 Emit (OpCodes.Stelem, type);
418 else if (type.IsPointer)
419 Emit (OpCodes.Stelem_I);
421 Emit (OpCodes.Stelem_Ref);
424 public void EmitInt (int i)
428 ig.Emit (OpCodes.Ldc_I4_M1);
432 ig.Emit (OpCodes.Ldc_I4_0);
436 ig.Emit (OpCodes.Ldc_I4_1);
440 ig.Emit (OpCodes.Ldc_I4_2);
444 ig.Emit (OpCodes.Ldc_I4_3);
448 ig.Emit (OpCodes.Ldc_I4_4);
452 ig.Emit (OpCodes.Ldc_I4_5);
456 ig.Emit (OpCodes.Ldc_I4_6);
460 ig.Emit (OpCodes.Ldc_I4_7);
464 ig.Emit (OpCodes.Ldc_I4_8);
468 if (i >= -128 && i <= 127) {
469 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
471 ig.Emit (OpCodes.Ldc_I4, i);
476 public void EmitLong (long l)
478 if (l >= int.MinValue && l <= int.MaxValue) {
479 EmitInt (unchecked ((int) l));
480 ig.Emit (OpCodes.Conv_I8);
484 if (l >= 0 && l <= uint.MaxValue) {
485 EmitInt (unchecked ((int) l));
486 ig.Emit (OpCodes.Conv_U8);
490 ig.Emit (OpCodes.Ldc_I8, l);
494 // Load the object from the pointer.
496 public void EmitLoadFromPtr (TypeSpec t)
498 if (t == TypeManager.int32_type)
499 ig.Emit (OpCodes.Ldind_I4);
500 else if (t == TypeManager.uint32_type)
501 ig.Emit (OpCodes.Ldind_U4);
502 else if (t == TypeManager.short_type)
503 ig.Emit (OpCodes.Ldind_I2);
504 else if (t == TypeManager.ushort_type)
505 ig.Emit (OpCodes.Ldind_U2);
506 else if (t == TypeManager.char_type)
507 ig.Emit (OpCodes.Ldind_U2);
508 else if (t == TypeManager.byte_type)
509 ig.Emit (OpCodes.Ldind_U1);
510 else if (t == TypeManager.sbyte_type)
511 ig.Emit (OpCodes.Ldind_I1);
512 else if (t == TypeManager.uint64_type)
513 ig.Emit (OpCodes.Ldind_I8);
514 else if (t == TypeManager.int64_type)
515 ig.Emit (OpCodes.Ldind_I8);
516 else if (t == TypeManager.float_type)
517 ig.Emit (OpCodes.Ldind_R4);
518 else if (t == TypeManager.double_type)
519 ig.Emit (OpCodes.Ldind_R8);
520 else if (t == TypeManager.bool_type)
521 ig.Emit (OpCodes.Ldind_I1);
522 else if (t == TypeManager.intptr_type)
523 ig.Emit (OpCodes.Ldind_I);
525 if (t == TypeManager.enum_type)
526 ig.Emit (OpCodes.Ldind_Ref);
528 EmitLoadFromPtr (EnumSpec.GetUnderlyingType (t));
529 } else if (TypeManager.IsStruct (t) || TypeManager.IsGenericParameter (t))
530 Emit (OpCodes.Ldobj, t);
531 else if (t.IsPointer)
532 ig.Emit (OpCodes.Ldind_I);
534 ig.Emit (OpCodes.Ldind_Ref);
538 // The stack contains the pointer and the value of type `type'
540 public void EmitStoreFromPtr (TypeSpec type)
543 type = EnumSpec.GetUnderlyingType (type);
545 if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
546 ig.Emit (OpCodes.Stind_I4);
547 else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
548 ig.Emit (OpCodes.Stind_I8);
549 else if (type == TypeManager.char_type || type == TypeManager.short_type ||
550 type == TypeManager.ushort_type)
551 ig.Emit (OpCodes.Stind_I2);
552 else if (type == TypeManager.float_type)
553 ig.Emit (OpCodes.Stind_R4);
554 else if (type == TypeManager.double_type)
555 ig.Emit (OpCodes.Stind_R8);
556 else if (type == TypeManager.byte_type || type == TypeManager.sbyte_type ||
557 type == TypeManager.bool_type)
558 ig.Emit (OpCodes.Stind_I1);
559 else if (type == TypeManager.intptr_type)
560 ig.Emit (OpCodes.Stind_I);
561 else if (TypeManager.IsStruct (type) || TypeManager.IsGenericParameter (type))
562 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
564 ig.Emit (OpCodes.Stind_Ref);
568 /// Returns a temporary storage for a variable of type t as
569 /// a local variable in the current body.
571 public LocalBuilder GetTemporaryLocal (TypeSpec t)
573 if (temporary_storage != null) {
575 if (temporary_storage.TryGetValue (t, out o)) {
576 if (o is Stack<LocalBuilder>) {
577 var s = (Stack<LocalBuilder>) o;
578 o = s.Count == 0 ? null : s.Pop ();
580 temporary_storage.Remove (t);
584 return (LocalBuilder) o;
586 return DeclareLocal (t, false);
589 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
591 if (temporary_storage == null) {
592 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
593 temporary_storage.Add (t, b);
598 if (!temporary_storage.TryGetValue (t, out o)) {
599 temporary_storage.Add (t, b);
602 var s = o as Stack<LocalBuilder>;
604 s = new Stack<LocalBuilder> ();
605 s.Push ((LocalBuilder)o);
606 temporary_storage [t] = s;
612 /// ReturnValue creates on demand the LocalBuilder for the
613 /// return value from the function. By default this is not
614 /// used. This is only required when returns are found inside
615 /// Try or Catch statements.
617 /// This method is typically invoked from the Emit phase, so
618 /// we allow the creation of a return label if it was not
619 /// requested during the resolution phase. Could be cleaned
620 /// up, but it would replicate a lot of logic in the Emit phase
621 /// of the code that uses it.
623 public LocalBuilder TemporaryReturn ()
625 if (return_value == null){
626 return_value = DeclareLocal (return_type, false);
627 if (!HasReturnLabel){
628 ReturnLabel = DefineLabel ();
629 HasReturnLabel = true;