1 //
2 // codegen.cs: The code generator
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2004 Novell, Inc.
10 // Copyright 2011 Xamarin Inc
11 //
12 
13 using System;
14 using System.Collections.Generic;
15 using Mono.CompilerServices.SymbolWriter;
16 
17 #if STATIC
18 using MetaType = IKVM.Reflection.Type;
19 using IKVM.Reflection;
20 using IKVM.Reflection.Emit;
21 #else
22 using MetaType = System.Type;
23 using System.Reflection;
24 using System.Reflection.Emit;
25 #endif
26 
27 namespace Mono.CSharp
28 {
29 	/// <summary>
30 	///   An Emit Context is created for each body of code (from methods,
31 	///   properties bodies, indexer bodies or constructor bodies)
32 	/// </summary>
33 	public class EmitContext : BuilderContext
34 	{
35 		// TODO: Has to be private
36 		public readonly ILGenerator ig;
37 
38 		/// <summary>
39 		///   The value that is allowed to be returned or NULL if there is no
40 		///   return type.
41 		/// </summary>
42 		readonly TypeSpec return_type;
43 
44 		/// <summary>
45 		///   Keeps track of the Type to LocalBuilder temporary storage created
46 		///   to store structures (used to compute the address of the structure
47 		///   value on structure method invocations)
48 		/// </summary>
49 		Dictionary<TypeSpec, object> temporary_storage;
50 
51 		/// <summary>
52 		///   The location where we store the return value.
53 		/// </summary>
54 		public LocalBuilder return_value;
55 
56 
57 		/// <summary>
58 		///   Current loop begin and end labels.
59 		/// </summary>
60 		public Label LoopBegin, LoopEnd;
61 
62 		/// <summary>
63 		///   Default target in a switch statement.   Only valid if
64 		///   InSwitch is true
65 		/// </summary>
66 		public Label DefaultTarget;
67 
68 		/// <summary>
69 		///   If this is non-null, points to the current switch statement
70 		/// </summary>
71 		public Switch Switch;
72 
73 		/// <summary>
74 		///  Whether we are inside an anonymous method.
75 		/// </summary>
76 		public AnonymousExpression CurrentAnonymousMethod;
77 
78 		readonly IMemberContext member_context;
79 
80 		readonly SourceMethodBuilder methodSymbols;
81 
82 		DynamicSiteClass dynamic_site_container;
83 
84 		Label? return_label;
85 
86 		List<IExpressionCleanup> epilogue_expressions;
87 
EmitContext(IMemberContext rc, ILGenerator ig, TypeSpec return_type, SourceMethodBuilder methodSymbols)88 		public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type, SourceMethodBuilder methodSymbols)
89 		{
90 			this.member_context = rc;
91 			this.ig = ig;
92 			this.return_type = return_type;
93 
94 			if (rc.Module.Compiler.Settings.Checked)
95 				flags |= Options.CheckedScope;
96 
97 			if (methodSymbols != null) {
98 				this.methodSymbols = methodSymbols;
99 				if (!rc.Module.Compiler.Settings.Optimize)
100 					flags |= Options.AccurateDebugInfo;
101 			} else {
102 				flags |= Options.OmitDebugInfo;
103 			}
104 
105 #if STATIC
106 			ig.__CleverExceptionBlockAssistance ();
107 #endif
108 		}
109 
110 		#region Properties
111 
112 		internal AsyncTaskStorey AsyncTaskStorey {
113 			get {
114 				return CurrentAnonymousMethod.Storey as AsyncTaskStorey;
115 			}
116 		}
117 
118 		public BuiltinTypes BuiltinTypes {
119 			get {
120 				return MemberContext.Module.Compiler.BuiltinTypes;
121 			}
122 		}
123 
124 		public ConditionalAccessContext ConditionalAccess { get; set; }
125 
126 		public TypeSpec CurrentType {
127 			get { return member_context.CurrentType; }
128 		}
129 
130 		public TypeParameters CurrentTypeParameters {
131 		    get { return member_context.CurrentTypeParameters; }
132 		}
133 
134 		public MemberCore CurrentTypeDefinition {
135 			get { return member_context.CurrentMemberDefinition; }
136 		}
137 
138 		public bool EmitAccurateDebugInfo {
139 			get {
140 				return (flags & Options.AccurateDebugInfo) != 0;
141 			}
142 		}
143 
144 		public bool HasMethodSymbolBuilder {
145 			get {
146 				return methodSymbols != null;
147 			}
148 		}
149 
150 		public bool HasReturnLabel {
151 			get {
152 				return return_label.HasValue;
153 			}
154 		}
155 
156 		public bool IsStatic {
157 			get { return member_context.IsStatic; }
158 		}
159 
160 		public bool IsStaticConstructor {
161 			get {
162 				return member_context.IsStatic && (flags & Options.ConstructorScope) != 0;
163 			}
164 		}
165 
166 		public bool IsAnonymousStoreyMutateRequired {
167 			get {
168 				return CurrentAnonymousMethod != null &&
169 					CurrentAnonymousMethod.Storey != null &&
170 					CurrentAnonymousMethod.Storey.Mutator != null;
171 			}
172 		}
173 
174 		public IMemberContext MemberContext {
175 			get {
176 				return member_context;
177 			}
178 		}
179 
180 		public ModuleContainer Module {
181 			get {
182 				return member_context.Module;
183 			}
184 		}
185 
186 		public bool NotifyEvaluatorOnStore {
187 			get {
188 				return Module.Evaluator != null && Module.Evaluator.ModificationListener != null;
189 			}
190 		}
191 
192 		// Has to be used for specific emitter errors only any
193 		// possible resolver errors have to be reported during Resolve
194 		public Report Report {
195 			get {
196 				return member_context.Module.Compiler.Report;
197 			}
198 		}
199 
200 		public TypeSpec ReturnType {
201 			get {
202 				return return_type;
203 			}
204 		}
205 
206 		//
207 		// The label where we have to jump before leaving the context
208 		//
209 		public Label ReturnLabel {
210 			get {
211 				return return_label.Value;
212 			}
213 		}
214 
215 		public List<IExpressionCleanup> StatementEpilogue {
216 			get {
217 				return epilogue_expressions;
218 			}
219 		}
220 
221 		public LocalVariable AsyncThrowVariable { get; set; }
222 
223 		public List<TryFinally> TryFinallyUnwind { get; set; }
224 
225 		public Label RecursivePatternLabel { get; set; }
226 
227 		#endregion
228 
AddStatementEpilog(IExpressionCleanup cleanupExpression)229 		public void AddStatementEpilog (IExpressionCleanup cleanupExpression)
230 		{
231 			if (epilogue_expressions == null) {
232 				epilogue_expressions = new List<IExpressionCleanup> ();
233 			} else if (epilogue_expressions.Contains (cleanupExpression)) {
234 				return;
235 			}
236 
237 			epilogue_expressions.Add (cleanupExpression);
238 		}
239 
AssertEmptyStack()240 		public void AssertEmptyStack ()
241 		{
242 #if STATIC
243 			if (ig.__StackHeight != 0)
244 				throw new InternalErrorException ("Await yields with non-empty stack in `{0}",
245 					member_context.GetSignatureForError ());
246 #endif
247 		}
248 
249 		/// <summary>
250 		///   This is called immediately before emitting an IL opcode to tell the symbol
251 		///   writer to which source line this opcode belongs.
252 		/// </summary>
Mark(Location loc)253 		public bool Mark (Location loc)
254 		{
255 			if ((flags & Options.OmitDebugInfo) != 0)
256 				return false;
257 
258 			if (loc.IsNull || methodSymbols == null)
259 				return false;
260 
261 			var sf = loc.SourceFile;
262 			if (sf.IsHiddenLocation (loc))
263 				return false;
264 
265 			methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false);
266 			return true;
267 		}
268 
MarkCallEntry(Location loc)269 		public void MarkCallEntry (Location loc)
270 		{
271 			if (!EmitAccurateDebugInfo)
272 				return;
273 
274 			//
275 			// TODO: This should emit different kind of sequence point to make
276 			// step-over work for statement over multiple lines
277 			//
278 			// Debugging experience for Foo (A () + B ()) where A and B are
279 			// on separate lines is not great
280 			//
281 			Mark (loc);
282 		}
283 
DefineLocalVariable(string name, LocalBuilder builder)284 		public void DefineLocalVariable (string name, LocalBuilder builder)
285 		{
286 			if ((flags & Options.OmitDebugInfo) != 0)
287 				return;
288 
289 			methodSymbols.AddLocal (builder.LocalIndex, name);
290 		}
291 
BeginCatchBlock(TypeSpec type)292 		public void BeginCatchBlock (TypeSpec type)
293 		{
294 			if (IsAnonymousStoreyMutateRequired)
295 				type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
296 
297 			ig.BeginCatchBlock (type.GetMetaInfo ());
298 		}
299 
BeginFilterHandler()300 		public void BeginFilterHandler ()
301 		{
302 			ig.BeginCatchBlock (null);
303 		}
304 
BeginExceptionBlock()305 		public void BeginExceptionBlock ()
306 		{
307 			ig.BeginExceptionBlock ();
308 		}
309 
BeginExceptionFilterBlock()310 		public void BeginExceptionFilterBlock ()
311 		{
312 			ig.BeginExceptFilterBlock ();
313 		}
314 
BeginFinallyBlock()315 		public void BeginFinallyBlock ()
316 		{
317 			ig.BeginFinallyBlock ();
318 		}
319 
BeginScope(int scopeIndex)320 		public void BeginScope (int scopeIndex)
321 		{
322 			if ((flags & Options.OmitDebugInfo) != 0)
323 				return;
324 
325 			methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset, scopeIndex);
326 		}
327 
BeginCompilerScope(int scopeIndex)328 		public void BeginCompilerScope (int scopeIndex)
329 		{
330 			if ((flags & Options.OmitDebugInfo) != 0)
331 				return;
332 
333 			methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset, scopeIndex);
334 		}
335 
EndExceptionBlock()336 		public void EndExceptionBlock ()
337 		{
338 			ig.EndExceptionBlock ();
339 		}
340 
EndScope()341 		public void EndScope ()
342 		{
343 			if ((flags & Options.OmitDebugInfo) != 0)
344 				return;
345 
346 			methodSymbols.EndBlock (ig.ILOffset);
347 		}
348 
CloseConditionalAccess(TypeSpec type)349 		public void CloseConditionalAccess (TypeSpec type)
350 		{
351 			if (type != null)
352 				Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type));
353 
354 			MarkLabel (ConditionalAccess.EndLabel);
355 			ConditionalAccess = null;
356 		}
357 
358 		//
359 		// Creates a nested container in this context for all dynamic compiler generated stuff
360 		//
CreateDynamicSite()361 		internal DynamicSiteClass CreateDynamicSite ()
362 		{
363 			if (dynamic_site_container == null) {
364 				var mc = member_context.CurrentMemberDefinition as MemberBase;
365 				dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, member_context.CurrentTypeParameters);
366 
367 				CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
368 				dynamic_site_container.CreateContainer ();
369 				dynamic_site_container.DefineContainer ();
370 				dynamic_site_container.Define ();
371 
372 				var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
373 				var inflated = dynamic_site_container.CurrentType.InflateMember (inflator);
374 				CurrentType.MemberCache.AddMember (inflated);
375 			}
376 
377 			return dynamic_site_container;
378 		}
379 
CreateReturnLabel()380 		public Label CreateReturnLabel ()
381 		{
382 			if (!return_label.HasValue)
383 				return_label = DefineLabel ();
384 
385 			return return_label.Value;
386 		}
387 
DeclareLocal(TypeSpec type, bool pinned)388 		public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
389 		{
390 			if (IsAnonymousStoreyMutateRequired)
391 				type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
392 
393 			if (pinned) {
394 				//
395 				// This is for .net compatibility. I am not sure why pinned
396 				// pointer temps are converted to & even if they are pointers to
397 				// pointers.
398 				//
399 				var pt = type as PointerContainer;
400 				if (pt != null) {
401 					type = pt.Element;
402 					if (type.Kind == MemberKind.Void)
403 						type = Module.Compiler.BuiltinTypes.IntPtr;
404 
405 					return ig.DeclareLocal (type.GetMetaInfo ().MakeByRefType (), true);
406 				}
407 			}
408 
409 			return ig.DeclareLocal (type.GetMetaInfo (), pinned);
410 		}
411 
DefineLabel()412 		public Label DefineLabel ()
413 		{
414 			return ig.DefineLabel ();
415 		}
416 
417 		//
418 		// Creates temporary field in current async storey
419 		//
GetTemporaryField(TypeSpec type, bool initializedFieldRequired = false)420 		public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false)
421 		{
422 			var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired);
423 			var fexpr = new StackFieldExpr (f);
424 			fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
425 			return fexpr;
426 		}
427 
MarkLabel(Label label)428 		public void MarkLabel (Label label)
429 		{
430 			ig.MarkLabel (label);
431 		}
432 
Emit(OpCode opcode)433 		public void Emit (OpCode opcode)
434 		{
435 			ig.Emit (opcode);
436 		}
437 
Emit(OpCode opcode, LocalBuilder local)438 		public void Emit (OpCode opcode, LocalBuilder local)
439 		{
440 			ig.Emit (opcode, local);
441 		}
442 
Emit(OpCode opcode, string arg)443 		public void Emit (OpCode opcode, string arg)
444 		{
445 			ig.Emit (opcode, arg);
446 		}
447 
Emit(OpCode opcode, double arg)448 		public void Emit (OpCode opcode, double arg)
449 		{
450 			ig.Emit (opcode, arg);
451 		}
452 
Emit(OpCode opcode, float arg)453 		public void Emit (OpCode opcode, float arg)
454 		{
455 			ig.Emit (opcode, arg);
456 		}
457 
Emit(OpCode opcode, Label label)458 		public void Emit (OpCode opcode, Label label)
459 		{
460 			ig.Emit (opcode, label);
461 		}
462 
Emit(OpCode opcode, Label[] labels)463 		public void Emit (OpCode opcode, Label[] labels)
464 		{
465 			ig.Emit (opcode, labels);
466 		}
467 
Emit(OpCode opcode, TypeSpec type)468 		public void Emit (OpCode opcode, TypeSpec type)
469 		{
470 			if (IsAnonymousStoreyMutateRequired)
471 				type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
472 
473 			ig.Emit (opcode, type.GetMetaInfo ());
474 		}
475 
Emit(OpCode opcode, FieldSpec field)476 		public void Emit (OpCode opcode, FieldSpec field)
477 		{
478 			if (IsAnonymousStoreyMutateRequired)
479 				field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
480 
481 			ig.Emit (opcode, field.GetMetaInfo ());
482 		}
483 
Emit(OpCode opcode, MethodSpec method)484 		public void Emit (OpCode opcode, MethodSpec method)
485 		{
486 			if (IsAnonymousStoreyMutateRequired)
487 				method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
488 
489 			if (method.IsConstructor)
490 				ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
491 			else
492 				ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
493 		}
494 
495 		// TODO: REMOVE breaks mutator
Emit(OpCode opcode, MethodInfo method)496 		public void Emit (OpCode opcode, MethodInfo method)
497 		{
498 			ig.Emit (opcode, method);
499 		}
500 
Emit(OpCode opcode, MethodSpec method, MetaType[] vargs)501 		public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
502 		{
503 			// TODO MemberCache: This should mutate too
504 			ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
505 		}
506 
EmitArrayNew(ArrayContainer ac)507 		public void EmitArrayNew (ArrayContainer ac)
508 		{
509 			if (ac.Rank == 1) {
510 				var type = IsAnonymousStoreyMutateRequired ?
511 					CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
512 					ac.Element;
513 
514 				ig.Emit (OpCodes.Newarr, type.GetMetaInfo ());
515 			} else {
516 				if (IsAnonymousStoreyMutateRequired)
517 					ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
518 
519 				ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
520 			}
521 		}
522 
EmitArrayAddress(ArrayContainer ac)523 		public void EmitArrayAddress (ArrayContainer ac)
524 		{
525 			if (ac.Rank > 1) {
526 				if (IsAnonymousStoreyMutateRequired)
527 					ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
528 
529 				ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
530 			} else {
531 				var type = IsAnonymousStoreyMutateRequired ?
532 					CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
533 					ac.Element;
534 
535 				ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
536 			}
537 		}
538 
539 		//
540 		// Emits the right opcode to load from an array
541 		//
EmitArrayLoad(ArrayContainer ac)542 		public void EmitArrayLoad (ArrayContainer ac)
543 		{
544 			if (ac.Rank > 1) {
545 				if (IsAnonymousStoreyMutateRequired)
546 					ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
547 
548 				ig.Emit (OpCodes.Call, ac.GetGetMethod ());
549 				return;
550 			}
551 
552 
553 			var type = ac.Element;
554 			if (type.Kind == MemberKind.Enum)
555 				type = EnumSpec.GetUnderlyingType (type);
556 
557 			switch (type.BuiltinType) {
558 			case BuiltinTypeSpec.Type.Bool:
559 				//
560 				// bool array can actually store any byte value in underlying byte slot
561 				// and C# spec does not specify any normalization rule, except the result
562 				// is undefined
563 				//
564 			case BuiltinTypeSpec.Type.Byte:
565 				ig.Emit (OpCodes.Ldelem_U1);
566 				break;
567 			case BuiltinTypeSpec.Type.SByte:
568 				ig.Emit (OpCodes.Ldelem_I1);
569 				break;
570 			case BuiltinTypeSpec.Type.Short:
571 				ig.Emit (OpCodes.Ldelem_I2);
572 				break;
573 			case BuiltinTypeSpec.Type.UShort:
574 			case BuiltinTypeSpec.Type.Char:
575 				ig.Emit (OpCodes.Ldelem_U2);
576 				break;
577 			case BuiltinTypeSpec.Type.Int:
578 				ig.Emit (OpCodes.Ldelem_I4);
579 				break;
580 			case BuiltinTypeSpec.Type.UInt:
581 				ig.Emit (OpCodes.Ldelem_U4);
582 				break;
583 			case BuiltinTypeSpec.Type.ULong:
584 			case BuiltinTypeSpec.Type.Long:
585 				ig.Emit (OpCodes.Ldelem_I8);
586 				break;
587 			case BuiltinTypeSpec.Type.Float:
588 				ig.Emit (OpCodes.Ldelem_R4);
589 				break;
590 			case BuiltinTypeSpec.Type.Double:
591 				ig.Emit (OpCodes.Ldelem_R8);
592 				break;
593 			case BuiltinTypeSpec.Type.IntPtr:
594 				ig.Emit (OpCodes.Ldelem_I);
595 				break;
596 			default:
597 				switch (type.Kind) {
598 				case MemberKind.Struct:
599 					if (IsAnonymousStoreyMutateRequired)
600 						type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
601 
602 					ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
603 					ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
604 					break;
605 				case MemberKind.TypeParameter:
606 					if (IsAnonymousStoreyMutateRequired)
607 						type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
608 
609 					ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ());
610 					break;
611 				case MemberKind.PointerType:
612 					ig.Emit (OpCodes.Ldelem_I);
613 					break;
614 				default:
615 					ig.Emit (OpCodes.Ldelem_Ref);
616 					break;
617 				}
618 				break;
619 			}
620 		}
621 
622 		//
623 		// Emits the right opcode to store to an array
624 		//
EmitArrayStore(ArrayContainer ac)625 		public void EmitArrayStore (ArrayContainer ac)
626 		{
627 			if (ac.Rank > 1) {
628 				if (IsAnonymousStoreyMutateRequired)
629 					ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
630 
631 				ig.Emit (OpCodes.Call, ac.GetSetMethod ());
632 				return;
633 			}
634 
635 			var type = ac.Element;
636 
637 			if (type.Kind == MemberKind.Enum)
638 				type = EnumSpec.GetUnderlyingType (type);
639 
640 			switch (type.BuiltinType) {
641 			case BuiltinTypeSpec.Type.Byte:
642 			case BuiltinTypeSpec.Type.SByte:
643 			case BuiltinTypeSpec.Type.Bool:
644 				Emit (OpCodes.Stelem_I1);
645 				return;
646 			case BuiltinTypeSpec.Type.Short:
647 			case BuiltinTypeSpec.Type.UShort:
648 			case BuiltinTypeSpec.Type.Char:
649 				Emit (OpCodes.Stelem_I2);
650 				return;
651 			case BuiltinTypeSpec.Type.Int:
652 			case BuiltinTypeSpec.Type.UInt:
653 				Emit (OpCodes.Stelem_I4);
654 				return;
655 			case BuiltinTypeSpec.Type.Long:
656 			case BuiltinTypeSpec.Type.ULong:
657 				Emit (OpCodes.Stelem_I8);
658 				return;
659 			case BuiltinTypeSpec.Type.Float:
660 				Emit (OpCodes.Stelem_R4);
661 				return;
662 			case BuiltinTypeSpec.Type.Double:
663 				Emit (OpCodes.Stelem_R8);
664 				return;
665 			}
666 
667 			switch (type.Kind) {
668 			case MemberKind.Struct:
669 				Emit (OpCodes.Stobj, type);
670 				break;
671 			case MemberKind.TypeParameter:
672 				Emit (OpCodes.Stelem, type);
673 				break;
674 			case MemberKind.PointerType:
675 				Emit (OpCodes.Stelem_I);
676 				break;
677 			default:
678 				Emit (OpCodes.Stelem_Ref);
679 				break;
680 			}
681 		}
682 
EmitInt(int i)683 		public void EmitInt (int i)
684 		{
685 			EmitIntConstant (i);
686 		}
687 
EmitIntConstant(int i)688 		void EmitIntConstant (int i)
689 		{
690 			switch (i) {
691 			case -1:
692 				ig.Emit (OpCodes.Ldc_I4_M1);
693 				break;
694 
695 			case 0:
696 				ig.Emit (OpCodes.Ldc_I4_0);
697 				break;
698 
699 			case 1:
700 				ig.Emit (OpCodes.Ldc_I4_1);
701 				break;
702 
703 			case 2:
704 				ig.Emit (OpCodes.Ldc_I4_2);
705 				break;
706 
707 			case 3:
708 				ig.Emit (OpCodes.Ldc_I4_3);
709 				break;
710 
711 			case 4:
712 				ig.Emit (OpCodes.Ldc_I4_4);
713 				break;
714 
715 			case 5:
716 				ig.Emit (OpCodes.Ldc_I4_5);
717 				break;
718 
719 			case 6:
720 				ig.Emit (OpCodes.Ldc_I4_6);
721 				break;
722 
723 			case 7:
724 				ig.Emit (OpCodes.Ldc_I4_7);
725 				break;
726 
727 			case 8:
728 				ig.Emit (OpCodes.Ldc_I4_8);
729 				break;
730 
731 			default:
732 				if (i >= -128 && i <= 127) {
733 					ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
734 				} else
735 					ig.Emit (OpCodes.Ldc_I4, i);
736 				break;
737 			}
738 		}
739 
EmitLong(long l)740 		public void EmitLong (long l)
741 		{
742 			if (l >= int.MinValue && l <= int.MaxValue) {
743 				EmitIntConstant (unchecked ((int) l));
744 				ig.Emit (OpCodes.Conv_I8);
745 			} else if (l >= 0 && l <= uint.MaxValue) {
746 				EmitIntConstant (unchecked ((int) l));
747 				ig.Emit (OpCodes.Conv_U8);
748 			} else {
749 				ig.Emit (OpCodes.Ldc_I8, l);
750 			}
751 		}
752 
753 		//
754 		// Load the object from the pointer.
755 		//
EmitLoadFromPtr(TypeSpec type)756 		public void EmitLoadFromPtr (TypeSpec type)
757 		{
758 			if (type.Kind == MemberKind.Enum)
759 				type = EnumSpec.GetUnderlyingType (type);
760 
761 			switch (type.BuiltinType) {
762 			case BuiltinTypeSpec.Type.Int:
763 				ig.Emit (OpCodes.Ldind_I4);
764 				break;
765 			case BuiltinTypeSpec.Type.UInt:
766 				ig.Emit (OpCodes.Ldind_U4);
767 				break;
768 			case BuiltinTypeSpec.Type.Short:
769 				ig.Emit (OpCodes.Ldind_I2);
770 				break;
771 			case BuiltinTypeSpec.Type.UShort:
772 			case BuiltinTypeSpec.Type.Char:
773 				ig.Emit (OpCodes.Ldind_U2);
774 				break;
775 			case BuiltinTypeSpec.Type.Byte:
776 				ig.Emit (OpCodes.Ldind_U1);
777 				break;
778 			case BuiltinTypeSpec.Type.SByte:
779 			case BuiltinTypeSpec.Type.Bool:
780 				ig.Emit (OpCodes.Ldind_I1);
781 				break;
782 			case BuiltinTypeSpec.Type.ULong:
783 			case BuiltinTypeSpec.Type.Long:
784 				ig.Emit (OpCodes.Ldind_I8);
785 				break;
786 			case BuiltinTypeSpec.Type.Float:
787 				ig.Emit (OpCodes.Ldind_R4);
788 				break;
789 			case BuiltinTypeSpec.Type.Double:
790 				ig.Emit (OpCodes.Ldind_R8);
791 				break;
792 			case BuiltinTypeSpec.Type.IntPtr:
793 				ig.Emit (OpCodes.Ldind_I);
794 				break;
795 			default:
796 				switch (type.Kind) {
797 				case MemberKind.Struct:
798 				case MemberKind.TypeParameter:
799 					if (IsAnonymousStoreyMutateRequired)
800 						type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
801 
802 					ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
803 					break;
804 				case MemberKind.PointerType:
805 					ig.Emit (OpCodes.Ldind_I);
806 					break;
807 				default:
808 					ig.Emit (OpCodes.Ldind_Ref);
809 					break;
810 				}
811 				break;
812 			}
813 		}
814 
EmitNull()815 		public void EmitNull ()
816 		{
817 			ig.Emit (OpCodes.Ldnull);
818 		}
819 
EmitArgumentAddress(int pos)820 		public void EmitArgumentAddress (int pos)
821 		{
822 			if (!IsStatic)
823 				++pos;
824 
825 			if (pos > byte.MaxValue)
826 				ig.Emit (OpCodes.Ldarga, pos);
827 			else
828 				ig.Emit (OpCodes.Ldarga_S, (byte) pos);
829 		}
830 
EmitArgumentLoad(int pos)831 		public void EmitArgumentLoad (int pos)
832 		{
833 			if (!IsStatic)
834 				++pos;
835 
836 			switch (pos) {
837 			case 0: ig.Emit (OpCodes.Ldarg_0); break;
838 			case 1: ig.Emit (OpCodes.Ldarg_1); break;
839 			case 2: ig.Emit (OpCodes.Ldarg_2); break;
840 			case 3: ig.Emit (OpCodes.Ldarg_3); break;
841 			default:
842 				if (pos > byte.MaxValue)
843 					ig.Emit (OpCodes.Ldarg, pos);
844 				else
845 					ig.Emit (OpCodes.Ldarg_S, (byte) pos);
846 				break;
847 			}
848 		}
849 
EmitArgumentStore(int pos)850 		public void EmitArgumentStore (int pos)
851 		{
852 			if (!IsStatic)
853 				++pos;
854 
855 			if (pos > byte.MaxValue)
856 				ig.Emit (OpCodes.Starg, pos);
857 			else
858 				ig.Emit (OpCodes.Starg_S, (byte) pos);
859 		}
860 
861 		//
862 		// The stack contains the pointer and the value of type `type'
863 		//
EmitStoreFromPtr(TypeSpec type)864 		public void EmitStoreFromPtr (TypeSpec type)
865 		{
866 			if (type.IsEnum)
867 				type = EnumSpec.GetUnderlyingType (type);
868 
869 			switch (type.BuiltinType) {
870 			case BuiltinTypeSpec.Type.Int:
871 			case BuiltinTypeSpec.Type.UInt:
872 				ig.Emit (OpCodes.Stind_I4);
873 				return;
874 			case BuiltinTypeSpec.Type.Long:
875 			case BuiltinTypeSpec.Type.ULong:
876 				ig.Emit (OpCodes.Stind_I8);
877 				return;
878 			case BuiltinTypeSpec.Type.Char:
879 			case BuiltinTypeSpec.Type.Short:
880 			case BuiltinTypeSpec.Type.UShort:
881 				ig.Emit (OpCodes.Stind_I2);
882 				return;
883 			case BuiltinTypeSpec.Type.Float:
884 				ig.Emit (OpCodes.Stind_R4);
885 				return;
886 			case BuiltinTypeSpec.Type.Double:
887 				ig.Emit (OpCodes.Stind_R8);
888 				return;
889 			case BuiltinTypeSpec.Type.Byte:
890 			case BuiltinTypeSpec.Type.SByte:
891 			case BuiltinTypeSpec.Type.Bool:
892 				ig.Emit (OpCodes.Stind_I1);
893 				return;
894 			case BuiltinTypeSpec.Type.IntPtr:
895 				ig.Emit (OpCodes.Stind_I);
896 				return;
897 			}
898 
899 			switch (type.Kind) {
900 			case MemberKind.Struct:
901 			case MemberKind.TypeParameter:
902 				if (IsAnonymousStoreyMutateRequired)
903 					type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
904 
905 				ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
906 				break;
907 			case MemberKind.PointerType:
908 				ig.Emit (OpCodes.Stind_I);
909 				break;
910 			default:
911 				ig.Emit (OpCodes.Stind_Ref);
912 				break;
913 			}
914 		}
915 
EmitThis()916 		public void EmitThis ()
917 		{
918 			ig.Emit (OpCodes.Ldarg_0);
919 		}
920 
EmitEpilogue()921 		public void EmitEpilogue ()
922 		{
923 			if (epilogue_expressions == null)
924 				return;
925 
926 			foreach (var e in epilogue_expressions)
927 				e.EmitCleanup (this);
928 
929 			epilogue_expressions = null;
930 		}
931 
932 		/// <summary>
933 		///   Returns a temporary storage for a variable of type t as
934 		///   a local variable in the current body.
935 		/// </summary>
GetTemporaryLocal(TypeSpec t)936 		public LocalBuilder GetTemporaryLocal (TypeSpec t)
937 		{
938 			if (temporary_storage != null) {
939 				object o;
940 				if (temporary_storage.TryGetValue (t, out o)) {
941 					if (o is Stack<LocalBuilder>) {
942 						var s = (Stack<LocalBuilder>) o;
943 						o = s.Count == 0 ? null : s.Pop ();
944 					} else {
945 						temporary_storage.Remove (t);
946 					}
947 				}
948 				if (o != null)
949 					return (LocalBuilder) o;
950 			}
951 			return DeclareLocal (t, false);
952 		}
953 
FreeTemporaryLocal(LocalBuilder b, TypeSpec t)954 		public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
955 		{
956 			if (temporary_storage == null) {
957 				temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
958 				temporary_storage.Add (t, b);
959 				return;
960 			}
961 			object o;
962 
963 			if (!temporary_storage.TryGetValue (t, out o)) {
964 				temporary_storage.Add (t, b);
965 				return;
966 			}
967 			var s = o as Stack<LocalBuilder>;
968 			if (s == null) {
969 				s = new Stack<LocalBuilder> ();
970 				s.Push ((LocalBuilder)o);
971 				temporary_storage [t] = s;
972 			}
973 			s.Push (b);
974 		}
975 
976 		/// <summary>
977 		///   ReturnValue creates on demand the LocalBuilder for the
978 		///   return value from the function.  By default this is not
979 		///   used.  This is only required when returns are found inside
980 		///   Try or Catch statements.
981 		///
982 		///   This method is typically invoked from the Emit phase, so
983 		///   we allow the creation of a return label if it was not
984 		///   requested during the resolution phase.   Could be cleaned
985 		///   up, but it would replicate a lot of logic in the Emit phase
986 		///   of the code that uses it.
987 		/// </summary>
TemporaryReturn()988 		public LocalBuilder TemporaryReturn ()
989 		{
990 			if (return_value == null){
991 				return_value = DeclareLocal (return_type, false);
992 			}
993 
994 			return return_value;
995 		}
996 	}
997 
998 	public class ConditionalAccessContext
999 	{
ConditionalAccessContext(TypeSpec type, Label endLabel)1000 		public ConditionalAccessContext (TypeSpec type, Label endLabel)
1001 		{
1002 			Type = type;
1003 			EndLabel = endLabel;
1004 		}
1005 
1006 		public bool Statement { get; set; }
1007 		public Label EndLabel { get; private set; }
1008 		public TypeSpec Type { get; private set; }
1009 	}
1010 
1011 	struct CallEmitter
1012 	{
1013 		public Expression InstanceExpression;
1014 
1015 		//
1016 		// When call has to leave an extra copy of all arguments on the stack
1017 		//
1018 		public bool DuplicateArguments;
1019 
1020 		//
1021 		// Does not emit InstanceExpression load when InstanceExpressionOnStack
1022 		// is set. Used by compound assignments.
1023 		//
1024 		public bool InstanceExpressionOnStack;
1025 
1026 		//
1027 		// Any of arguments contains await expression
1028 		//
1029 		public bool HasAwaitArguments;
1030 
1031 		public bool ConditionalAccess;
1032 
1033 		//
1034 		// When dealing with await arguments the original arguments are converted
1035 		// into a new set with hoisted stack results
1036 		//
1037 		public Arguments EmittedArguments;
1038 
EmitMono.CSharp.CallEmitter1039 		public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1040 		{
1041 			EmitPredefined (ec, method, Arguments, false, loc);
1042 		}
1043 
EmitStatementMono.CSharp.CallEmitter1044 		public void EmitStatement (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1045 		{
1046 			EmitPredefined (ec, method, Arguments, true, loc);
1047 		}
1048 
EmitPredefinedMono.CSharp.CallEmitter1049 		public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, bool statement = false, Location? loc = null)
1050 		{
1051 			Expression instance_copy = null;
1052 
1053 			if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) {
1054 				HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait ();
1055 				if (HasAwaitArguments && InstanceExpressionOnStack) {
1056 					throw new NotSupportedException ();
1057 				}
1058 			}
1059 
1060 			OpCode call_op;
1061 			LocalTemporary lt = null;
1062 
1063 			if (method.IsStatic) {
1064 				call_op = OpCodes.Call;
1065 			} else {
1066 				call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call;
1067 
1068 				if (HasAwaitArguments) {
1069 					instance_copy = InstanceExpression.EmitToField (ec);
1070 					var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
1071 
1072 					if (Arguments == null) {
1073 						if (ConditionalAccess)
1074 							ie.Emit (ec, true);
1075 						else
1076 							ie.EmitLoad (ec, true);
1077 					}
1078 				} else if (!InstanceExpressionOnStack) {
1079 					var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
1080 					ie.Emit (ec, ConditionalAccess);
1081 
1082 					if (DuplicateArguments) {
1083 						ec.Emit (OpCodes.Dup);
1084 						if (Arguments != null && Arguments.Count != 0) {
1085 							lt = new LocalTemporary (ie.GetStackType (ec));
1086 							lt.Store (ec);
1087 							instance_copy = lt;
1088 						}
1089 					}
1090 				}
1091 			}
1092 
1093 			if (Arguments != null && !InstanceExpressionOnStack) {
1094 				EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
1095 				if (EmittedArguments != null) {
1096 					if (instance_copy != null) {
1097 						var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
1098 						ie.Emit (ec, ConditionalAccess);
1099 
1100 						if (lt != null)
1101 							lt.Release (ec);
1102 					}
1103 
1104 					EmittedArguments.Emit (ec);
1105 				}
1106 			}
1107 
1108 			if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStructOrEnum)) {
1109 				ec.Emit (OpCodes.Constrained, InstanceExpression.Type);
1110 			}
1111 
1112 			if (loc != null) {
1113 				//
1114 				// Emit explicit sequence point for expressions like Foo.Bar () to help debugger to
1115 				// break at right place when LHS expression can be stepped-into
1116 				//
1117 				ec.MarkCallEntry (loc.Value);
1118 			}
1119 
1120 			//
1121 			// Set instance expression to actual result expression. When it contains await it can be
1122 			// picked up by caller
1123 			//
1124 			InstanceExpression = instance_copy;
1125 
1126 			if (method.Parameters.HasArglist) {
1127 				var varargs_types = GetVarargsTypes (method, Arguments);
1128 				ec.Emit (call_op, method, varargs_types);
1129 			} else {
1130 				//
1131 				// If you have:
1132 				// this.DoFoo ();
1133 				// and DoFoo is not virtual, you can omit the callvirt,
1134 				// because you don't need the null checking behavior.
1135 				//
1136 				ec.Emit (call_op, method);
1137 			}
1138 
1139 			//
1140 			// Pop the return value if there is one and stack should be empty
1141 			//
1142 			if (statement && method.ReturnType.Kind != MemberKind.Void)
1143 				ec.Emit (OpCodes.Pop);
1144 		}
1145 
GetVarargsTypesMono.CSharp.CallEmitter1146 		static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
1147 		{
1148 			AParametersCollection pd = method.Parameters;
1149 
1150 			Argument a = arguments[pd.Count - 1];
1151 			Arglist list = (Arglist) a.Expr;
1152 
1153 			return list.ArgumentTypes;
1154 		}
1155 
1156 		//
1157 		// Used to decide whether call or callvirt is needed
1158 		//
IsVirtualCallRequiredMono.CSharp.CallEmitter1159 		static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
1160 		{
1161 			//
1162 			// There are 2 scenarious where we emit callvirt
1163 			//
1164 			// Case 1: A method is virtual and it's not used to call base
1165 			// Case 2: A method instance expression can be null. In this casen callvirt ensures
1166 			// correct NRE exception when the method is called
1167 			//
1168 			var decl_type = method.DeclaringType;
1169 			if (decl_type.IsStruct || decl_type.IsEnum)
1170 				return false;
1171 
1172 			if (instance is BaseThis)
1173 				return false;
1174 
1175 			//
1176 			// It's non-virtual and will never be null and it can be determined
1177 			// whether it's known value or reference type by verifier
1178 			//
1179 			if (!method.IsVirtual && Expression.IsNeverNull (instance) && !instance.Type.IsGenericParameter)
1180 				return false;
1181 
1182 			return true;
1183 		}
1184 
IsAddressCallMono.CSharp.CallEmitter1185 		static bool IsAddressCall (Expression instance, OpCode callOpcode, TypeSpec declaringType)
1186 		{
1187 			var instance_type = instance.Type;
1188 			return (instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
1189 				instance_type.IsGenericParameter || declaringType.IsNullableType;
1190 		}
1191 	}
1192 
1193 	public struct InstanceEmitter
1194 	{
1195 		readonly Expression instance;
1196 		readonly bool addressRequired;
1197 
InstanceEmitterMono.CSharp.InstanceEmitter1198 		public InstanceEmitter (Expression instance, bool addressLoad)
1199 		{
1200 			this.instance = instance;
1201 			this.addressRequired = addressLoad;
1202 		}
1203 
EmitMono.CSharp.InstanceEmitter1204 		public void Emit (EmitContext ec, bool conditionalAccess)
1205 		{
1206 			Label NullOperatorLabel;
1207 			Nullable.Unwrap unwrap;
1208 
1209 			if (conditionalAccess && Expression.IsNeverNull (instance))
1210 				conditionalAccess = false;
1211 
1212 			if (conditionalAccess) {
1213 				NullOperatorLabel = ec.DefineLabel ();
1214 				unwrap = instance as Nullable.Unwrap;
1215 			} else {
1216 				NullOperatorLabel = new Label ();
1217 				unwrap = null;
1218 			}
1219 
1220 			IMemoryLocation instance_address = null;
1221 			bool conditional_access_dup = false;
1222 
1223 			if (unwrap != null) {
1224 				unwrap.Store (ec);
1225 				unwrap.EmitCheck (ec);
1226 				ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
1227 			} else {
1228 				if (conditionalAccess && addressRequired) {
1229 					//
1230 					// Don't allocate temp variable when instance load is cheap and load and load-address
1231 					// operate on same memory
1232 					//
1233 					instance_address = instance as VariableReference;
1234 					if (instance_address == null)
1235 						instance_address = instance as LocalTemporary;
1236 
1237 					if (instance_address == null) {
1238 						EmitLoad (ec, false);
1239 						ec.Emit (OpCodes.Dup);
1240 						ec.EmitLoadFromPtr (instance.Type);
1241 
1242 						conditional_access_dup = true;
1243 					} else {
1244 						instance.Emit (ec);
1245 					}
1246 				} else {
1247 					EmitLoad (ec, !conditionalAccess);
1248 
1249 					if (conditionalAccess) {
1250 						conditional_access_dup = !ExpressionAnalyzer.IsInexpensiveLoad (instance);
1251 						if (conditional_access_dup)
1252 							ec.Emit (OpCodes.Dup);
1253 					}
1254 				}
1255 
1256 				if (conditionalAccess) {
1257 					if (instance.Type.Kind == MemberKind.TypeParameter)
1258 						ec.Emit (OpCodes.Box, instance.Type);
1259 
1260 					ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
1261 
1262 					if (conditional_access_dup)
1263 						ec.Emit (OpCodes.Pop);
1264 				}
1265 			}
1266 
1267 			if (conditionalAccess) {
1268 				if (!ec.ConditionalAccess.Statement) {
1269 					var t = ec.ConditionalAccess.Type;
1270 					if (t.IsNullableType)
1271 						Nullable.LiftedNull.Create (t, Location.Null).Emit (ec);
1272 					else {
1273 						ec.EmitNull ();
1274 
1275 						if (t.IsGenericParameter)
1276 							ec.Emit (OpCodes.Unbox_Any, t);
1277 					}
1278 				}
1279 
1280 				ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
1281 				ec.MarkLabel (NullOperatorLabel);
1282 
1283 				if (instance_address != null) {
1284 					instance_address.AddressOf (ec, AddressOp.Load);
1285 				} else if (unwrap != null) {
1286 					unwrap.Emit (ec);
1287 					if (addressRequired) {
1288 						var tmp = ec.GetTemporaryLocal (unwrap.Type);
1289 						ec.Emit (OpCodes.Stloc, tmp);
1290 						ec.Emit (OpCodes.Ldloca, tmp);
1291 						ec.FreeTemporaryLocal (tmp, unwrap.Type);
1292 					}
1293 				} else if (!conditional_access_dup) {
1294 					instance.Emit (ec);
1295 				}
1296 			}
1297 		}
1298 
EmitLoadMono.CSharp.InstanceEmitter1299 		public void EmitLoad (EmitContext ec, bool boxInstance)
1300 		{
1301 			var instance_type = instance.Type;
1302 
1303 			//
1304 			// Push the instance expression
1305 			//
1306 			if (addressRequired) {
1307 				//
1308 				// If the expression implements IMemoryLocation, then
1309 				// we can optimize and use AddressOf on the
1310 				// return.
1311 				//
1312 				// If not we have to use some temporary storage for
1313 				// it.
1314 				var iml = instance as IMemoryLocation;
1315 				if (iml != null) {
1316 					iml.AddressOf (ec, AddressOp.Load);
1317 				} else {
1318 					LocalTemporary temp = new LocalTemporary (instance_type);
1319 					instance.Emit (ec);
1320 					temp.Store (ec);
1321 					temp.AddressOf (ec, AddressOp.Load);
1322 				}
1323 
1324 				return;
1325 			}
1326 
1327 			instance.Emit (ec);
1328 
1329 			// Only to make verifier happy
1330 			if (boxInstance && ExpressionAnalyzer.RequiresBoxing (instance)) {
1331 				ec.Emit (OpCodes.Box, instance_type);
1332 			}
1333 		}
1334 
GetStackTypeMono.CSharp.InstanceEmitter1335 		public TypeSpec GetStackType (EmitContext ec)
1336 		{
1337 			var instance_type = instance.Type;
1338 
1339 			if (addressRequired)
1340 				return ReferenceContainer.MakeType (ec.Module, instance_type);
1341 
1342 			if (instance_type.IsStructOrEnum)
1343 				return ec.Module.Compiler.BuiltinTypes.Object;
1344 
1345 			return instance_type;
1346 		}
1347 
1348 
1349 	}
1350 
1351 	static class ExpressionAnalyzer
1352 	{
RequiresBoxing(Expression instance)1353 		public static bool RequiresBoxing (Expression instance)
1354 		{
1355 			var instance_type = instance.Type;
1356 			if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type))
1357 				return true;
1358 
1359 			if (instance_type.IsStructOrEnum)
1360 				return true;
1361 
1362 			return false;
1363 		}
1364 
1365 		//
1366 		// Returns true for cheap race-free load, where we can avoid using dup
1367 		//
IsInexpensiveLoad(Expression instance)1368 		public static bool IsInexpensiveLoad (Expression instance)
1369 		{
1370 			if (instance is Constant)
1371 				return instance.IsSideEffectFree;
1372 
1373 			if (RequiresBoxing (instance))
1374 				return false;
1375 
1376 			var vr = instance as VariableReference;
1377 			if (vr != null) {
1378 				// Load from captured local would be racy without dup
1379 				return !vr.IsRef && !vr.IsHoisted;
1380 			}
1381 
1382 			if (instance is LocalTemporary)
1383 				return true;
1384 
1385 			var fe = instance as FieldExpr;
1386 			if (fe != null)
1387 				return fe.IsStatic || fe.InstanceExpression is This;
1388 
1389 			return false;
1390 		}
1391 	}
1392 }
1393