Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Safar <marek.safar@gmail.com>2016-01-28 15:08:11 +0300
committerMarek Safar <marek.safar@gmail.com>2016-01-28 15:09:43 +0300
commit85490058c8335b60441ef02863f861aaabfa51dd (patch)
tree047e47fef738602f56dbc4070f4240233436a75f
parent27f20bf400207e44b4bfac972e435c29615cad59 (diff)
[mcs] Implements null operator for dynamic expressions. Fixes #37801, #37824
-rw-r--r--mcs/mcs/dynamic.cs147
-rw-r--r--mcs/mcs/expression.cs13
-rw-r--r--mcs/tests/dtest-null-operator-01.cs57
-rw-r--r--mcs/tests/ver-il-net_4_x.xml29
4 files changed, 235 insertions, 11 deletions
diff --git a/mcs/mcs/dynamic.cs b/mcs/mcs/dynamic.cs
index 3b2a5b4b18d..09d78302854 100644
--- a/mcs/mcs/dynamic.cs
+++ b/mcs/mcs/dynamic.cs
@@ -13,6 +13,11 @@ using System;
using System.Linq;
using SLE = System.Linq.Expressions;
using System.Dynamic;
+#if STATIC
+using IKVM.Reflection.Emit;
+#else
+using System.Reflection.Emit;
+#endif
namespace Mono.CSharp
{
@@ -314,6 +319,50 @@ namespace Mono.CSharp
EmitCall (ec, binder_expr, arguments, true);
}
+ protected void EmitConditionalAccess (EmitContext ec)
+ {
+ var a_expr = arguments [0].Expr;
+
+ var des = a_expr as DynamicExpressionStatement;
+ if (des != null) {
+ des.EmitConditionalAccess (ec);
+ }
+
+ if (HasConditionalAccess ()) {
+ var NullOperatorLabel = ec.DefineLabel ();
+
+ if (ExpressionAnalyzer.IsInexpensiveLoad (a_expr)) {
+ a_expr.Emit (ec);
+ } else {
+ var lt = new LocalTemporary (a_expr.Type);
+ lt.EmitAssign (ec, a_expr, true, false);
+
+ Arguments [0].Expr = lt;
+ }
+
+ ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+
+ 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);
+
+ return;
+ }
+
+ if (a_expr.HasConditionalAccess ()) {
+ var lt = new LocalTemporary (a_expr.Type);
+ lt.EmitAssign (ec, a_expr, false, false);
+
+ Arguments [0].Expr = lt;
+ }
+ }
+
protected void EmitCall (EmitContext ec, Expression binder, Arguments arguments, bool isStatement)
{
//
@@ -502,6 +551,24 @@ namespace Mono.CSharp
StatementExpression s = new StatementExpression (new SimpleAssign (site_field_expr, new Invocation (new MemberAccess (instanceAccessExprType, "Create"), args)));
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
+
+ var conditionalAccessReceiver = IsConditionalAccessReceiver;
+ var ca = ec.ConditionalAccess;
+
+ if (conditionalAccessReceiver) {
+ ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()) {
+ Statement = isStatement
+ };
+
+ //
+ // Emit conditional access expressions before dynamic call
+ // is initialized. It pushes site_field_expr on stack before
+ // the actual instance argument is emited which would cause
+ // jump from non-empty stack.
+ //
+ EmitConditionalAccess (ec);
+ }
+
if (s.Resolve (bc)) {
Statement init = new If (new Binary (Binary.Operator.Equality, site_field_expr, new NullLiteral (loc)), s, loc);
init.Emit (ec);
@@ -526,9 +593,15 @@ namespace Mono.CSharp
}
}
- Expression target = new DelegateInvocation (new MemberAccess (site_field_expr, "Target", loc).Resolve (bc), args, false, loc).Resolve (bc);
- if (target != null)
+ var target = new DelegateInvocation (new MemberAccess (site_field_expr, "Target", loc).Resolve (bc), args, false, loc).Resolve (bc);
+ if (target != null) {
target.Emit (ec);
+ }
+
+ if (conditionalAccessReceiver) {
+ ec.CloseConditionalAccess (!isStatement && type.IsNullableType ? type : null);
+ ec.ConditionalAccess = ca;
+ }
}
}
@@ -547,6 +620,12 @@ namespace Mono.CSharp
{
return new MemberAccess (new TypeExpression (binder_type, loc), name, loc);
}
+
+ protected virtual bool IsConditionalAccessReceiver {
+ get {
+ return false;
+ }
+ }
}
//
@@ -671,14 +750,18 @@ namespace Mono.CSharp
class DynamicIndexBinder : DynamicMemberAssignable
{
bool can_be_mutator;
+ readonly bool conditional_access_receiver;
+ readonly bool conditional_access;
- public DynamicIndexBinder (Arguments args, Location loc)
+ public DynamicIndexBinder (Arguments args, bool conditionalAccessReceiver, bool conditionalAccess, Location loc)
: base (args, loc)
{
+ this.conditional_access_receiver = conditionalAccessReceiver;
+ this.conditional_access = conditionalAccess;
}
public DynamicIndexBinder (CSharpBinderFlags flags, Arguments args, Location loc)
- : this (args, loc)
+ : this (args, false, false, loc)
{
base.flags = flags;
}
@@ -732,22 +815,35 @@ namespace Mono.CSharp
setter_args.Add (new Argument (rhs));
return setter_args;
}
+
+ protected override bool IsConditionalAccessReceiver {
+ get {
+ return conditional_access_receiver;
+ }
+ }
+
+ public override bool HasConditionalAccess ()
+ {
+ return conditional_access;
+ }
}
class DynamicInvocation : DynamicExpressionStatement, IDynamicBinder
{
readonly ATypeNameExpression member;
+ readonly bool conditional_access_receiver;
- public DynamicInvocation (ATypeNameExpression member, Arguments args, Location loc)
+ public DynamicInvocation (ATypeNameExpression member, Arguments args, bool conditionalAccessReceiver, Location loc)
: base (null, args, loc)
{
base.binder = this;
this.member = member;
+ this.conditional_access_receiver = conditionalAccessReceiver;
}
public static DynamicInvocation CreateSpecialNameInvoke (ATypeNameExpression member, Arguments args, Location loc)
{
- return new DynamicInvocation (member, args, loc) {
+ return new DynamicInvocation (member, args, false, loc) {
flags = CSharpBinderFlags.InvokeSpecialName
};
}
@@ -805,11 +901,36 @@ namespace Mono.CSharp
flags |= CSharpBinderFlags.ResultDiscarded;
base.EmitStatement (ec);
}
+
+ protected override bool IsConditionalAccessReceiver {
+ get {
+ return conditional_access_receiver;
+ }
+ }
+
+ public override bool HasConditionalAccess ()
+ {
+ return member is ConditionalMemberAccess;
+ }
+ }
+
+ class DynamicConditionalMemberBinder : DynamicMemberBinder
+ {
+ public DynamicConditionalMemberBinder (string name, Arguments args, Location loc)
+ : base (name, args, loc)
+ {
+ }
+
+ public override bool HasConditionalAccess ()
+ {
+ return true;
+ }
}
class DynamicMemberBinder : DynamicMemberAssignable
{
readonly string name;
+ bool conditionalAccessReceiver;
public DynamicMemberBinder (string name, Arguments args, Location loc)
: base (args, loc)
@@ -835,6 +956,20 @@ namespace Mono.CSharp
isSet |= (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0;
return new Invocation (GetBinder (isSet ? "SetMember" : "GetMember", loc), binder_args);
}
+
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ if (!rc.HasSet (ResolveContext.Options.DontSetConditionalAccessReceiver))
+ conditionalAccessReceiver = HasConditionalAccess () || Arguments [0].Expr.HasConditionalAccess ();
+
+ return base.DoResolve (rc);
+ }
+
+ protected override bool IsConditionalAccessReceiver {
+ get {
+ return conditionalAccessReceiver;
+ }
+ }
}
//
diff --git a/mcs/mcs/expression.cs b/mcs/mcs/expression.cs
index e8938153c80..c5139d57f67 100644
--- a/mcs/mcs/expression.cs
+++ b/mcs/mcs/expression.cs
@@ -110,7 +110,6 @@ namespace Mono.CSharp
protected override Expression DoResolve (ResolveContext rc)
{
Expression res = null;
-
using (rc.With (ResolveContext.Options.DontSetConditionalAccessReceiver, false)) {
res = expr.Resolve (rc);
}
@@ -7182,7 +7181,7 @@ namespace Mono.CSharp
}
}
- return new DynamicInvocation (expr as ATypeNameExpression, args, loc).Resolve (ec);
+ return new DynamicInvocation (expr as ATypeNameExpression, args, conditional_access_receiver, loc).Resolve (ec);
}
protected virtual MethodGroupExpr DoResolveOverload (ResolveContext ec)
@@ -9720,6 +9719,8 @@ namespace Mono.CSharp
return retval;
}
+ var cma = this as ConditionalMemberAccess;
+
MemberExpr me;
TypeSpec expr_type = expr.Type;
if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
@@ -9729,10 +9730,13 @@ namespace Mono.CSharp
Arguments args = new Arguments (1);
args.Add (new Argument (expr));
+
+ if (cma != null)
+ return new DynamicConditionalMemberBinder (Name, args, loc);
+
return new DynamicMemberBinder (Name, args, loc);
}
- var cma = this as ConditionalMemberAccess;
if (cma != null) {
if (!IsNullPropagatingValid (expr.Type)) {
expr.Error_OperatorCannotBeApplied (rc, loc, "?", expr.Type);
@@ -10863,7 +10867,7 @@ namespace Mono.CSharp
args.AddRange (arguments);
best_candidate = null;
- return new DynamicIndexBinder (args, loc);
+ return new DynamicIndexBinder (args, conditional_access_receiver, ConditionalAccess, loc);
}
//
@@ -11671,7 +11675,6 @@ namespace Mono.CSharp
args.Add (new Argument (rc.CurrentInitializerVariable));
target = new DynamicMemberBinder (Name, args, loc);
} else {
-
var member = MemberLookup (rc, false, t, Name, 0, MemberLookupRestrictions.ExactArity, loc);
if (member == null) {
member = Expression.MemberLookup (rc, true, t, Name, 0, MemberLookupRestrictions.ExactArity, loc);
diff --git a/mcs/tests/dtest-null-operator-01.cs b/mcs/tests/dtest-null-operator-01.cs
new file mode 100644
index 00000000000..de6013ca044
--- /dev/null
+++ b/mcs/tests/dtest-null-operator-01.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+class X
+{
+ public string Prop;
+ public A A = new A ();
+}
+
+class A
+{
+ public string B;
+}
+
+class MainClass
+{
+ static void NullCheckTest ()
+ {
+ dynamic dyn = null;
+ dynamic res;
+
+ res = dyn?.ToString ();
+ res = dyn?.GetHashCode ();
+ res = dyn?.DD.Length?.GetHashCode ();
+
+ dyn?.ToString ();
+
+ res = dyn?.Prop;
+ res = dyn?.Prop?.Prop2;
+ res = dyn?[0];
+ }
+
+ static void Test_1 ()
+ {
+ dynamic dyn = new X ();
+ dynamic res;
+
+ res = dyn.Prop?.Length;
+ res = dyn.A.B?.C.D?.E.F;
+ }
+
+ static dynamic Test_2 (IEnumerable<dynamic> collection)
+ {
+ return collection?.FirstOrDefault ().Length;
+ }
+
+ public static void Main ()
+ {
+ NullCheckTest ();
+
+ Test_1 ();
+ Test_2 (null);
+ }
+}
+
+ \ No newline at end of file
diff --git a/mcs/tests/ver-il-net_4_x.xml b/mcs/tests/ver-il-net_4_x.xml
index 56be2d26469..dcd5f610003 100644
--- a/mcs/tests/ver-il-net_4_x.xml
+++ b/mcs/tests/ver-il-net_4_x.xml
@@ -3468,6 +3468,35 @@
</method>
</type>
</test>
+ <test name="dtest-null-operator-01.cs">
+ <type name="X">
+ <method name="Void .ctor()" attrs="6278">
+ <size>18</size>
+ </method>
+ </type>
+ <type name="A">
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="MainClass">
+ <method name="Void NullCheckTest()" attrs="145">
+ <size>890</size>
+ </method>
+ <method name="Void Test_1()" attrs="145">
+ <size>672</size>
+ </method>
+ <method name="System.Object Test_2(System.Collections.Generic.IEnumerable`1[System.Object])" attrs="145">
+ <size>103</size>
+ </method>
+ <method name="Void Main()" attrs="150">
+ <size>19</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
<test name="dtest-optional-01.cs">
<type name="G`1[T]">
<method name="System.Object M1(T)" attrs="134">