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 TypeSpec[] stack_types;
92 public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type)
94 this.member_context = rc;
96 this.return_type = return_type;
98 if (rc.Module.Compiler.Settings.Checked)
99 flags |= Options.CheckedScope;
102 ig.__CleverExceptionBlockAssistance ();
108 internal AsyncTaskStorey AsyncTaskStorey {
110 return CurrentAnonymousMethod.Storey as AsyncTaskStorey;
114 public BuiltinTypes BuiltinTypes {
116 return MemberContext.Module.Compiler.BuiltinTypes;
120 public TypeSpec CurrentType {
121 get { return member_context.CurrentType; }
124 public TypeParameter[] CurrentTypeParameters {
125 get { return member_context.CurrentTypeParameters; }
128 public MemberCore CurrentTypeDefinition {
129 get { return member_context.CurrentMemberDefinition; }
132 public bool IsStatic {
133 get { return member_context.IsStatic; }
136 public bool IsAnonymousStoreyMutateRequired {
138 return CurrentAnonymousMethod != null &&
139 CurrentAnonymousMethod.Storey != null &&
140 CurrentAnonymousMethod.Storey.Mutator != null;
144 public IMemberContext MemberContext {
146 return member_context;
150 public ModuleContainer Module {
152 return member_context.Module;
156 // Has to be used for specific emitter errors only any
157 // possible resolver errors have to be reported during Resolve
158 public Report Report {
160 return member_context.Module.Compiler.Report;
164 public TypeSpec ReturnType {
170 public int StackHeight {
173 return ig.__StackHeight;
175 throw new NotImplementedException ();
181 // Enabled when tracking stack types during emit phase
183 bool TrackStackTypes {
185 return (flags & Options.AsyncBody) != 0;
192 /// This is called immediately before emitting an IL opcode to tell the symbol
193 /// writer to which source line this opcode belongs.
195 public void Mark (Location loc)
197 if (!SymbolWriter.HasSymbolWriter || HasSet (Options.OmitDebugInfo) || loc.IsNull)
200 SymbolWriter.MarkSequencePoint (ig, loc);
203 public void DefineLocalVariable (string name, LocalBuilder builder)
205 SymbolWriter.DefineLocalVariable (name, builder);
208 public void BeginCatchBlock (TypeSpec type)
210 ig.BeginCatchBlock (type.GetMetaInfo ());
213 public void BeginExceptionBlock ()
215 ig.BeginExceptionBlock ();
218 public void BeginFinallyBlock ()
220 ig.BeginFinallyBlock ();
223 public void BeginScope ()
225 SymbolWriter.OpenScope(ig);
228 public void EndExceptionBlock ()
230 ig.EndExceptionBlock ();
233 public void EndScope ()
235 SymbolWriter.CloseScope(ig);
239 // Creates a nested container in this context for all dynamic compiler generated stuff
241 internal DynamicSiteClass CreateDynamicSite ()
243 if (dynamic_site_container == null) {
244 var mc = member_context.CurrentMemberDefinition as MemberBase;
245 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, CurrentTypeParameters);
247 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
248 dynamic_site_container.CreateType ();
249 dynamic_site_container.DefineType ();
250 dynamic_site_container.ResolveTypeParameters ();
251 dynamic_site_container.Define ();
253 var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
254 var inflated = dynamic_site_container.CurrentType.InflateMember (inflator);
255 CurrentType.MemberCache.AddMember (inflated);
258 return dynamic_site_container;
261 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
263 if (IsAnonymousStoreyMutateRequired)
264 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
266 return ig.DeclareLocal (type.GetMetaInfo (), pinned);
269 public Label DefineLabel ()
271 return ig.DefineLabel ();
275 // Creates temporary field in current async storey
277 public FieldExpr GetTemporaryField (TypeSpec type)
279 var f = AsyncTaskStorey.AddCapturedLocalVariable (type);
280 var fexpr = new FieldExpr (f, Location.Null);
281 fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
285 public void MarkLabel (Label label)
287 ig.MarkLabel (label);
290 public void Emit (OpCode opcode)
294 if (TrackStackTypes) {
295 switch (opcode.StackBehaviourPush) {
296 case StackBehaviour.Push0:
299 case StackBehaviour.Pushi:
300 SetStackType (Module.Compiler.BuiltinTypes.Int);
302 case StackBehaviour.Pushi8:
303 SetStackType (Module.Compiler.BuiltinTypes.Long);
305 case StackBehaviour.Pushr4:
306 SetStackType (Module.Compiler.BuiltinTypes.Float);
308 case StackBehaviour.Pushr8:
309 SetStackType (Module.Compiler.BuiltinTypes.Double);
311 case StackBehaviour.Push1:
312 if (opcode.StackBehaviourPop == StackBehaviour.Pop1) {
314 } else if (opcode.StackBehaviourPop == StackBehaviour.Pop1_pop1) {
317 throw new NotImplementedException (opcode.Name);
320 case StackBehaviour.Push1_push1:
321 if (opcode.StackBehaviourPop == StackBehaviour.Pop1) {
322 SetStackType (stack_types[StackHeight - 2]);
324 throw new NotImplementedException (opcode.Name);
328 throw new NotImplementedException (opcode.Name);
333 public void Emit (OpCode opcode, LocalBuilder local, TypeSpec type)
335 ig.Emit (opcode, local);
337 if (TrackStackTypes) {
338 if (opcode.StackBehaviourPush == StackBehaviour.Push0) {
340 } else if (opcode.StackBehaviourPush == StackBehaviour.Push1) {
342 } else if (opcode.StackBehaviourPush == StackBehaviour.Pushi) {
343 SetStackType (ReferenceContainer.MakeType (Module, type));
345 throw new NotImplementedException (opcode.Name);
350 public void Emit (OpCode opcode, string arg)
352 ig.Emit (opcode, arg);
354 if (TrackStackTypes) {
355 SetStackType (Module.Compiler.BuiltinTypes.String);
359 public void Emit (OpCode opcode, double arg)
361 ig.Emit (opcode, arg);
363 if (TrackStackTypes) {
364 SetStackType (Module.Compiler.BuiltinTypes.Double);
368 public void Emit (OpCode opcode, float arg)
370 ig.Emit (opcode, arg);
372 if (TrackStackTypes) {
373 SetStackType (Module.Compiler.BuiltinTypes.Float);
377 public void Emit (OpCode opcode, Label label)
379 ig.Emit (opcode, label);
382 public void Emit (OpCode opcode, Label[] labels)
384 ig.Emit (opcode, labels);
387 public void Emit (OpCode opcode, TypeSpec type)
389 if (IsAnonymousStoreyMutateRequired)
390 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
392 ig.Emit (opcode, type.GetMetaInfo ());
394 if (TrackStackTypes) {
395 switch (opcode.StackBehaviourPush) {
396 case StackBehaviour.Push0:
399 case StackBehaviour.Pushi:
400 SetStackType (ReferenceContainer.MakeType (Module, type));
402 case StackBehaviour.Push1:
406 if (opcode == OpCodes.Box) {
407 SetStackType (Module.Compiler.BuiltinTypes.Object);
408 } else if (opcode == OpCodes.Castclass) {
411 throw new NotImplementedException (opcode.Name);
418 public void Emit (OpCode opcode, FieldSpec field)
420 if (IsAnonymousStoreyMutateRequired)
421 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
423 ig.Emit (opcode, field.GetMetaInfo ());
425 if (TrackStackTypes) {
426 switch (opcode.StackBehaviourPush) {
427 case StackBehaviour.Push0:
430 case StackBehaviour.Push1:
431 SetStackType (field.MemberType);
433 case StackBehaviour.Pushi:
434 SetStackType (ReferenceContainer.MakeType (Module, field.MemberType));
437 throw new NotImplementedException ();
442 public void Emit (OpCode opcode, MethodSpec method)
444 if (IsAnonymousStoreyMutateRequired)
445 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
447 if (method.IsConstructor)
448 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
450 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
452 if (TrackStackTypes) {
454 // Don't bother with ldftn/Ldvirtftn they can never appear on open stack
456 if (method.IsConstructor) {
457 if (opcode == OpCodes.Newobj)
458 SetStackType (method.DeclaringType);
460 if (method.ReturnType.Kind != MemberKind.Void) {
461 SetStackType (method.ReturnType);
467 // TODO: REMOVE breaks mutator
468 public void Emit (OpCode opcode, MethodInfo method)
470 ig.Emit (opcode, method);
473 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
475 // TODO MemberCache: This should mutate too
476 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
479 public void EmitArrayNew (ArrayContainer ac)
482 var type = IsAnonymousStoreyMutateRequired ?
483 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
486 ig.Emit (OpCodes.Newarr, type.GetMetaInfo ());
488 if (IsAnonymousStoreyMutateRequired)
489 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
491 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
494 if (TrackStackTypes) {
499 public void EmitArrayAddress (ArrayContainer ac)
502 if (IsAnonymousStoreyMutateRequired)
503 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
505 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
507 if (TrackStackTypes) {
508 SetStackType (ReferenceContainer.MakeType (Module, ac.Element));
511 var type = IsAnonymousStoreyMutateRequired ?
512 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
515 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
517 if (TrackStackTypes) {
518 SetStackType (ReferenceContainer.MakeType (Module, type));
524 // Emits the right opcode to load from an array
526 public void EmitArrayLoad (ArrayContainer ac)
529 if (IsAnonymousStoreyMutateRequired)
530 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
532 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
534 if (TrackStackTypes) {
535 SetStackType (ac.Element);
542 var type = ac.Element;
543 if (type.Kind == MemberKind.Enum)
544 type = EnumSpec.GetUnderlyingType (type);
546 switch (type.BuiltinType) {
547 case BuiltinTypeSpec.Type.Byte:
548 case BuiltinTypeSpec.Type.Bool:
549 ig.Emit (OpCodes.Ldelem_U1);
551 case BuiltinTypeSpec.Type.SByte:
552 ig.Emit (OpCodes.Ldelem_I1);
554 case BuiltinTypeSpec.Type.Short:
555 ig.Emit (OpCodes.Ldelem_I2);
557 case BuiltinTypeSpec.Type.UShort:
558 case BuiltinTypeSpec.Type.Char:
559 ig.Emit (OpCodes.Ldelem_U2);
561 case BuiltinTypeSpec.Type.Int:
562 ig.Emit (OpCodes.Ldelem_I4);
564 case BuiltinTypeSpec.Type.UInt:
565 ig.Emit (OpCodes.Ldelem_U4);
567 case BuiltinTypeSpec.Type.ULong:
568 case BuiltinTypeSpec.Type.Long:
569 ig.Emit (OpCodes.Ldelem_I8);
571 case BuiltinTypeSpec.Type.Float:
572 ig.Emit (OpCodes.Ldelem_R4);
574 case BuiltinTypeSpec.Type.Double:
575 ig.Emit (OpCodes.Ldelem_R8);
577 case BuiltinTypeSpec.Type.IntPtr:
578 ig.Emit (OpCodes.Ldelem_I);
582 case MemberKind.Struct:
583 if (IsAnonymousStoreyMutateRequired)
584 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
586 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
587 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
589 case MemberKind.TypeParameter:
590 if (IsAnonymousStoreyMutateRequired)
591 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
593 ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ());
595 case MemberKind.PointerType:
596 ig.Emit (OpCodes.Ldelem_I);
599 ig.Emit (OpCodes.Ldelem_Ref);
605 if (TrackStackTypes) {
611 // Emits the right opcode to store to an array
613 public void EmitArrayStore (ArrayContainer ac)
616 if (IsAnonymousStoreyMutateRequired)
617 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
619 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
623 var type = ac.Element;
625 if (type.Kind == MemberKind.Enum)
626 type = EnumSpec.GetUnderlyingType (type);
628 switch (type.BuiltinType) {
629 case BuiltinTypeSpec.Type.Byte:
630 case BuiltinTypeSpec.Type.SByte:
631 case BuiltinTypeSpec.Type.Bool:
632 Emit (OpCodes.Stelem_I1);
634 case BuiltinTypeSpec.Type.Short:
635 case BuiltinTypeSpec.Type.UShort:
636 case BuiltinTypeSpec.Type.Char:
637 Emit (OpCodes.Stelem_I2);
639 case BuiltinTypeSpec.Type.Int:
640 case BuiltinTypeSpec.Type.UInt:
641 Emit (OpCodes.Stelem_I4);
643 case BuiltinTypeSpec.Type.Long:
644 case BuiltinTypeSpec.Type.ULong:
645 Emit (OpCodes.Stelem_I8);
647 case BuiltinTypeSpec.Type.Float:
648 Emit (OpCodes.Stelem_R4);
650 case BuiltinTypeSpec.Type.Double:
651 Emit (OpCodes.Stelem_R8);
656 case MemberKind.Struct:
657 Emit (OpCodes.Stobj, type);
659 case MemberKind.TypeParameter:
660 Emit (OpCodes.Stelem, type);
662 case MemberKind.PointerType:
663 Emit (OpCodes.Stelem_I);
666 Emit (OpCodes.Stelem_Ref);
671 public void EmitInt (int i)
675 if (TrackStackTypes) {
676 SetStackType (Module.Compiler.BuiltinTypes.Int);
680 void EmitIntConstant (int i)
684 ig.Emit (OpCodes.Ldc_I4_M1);
688 ig.Emit (OpCodes.Ldc_I4_0);
692 ig.Emit (OpCodes.Ldc_I4_1);
696 ig.Emit (OpCodes.Ldc_I4_2);
700 ig.Emit (OpCodes.Ldc_I4_3);
704 ig.Emit (OpCodes.Ldc_I4_4);
708 ig.Emit (OpCodes.Ldc_I4_5);
712 ig.Emit (OpCodes.Ldc_I4_6);
716 ig.Emit (OpCodes.Ldc_I4_7);
720 ig.Emit (OpCodes.Ldc_I4_8);
724 if (i >= -128 && i <= 127) {
725 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
727 ig.Emit (OpCodes.Ldc_I4, i);
732 public void EmitLong (long l)
734 if (l >= int.MinValue && l <= int.MaxValue) {
735 EmitIntConstant (unchecked ((int) l));
736 ig.Emit (OpCodes.Conv_I8);
737 } else if (l >= 0 && l <= uint.MaxValue) {
738 EmitIntConstant (unchecked ((int) l));
739 ig.Emit (OpCodes.Conv_U8);
741 ig.Emit (OpCodes.Ldc_I8, l);
744 if (TrackStackTypes) {
745 SetStackType (Module.Compiler.BuiltinTypes.Long);
750 // Load the object from the pointer.
752 public void EmitLoadFromPtr (TypeSpec type)
754 if (type.Kind == MemberKind.Enum)
755 type = EnumSpec.GetUnderlyingType (type);
757 switch (type.BuiltinType) {
758 case BuiltinTypeSpec.Type.Int:
759 ig.Emit (OpCodes.Ldind_I4);
761 case BuiltinTypeSpec.Type.UInt:
762 ig.Emit (OpCodes.Ldind_U4);
764 case BuiltinTypeSpec.Type.Short:
765 ig.Emit (OpCodes.Ldind_I2);
767 case BuiltinTypeSpec.Type.UShort:
768 case BuiltinTypeSpec.Type.Char:
769 ig.Emit (OpCodes.Ldind_U2);
771 case BuiltinTypeSpec.Type.Byte:
772 ig.Emit (OpCodes.Ldind_U1);
774 case BuiltinTypeSpec.Type.SByte:
775 case BuiltinTypeSpec.Type.Bool:
776 ig.Emit (OpCodes.Ldind_I1);
778 case BuiltinTypeSpec.Type.ULong:
779 case BuiltinTypeSpec.Type.Long:
780 ig.Emit (OpCodes.Ldind_I8);
782 case BuiltinTypeSpec.Type.Float:
783 ig.Emit (OpCodes.Ldind_R4);
785 case BuiltinTypeSpec.Type.Double:
786 ig.Emit (OpCodes.Ldind_R8);
788 case BuiltinTypeSpec.Type.IntPtr:
789 ig.Emit (OpCodes.Ldind_I);
793 case MemberKind.Struct:
794 case MemberKind.TypeParameter:
795 if (IsAnonymousStoreyMutateRequired)
796 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
798 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
800 case MemberKind.PointerType:
801 ig.Emit (OpCodes.Ldind_I);
804 ig.Emit (OpCodes.Ldind_Ref);
810 if (TrackStackTypes) {
811 // TODO: test for async when `this' can be used inside structs
816 public void EmitNull ()
818 ig.Emit (OpCodes.Ldnull);
820 if (TrackStackTypes) {
821 SetStackType (Module.Compiler.BuiltinTypes.Object);
825 public void EmitArgumentAddress (int pos)
830 if (pos > byte.MaxValue)
831 ig.Emit (OpCodes.Ldarga, pos);
833 ig.Emit (OpCodes.Ldarga_S, (byte) pos);
835 if (TrackStackTypes) {
837 // Should never be reached, all parameters are hoisted into class
839 throw new NotImplementedException ();
843 public void EmitArgumentLoad (int pos)
849 case 0: ig.Emit (OpCodes.Ldarg_0); break;
850 case 1: ig.Emit (OpCodes.Ldarg_1); break;
851 case 2: ig.Emit (OpCodes.Ldarg_2); break;
852 case 3: ig.Emit (OpCodes.Ldarg_3); break;
854 if (pos > byte.MaxValue)
855 ig.Emit (OpCodes.Ldarg, pos);
857 ig.Emit (OpCodes.Ldarg_S, (byte) pos);
861 if (TrackStackTypes) {
863 // Should never be reached, all parameters are hoisted into class
865 throw new NotImplementedException ();
869 public void EmitArgumentStore (int pos)
874 if (pos > byte.MaxValue)
875 ig.Emit (OpCodes.Starg, pos);
877 ig.Emit (OpCodes.Starg_S, (byte) pos);
881 // The stack contains the pointer and the value of type `type'
883 public void EmitStoreFromPtr (TypeSpec type)
886 type = EnumSpec.GetUnderlyingType (type);
888 switch (type.BuiltinType) {
889 case BuiltinTypeSpec.Type.Int:
890 case BuiltinTypeSpec.Type.UInt:
891 ig.Emit (OpCodes.Stind_I4);
893 case BuiltinTypeSpec.Type.Long:
894 case BuiltinTypeSpec.Type.ULong:
895 ig.Emit (OpCodes.Stind_I8);
897 case BuiltinTypeSpec.Type.Char:
898 case BuiltinTypeSpec.Type.Short:
899 case BuiltinTypeSpec.Type.UShort:
900 ig.Emit (OpCodes.Stind_I2);
902 case BuiltinTypeSpec.Type.Float:
903 ig.Emit (OpCodes.Stind_R4);
905 case BuiltinTypeSpec.Type.Double:
906 ig.Emit (OpCodes.Stind_R8);
908 case BuiltinTypeSpec.Type.Byte:
909 case BuiltinTypeSpec.Type.SByte:
910 case BuiltinTypeSpec.Type.Bool:
911 ig.Emit (OpCodes.Stind_I1);
913 case BuiltinTypeSpec.Type.IntPtr:
914 ig.Emit (OpCodes.Stind_I);
919 case MemberKind.Struct:
920 case MemberKind.TypeParameter:
921 if (IsAnonymousStoreyMutateRequired)
922 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
924 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
927 ig.Emit (OpCodes.Stind_Ref);
932 public void EmitThis ()
934 ig.Emit (OpCodes.Ldarg_0);
936 if (TrackStackTypes) {
938 // Using CurrentTypeOnStack as a placeholder for CurrentType to allow
939 // optimizations based on `this' presence
941 SetStackType (InternalType.CurrentTypeOnStack);
946 // Returns actual stack types when stack types tracing is enabled
948 public TypeSpec[] GetStackTypes ()
950 TypeSpec[] types = new TypeSpec[StackHeight];
951 Array.Copy (stack_types, types, types.Length);
956 /// Returns a temporary storage for a variable of type t as
957 /// a local variable in the current body.
959 public LocalBuilder GetTemporaryLocal (TypeSpec t)
961 if (temporary_storage != null) {
963 if (temporary_storage.TryGetValue (t, out o)) {
964 if (o is Stack<LocalBuilder>) {
965 var s = (Stack<LocalBuilder>) o;
966 o = s.Count == 0 ? null : s.Pop ();
968 temporary_storage.Remove (t);
972 return (LocalBuilder) o;
974 return DeclareLocal (t, false);
977 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
979 if (temporary_storage == null) {
980 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
981 temporary_storage.Add (t, b);
986 if (!temporary_storage.TryGetValue (t, out o)) {
987 temporary_storage.Add (t, b);
990 var s = o as Stack<LocalBuilder>;
992 s = new Stack<LocalBuilder> ();
993 s.Push ((LocalBuilder)o);
994 temporary_storage [t] = s;
999 void SetStackType (TypeSpec type)
1002 throw new ArgumentNullException ("type");
1004 if (stack_types == null) {
1005 stack_types = new TypeSpec[8];
1006 } else if (StackHeight > stack_types.Length) {
1007 Array.Resize (ref stack_types, stack_types.Length * 2);
1010 stack_types[StackHeight - 1] = type;
1014 /// ReturnValue creates on demand the LocalBuilder for the
1015 /// return value from the function. By default this is not
1016 /// used. This is only required when returns are found inside
1017 /// Try or Catch statements.
1019 /// This method is typically invoked from the Emit phase, so
1020 /// we allow the creation of a return label if it was not
1021 /// requested during the resolution phase. Could be cleaned
1022 /// up, but it would replicate a lot of logic in the Emit phase
1023 /// of the code that uses it.
1025 public LocalBuilder TemporaryReturn ()
1027 if (return_value == null){
1028 return_value = DeclareLocal (return_type, false);
1029 if (!HasReturnLabel){
1030 ReturnLabel = DefineLabel ();
1031 HasReturnLabel = true;
1035 return return_value;
1041 public Expression InstanceExpression;
1044 // When set leaves an extra copy of all arguments on the stack
1046 public bool DuplicateArguments;
1049 // Does not emit InstanceExpression load when InstanceExpressionOnStack
1050 // is set. Used by compound assignments.
1052 public bool InstanceExpressionOnStack;
1055 // Any of arguments contains await expression
1057 public bool HasAwaitArguments;
1060 // When dealing with await arguments the original arguments are converted
1061 // into a new set with hoisted stack results
1063 public Arguments EmittedArguments;
1065 public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1067 // Speed up the check by not doing it on not allowed targets
1068 if (method.ReturnType.Kind == MemberKind.Void && method.IsConditionallyExcluded (ec.Module.Compiler, loc))
1071 EmitPredefined (ec, method, Arguments);
1074 public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments)
1076 Expression instance_copy = null;
1078 if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) {
1079 HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait ();
1080 if (HasAwaitArguments && InstanceExpressionOnStack) {
1081 throw new NotSupportedException ();
1086 LocalTemporary lt = null;
1088 if (method.IsStatic) {
1089 call_op = OpCodes.Call;
1091 if (IsVirtualCallRequired (InstanceExpression, method)) {
1092 call_op = OpCodes.Callvirt;
1094 call_op = OpCodes.Call;
1097 if (HasAwaitArguments) {
1098 instance_copy = InstanceExpression.EmitToField (ec);
1099 if (Arguments == null)
1100 EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
1101 } else if (!InstanceExpressionOnStack) {
1102 var instance_on_stack_type = EmitCallInstance (ec, InstanceExpression, method.DeclaringType, call_op);
1104 if (DuplicateArguments) {
1105 ec.Emit (OpCodes.Dup);
1106 if (Arguments != null && Arguments.Count != 0) {
1107 lt = new LocalTemporary (instance_on_stack_type);
1115 if (Arguments != null && !InstanceExpressionOnStack) {
1116 EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
1117 if (EmittedArguments != null) {
1118 if (instance_copy != null) {
1119 EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
1125 EmittedArguments.Emit (ec);
1129 if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStruct)) {
1130 ec.Emit (OpCodes.Constrained, InstanceExpression.Type);
1134 // Set instance expression to actual result expression. When it contains await it can be
1135 // picked up by caller
1137 InstanceExpression = instance_copy;
1139 if (method.Parameters.HasArglist) {
1140 var varargs_types = GetVarargsTypes (method, Arguments);
1141 ec.Emit (call_op, method, varargs_types);
1148 // and DoFoo is not virtual, you can omit the callvirt,
1149 // because you don't need the null checking behavior.
1151 ec.Emit (call_op, method);
1154 static TypeSpec EmitCallInstance (EmitContext ec, Expression instance, TypeSpec declaringType, OpCode callOpcode)
1156 var instance_type = instance.Type;
1159 // Push the instance expression
1161 if ((instance_type.IsStruct && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType == instance_type))) ||
1162 instance_type.IsGenericParameter || declaringType.IsNullableType) {
1164 // If the expression implements IMemoryLocation, then
1165 // we can optimize and use AddressOf on the
1168 // If not we have to use some temporary storage for
1170 var iml = instance as IMemoryLocation;
1172 iml.AddressOf (ec, AddressOp.Load);
1174 LocalTemporary temp = new LocalTemporary (instance_type);
1177 temp.AddressOf (ec, AddressOp.Load);
1181 return ReferenceContainer.MakeType (ec.Module, instance_type);
1184 if (instance_type.IsEnum || instance_type.IsStruct) {
1186 ec.Emit (OpCodes.Box, instance_type);
1187 return ec.BuiltinTypes.Object;
1191 return instance_type;
1194 static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
1196 AParametersCollection pd = method.Parameters;
1198 Argument a = arguments[pd.Count - 1];
1199 Arglist list = (Arglist) a.Expr;
1201 return list.ArgumentTypes;
1205 // Used to decide whether call or callvirt is needed
1207 static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
1210 // There are 2 scenarious where we emit callvirt
1212 // Case 1: A method is virtual and it's not used to call base
1213 // Case 2: A method instance expression can be null. In this casen callvirt ensures
1214 // correct NRE exception when the method is called
1216 var decl_type = method.DeclaringType;
1217 if (decl_type.IsStruct || decl_type.IsEnum)
1220 if (instance is BaseThis)
1224 // It's non-virtual and will never be null
1226 if (!method.IsVirtual && (instance is This || instance is New || instance is ArrayCreation || instance is DelegateCreation))