Merge pull request #1936 from esdrubal/DotNetRelativeOrAbsolute
[mono.git] / mcs / mcs / codegen.cs
index 1f81899762473f67532c29cac010f990cb1e531a..429c3b9d3b0d1f23db2fb66109e2d5ab93f6ca03 100644 (file)
@@ -121,6 +121,8 @@ namespace Mono.CSharp
                        }
                }
 
+               public ConditionalAccessContext ConditionalAccess { get; set; }
+
                public TypeSpec CurrentType {
                        get { return member_context.CurrentType; }
                }
@@ -155,6 +157,12 @@ namespace Mono.CSharp
                        get { return member_context.IsStatic; }
                }
 
+               public bool IsStaticConstructor {
+                       get {
+                               return member_context.IsStatic && (flags & Options.ConstructorScope) != 0;
+                       }
+               }
+
                public bool IsAnonymousStoreyMutateRequired {
                        get {
                                return CurrentAnonymousMethod != null &&
@@ -175,6 +183,12 @@ namespace Mono.CSharp
                        }
                }
 
+               public bool NotifyEvaluatorOnStore {
+                       get {
+                               return Module.Evaluator != null && Module.Evaluator.ModificationListener != null;
+                       }
+               }
+
                // Has to be used for specific emitter errors only any
                // possible resolver errors have to be reported during Resolve
                public Report Report {
@@ -208,6 +222,8 @@ namespace Mono.CSharp
 
                public List<TryFinally> TryFinallyUnwind { get; set; }
 
+               public Label RecursivePatternLabel { get; set; }
+
                #endregion
 
                public void AddStatementEpilog (IExpressionCleanup cleanupExpression)
@@ -246,9 +262,7 @@ namespace Mono.CSharp
                        if (sf.IsHiddenLocation (loc))
                                return false;
 
-#if NET_4_0
                        methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false);
-#endif
                        return true;
                }
 
@@ -308,9 +322,7 @@ namespace Mono.CSharp
                        if ((flags & Options.OmitDebugInfo) != 0)
                                return;
 
-#if NET_4_0
                        methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset);
-#endif
                }
 
                public void BeginCompilerScope ()
@@ -318,9 +330,7 @@ namespace Mono.CSharp
                        if ((flags & Options.OmitDebugInfo) != 0)
                                return;
 
-#if NET_4_0
                        methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset);
-#endif
                }
 
                public void EndExceptionBlock ()
@@ -333,9 +343,16 @@ namespace Mono.CSharp
                        if ((flags & Options.OmitDebugInfo) != 0)
                                return;
 
-#if NET_4_0
                        methodSymbols.EndBlock (ig.ILOffset);
-#endif
+               }
+
+               public void CloseConditionalAccess (TypeSpec type)
+               {
+                       if (type != null)
+                               Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type));
+
+                       MarkLabel (ConditionalAccess.EndLabel);
+                       ConditionalAccess = null;
                }
 
                //
@@ -384,9 +401,9 @@ namespace Mono.CSharp
                //
                // Creates temporary field in current async storey
                //
-               public StackFieldExpr GetTemporaryField (TypeSpec type)
+               public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false)
                {
-                       var f = AsyncTaskStorey.AddCapturedLocalVariable (type);
+                       var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired);
                        var fexpr = new StackFieldExpr (f);
                        fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
                        return fexpr;
@@ -522,8 +539,13 @@ namespace Mono.CSharp
                                type = EnumSpec.GetUnderlyingType (type);
 
                        switch (type.BuiltinType) {
-                       case BuiltinTypeSpec.Type.Byte:
                        case BuiltinTypeSpec.Type.Bool:
+                               //
+                               // bool array can actually store any byte value in underlying byte slot
+                               // and C# spec does not specify any normalization rule, except the result
+                               // is undefined
+                               //
+                       case BuiltinTypeSpec.Type.Byte:
                                ig.Emit (OpCodes.Ldelem_U1);
                                break;
                        case BuiltinTypeSpec.Type.SByte:
@@ -866,6 +888,9 @@ namespace Mono.CSharp
 
                                ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
                                break;
+                       case MemberKind.PointerType:
+                               ig.Emit (OpCodes.Stind_I);
+                               break;
                        default:
                                ig.Emit (OpCodes.Stind_Ref);
                                break;
@@ -954,12 +979,25 @@ namespace Mono.CSharp
                }
        }
 
+       public class ConditionalAccessContext
+       {
+               public ConditionalAccessContext (TypeSpec type, Label endLabel)
+               {
+                       Type = type;
+                       EndLabel = endLabel;
+               }
+
+               public bool Statement { get; set; }
+               public Label EndLabel { get; private set; }
+               public TypeSpec Type { get; private set; }
+       }
+
        struct CallEmitter
        {
                public Expression InstanceExpression;
 
                //
-               // When set leaves an extra copy of all arguments on the stack
+               // When call has to leave an extra copy of all arguments on the stack
                //
                public bool DuplicateArguments;
 
@@ -974,6 +1012,8 @@ namespace Mono.CSharp
                //
                public bool HasAwaitArguments;
 
+               public bool ConditionalAccess;
+
                //
                // When dealing with await arguments the original arguments are converted
                // into a new set with hoisted stack results
@@ -982,10 +1022,15 @@ namespace Mono.CSharp
 
                public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
                {
-                       EmitPredefined (ec, method, Arguments, loc);
+                       EmitPredefined (ec, method, Arguments, false, loc);
+               }
+
+               public void EmitStatement (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
+               {
+                       EmitPredefined (ec, method, Arguments, true, loc);
                }
 
-               public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, Location? loc = null)
+               public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, bool statement = false, Location? loc = null)
                {
                        Expression instance_copy = null;
 
@@ -1002,23 +1047,23 @@ namespace Mono.CSharp
                        if (method.IsStatic) {
                                call_op = OpCodes.Call;
                        } else {
-                               if (IsVirtualCallRequired (InstanceExpression, method)) {
-                                       call_op = OpCodes.Callvirt;
-                               } else {
-                                       call_op = OpCodes.Call;
-                               }
+                               call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call;
 
                                if (HasAwaitArguments) {
                                        instance_copy = InstanceExpression.EmitToField (ec);
-                                       if (Arguments == null)
-                                               EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
+                                       var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+
+                                       if (Arguments == null) {
+                                               ie.EmitLoad (ec, true);
+                                       }
                                } else if (!InstanceExpressionOnStack) {
-                                       var instance_on_stack_type = EmitCallInstance (ec, InstanceExpression, method.DeclaringType, call_op);
+                                       var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
+                                       ie.Emit (ec, ConditionalAccess);
 
                                        if (DuplicateArguments) {
                                                ec.Emit (OpCodes.Dup);
                                                if (Arguments != null && Arguments.Count != 0) {
-                                                       lt = new LocalTemporary (instance_on_stack_type);
+                                                       lt = new LocalTemporary (ie.GetStackType (ec));
                                                        lt.Store (ec);
                                                        instance_copy = lt;
                                                }
@@ -1030,7 +1075,8 @@ namespace Mono.CSharp
                                EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
                                if (EmittedArguments != null) {
                                        if (instance_copy != null) {
-                                               EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
+                                               var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+                                               ie.Emit (ec, ConditionalAccess);
 
                                                if (lt != null)
                                                        lt.Release (ec);
@@ -1061,27 +1107,177 @@ namespace Mono.CSharp
                        if (method.Parameters.HasArglist) {
                                var varargs_types = GetVarargsTypes (method, Arguments);
                                ec.Emit (call_op, method, varargs_types);
-                               return;
+                       } else {
+                               //
+                               // If you have:
+                               // this.DoFoo ();
+                               // and DoFoo is not virtual, you can omit the callvirt,
+                               // because you don't need the null checking behavior.
+                               //
+                               ec.Emit (call_op, method);
                        }
 
+                       // 
+                       // Pop the return value if there is one and stack should be empty
+                       //
+                       if (statement && method.ReturnType.Kind != MemberKind.Void)
+                               ec.Emit (OpCodes.Pop);
+               }
+
+               static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
+               {
+                       AParametersCollection pd = method.Parameters;
+
+                       Argument a = arguments[pd.Count - 1];
+                       Arglist list = (Arglist) a.Expr;
+
+                       return list.ArgumentTypes;
+               }
+
+               //
+               // Used to decide whether call or callvirt is needed
+               //
+               static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
+               {
+                       //
+                       // There are 2 scenarious where we emit callvirt
+                       //
+                       // Case 1: A method is virtual and it's not used to call base
+                       // Case 2: A method instance expression can be null. In this casen callvirt ensures
+                       // correct NRE exception when the method is called
+                       //
+                       var decl_type = method.DeclaringType;
+                       if (decl_type.IsStruct || decl_type.IsEnum)
+                               return false;
+
+                       if (instance is BaseThis)
+                               return false;
+
                        //
-                       // If you have:
-                       // this.DoFoo ();
-                       // and DoFoo is not virtual, you can omit the callvirt,
-                       // because you don't need the null checking behavior.
+                       // It's non-virtual and will never be null and it can be determined
+                       // whether it's known value or reference type by verifier
                        //
-                       ec.Emit (call_op, method);
+                       if (!method.IsVirtual && Expression.IsNeverNull (instance) && !instance.Type.IsGenericParameter)
+                               return false;
+
+                       return true;
                }
 
-               static TypeSpec EmitCallInstance (EmitContext ec, Expression instance, TypeSpec declaringType, OpCode callOpcode)
+               static bool IsAddressCall (Expression instance, OpCode callOpcode, TypeSpec declaringType)
+               {
+                       var instance_type = instance.Type;
+                       return (instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
+                               instance_type.IsGenericParameter || declaringType.IsNullableType;
+               }
+       }
+
+       public struct InstanceEmitter
+       {
+               readonly Expression instance;
+               readonly bool addressRequired;
+
+               public InstanceEmitter (Expression instance, bool addressLoad)
+               {
+                       this.instance = instance;
+                       this.addressRequired = addressLoad;
+               }
+
+               public void Emit (EmitContext ec, bool conditionalAccess)
+               {
+                       Label NullOperatorLabel;
+                       Nullable.Unwrap unwrap;
+
+                       if (conditionalAccess && Expression.IsNeverNull (instance))
+                               conditionalAccess = false;
+
+                       if (conditionalAccess) {
+                               NullOperatorLabel = ec.DefineLabel ();
+                               unwrap = instance as Nullable.Unwrap;
+                       } else {
+                               NullOperatorLabel = new Label ();
+                               unwrap = null;
+                       }
+
+                       IMemoryLocation instance_address = null;
+                       bool conditional_access_dup = false;
+
+                       if (unwrap != null) {
+                               unwrap.Store (ec);
+                               unwrap.EmitCheck (ec);
+                               ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+                       } else {
+                               if (conditionalAccess && addressRequired) {
+                                       //
+                                       // Don't allocate temp variable when instance load is cheap and load and load-address
+                                       // operate on same memory
+                                       //
+                                       instance_address = instance as VariableReference;
+                                       if (instance_address == null)
+                                               instance_address = instance as LocalTemporary;
+
+                                       if (instance_address == null) {
+                                               EmitLoad (ec, false);
+                                               ec.Emit (OpCodes.Dup);
+                                               ec.EmitLoadFromPtr (instance.Type);
+
+                                               conditional_access_dup = true;
+                                       } else {
+                                               instance.Emit (ec);
+                                       }
+                               } else {
+                                       EmitLoad (ec, !conditionalAccess);
+
+                                       if (conditionalAccess) {
+                                               conditional_access_dup = !IsInexpensiveLoad ();
+                                               if (conditional_access_dup)
+                                                       ec.Emit (OpCodes.Dup);
+                                       }
+                               }
+
+                               if (conditionalAccess) {
+                                       if (instance.Type.Kind == MemberKind.TypeParameter)
+                                               ec.Emit (OpCodes.Box, instance.Type);
+
+                                       ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+
+                                       if (conditional_access_dup)
+                                               ec.Emit (OpCodes.Pop);
+                               }
+                       }
+
+                       if (conditionalAccess) {
+                               if (!ec.ConditionalAccess.Statement) {
+                                       if (ec.ConditionalAccess.Type.IsNullableType)
+                                               Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec);
+                                       else
+                                               ec.EmitNull ();
+                               }
+
+                               ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
+                               ec.MarkLabel (NullOperatorLabel);
+
+                               if (instance_address != null) {
+                                       instance_address.AddressOf (ec, AddressOp.Load);
+                               } else if (unwrap != null) {
+                                       unwrap.Emit (ec);
+                                       var tmp = ec.GetTemporaryLocal (unwrap.Type);
+                                       ec.Emit (OpCodes.Stloc, tmp);
+                                       ec.Emit (OpCodes.Ldloca, tmp);
+                                       ec.FreeTemporaryLocal (tmp, unwrap.Type);
+                               } else if (!conditional_access_dup) {
+                                       instance.Emit (ec);
+                               }
+                       }
+               }
+
+               public void EmitLoad (EmitContext ec, bool boxInstance)
                {
                        var instance_type = instance.Type;
 
                        //
                        // Push the instance expression
                        //
-                       if ((instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
-                               instance_type.IsGenericParameter || declaringType.IsNullableType) {
+                       if (addressRequired) {
                                //
                                // If the expression implements IMemoryLocation, then
                                // we can optimize and use AddressOf on the
@@ -1099,57 +1295,67 @@ namespace Mono.CSharp
                                        temp.AddressOf (ec, AddressOp.Load);
                                }
 
-                               return ReferenceContainer.MakeType (ec.Module, instance_type);
+                               return;
                        }
 
-                       if (instance_type.IsStructOrEnum) {
-                               instance.Emit (ec);
+                       instance.Emit (ec);
+
+                       // Only to make verifier happy
+                       if (boxInstance && RequiresBoxing ()) {
                                ec.Emit (OpCodes.Box, instance_type);
-                               return ec.BuiltinTypes.Object;
                        }
+               }
+
+               public TypeSpec GetStackType (EmitContext ec)
+               {
+                       var instance_type = instance.Type;
+
+                       if (addressRequired)
+                               return ReferenceContainer.MakeType (ec.Module, instance_type);
+
+                       if (instance_type.IsStructOrEnum)
+                               return ec.Module.Compiler.BuiltinTypes.Object;
 
-                       instance.Emit (ec);
                        return instance_type;
                }
 
-               static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
+               bool RequiresBoxing ()
                {
-                       AParametersCollection pd = method.Parameters;
+                       var instance_type = instance.Type;
+                       if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type))
+                               return true;
 
-                       Argument a = arguments[pd.Count - 1];
-                       Arglist list = (Arglist) a.Expr;
+                       if (instance_type.IsStructOrEnum)
+                               return true;
 
-                       return list.ArgumentTypes;
+                       return false;
                }
 
                //
-               // Used to decide whether call or callvirt is needed
+               // Returns true for cheap race-free load, where we can avoid using dup
                //
-               static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
+               bool IsInexpensiveLoad ()
                {
-                       //
-                       // There are 2 scenarious where we emit callvirt
-                       //
-                       // Case 1: A method is virtual and it's not used to call base
-                       // Case 2: A method instance expression can be null. In this casen callvirt ensures
-                       // correct NRE exception when the method is called
-                       //
-                       var decl_type = method.DeclaringType;
-                       if (decl_type.IsStruct || decl_type.IsEnum)
-                               return false;
+                       if (instance is Constant)
+                               return instance.IsSideEffectFree;
 
-                       if (instance is BaseThis)
+                       if (RequiresBoxing ())
                                return false;
 
-                       //
-                       // It's non-virtual and will never be null and it can be determined
-                       // whether it's known value or reference type by verifier
-                       //
-                       if (!method.IsVirtual && (instance is This || instance is New || instance is ArrayCreation || instance is DelegateCreation) &&
-                               !instance.Type.IsGenericParameter)
-                               return false;
+                       var vr = instance as VariableReference;
+                       if (vr != null) {
+                               // Load from captured local would be racy without dup
+                               return !vr.IsRef && !vr.IsHoisted;
+                       }
 
-                       return true;
+                       if (instance is LocalTemporary)
+                               return true;
+
+                       var fe = instance as FieldExpr;
+                       if (fe != null)
+                               return fe.IsStatic || fe.InstanceExpression is This;
+
+                       return false;
                }
        }
 }