Skip to content

Commit

Permalink
Prevent invoking property 'init' method (dotnet#41293)
Browse files Browse the repository at this point in the history
  • Loading branch information
cston authored Aug 25, 2020
1 parent e94dc2f commit d9fe9db
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ private void CheckLvalue(Expr expr, CheckLvalueKind kind)
{
case ExpressionKind.Property:
ExprProperty prop = (ExprProperty)expr;
Debug.Assert(!prop.MethWithTypeSet);
Debug.Assert(!prop.MethWithTypeSet || ExprProperty.HasIsExternalInitModifier(prop.MethWithTypeSet));
// Assigning to a property without a setter.
// If we have
// bool? b = true; (bool)b = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using System.Reflection;

namespace Microsoft.CSharp.RuntimeBinder.Semantics
{
internal sealed class ExprProperty : ExprWithArgs
Expand Down Expand Up @@ -28,7 +31,10 @@ public ExprProperty(CType type, Expr pOptionalObjectThrough, Expr pOptionalArgum
if (mwtSet != null)
{
MethWithTypeSet = mwtSet;
Flags = EXPRFLAG.EXF_LVALUE;
if (!HasIsExternalInitModifier(mwtSet))
{
Flags = EXPRFLAG.EXF_LVALUE;
}
}
}

Expand All @@ -39,5 +45,12 @@ public ExprProperty(CType type, Expr pOptionalObjectThrough, Expr pOptionalArgum
public MethWithType MethWithTypeSet { get; }

public override SymWithType GetSymWithType() => PropWithTypeSlot;

internal static bool HasIsExternalInitModifier(MethWithType mwtSet)
{
var types = (mwtSet.Meth()?.AssociatedMemberInfo as MethodInfo)?.ReturnParameter.GetRequiredCustomModifiers();
return types != null &&
types.Any(type => type.Name == "IsExternalInit" && !type.IsNested && type.Namespace == "System.Runtime.CompilerServices");
}
}
}
60 changes: 60 additions & 0 deletions src/libraries/Microsoft.CSharp/tests/BindingErrors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -560,5 +560,65 @@ public void AllowStringIndexerAccess()
char c = d.get_Chars(2);
Assert.Equal('c', c);
}

private sealed class InitOnlyProperty
{
public InitOnlyProperty() { }
public InitOnlyProperty(int value)
{
((dynamic)this).P = value;
}
public int P { get; init; }
public int Q
{
get { return P; }
init { ((dynamic)this).P = value; }
}
}

[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public void InitOnlyProperty_Set()
{
dynamic d = new InitOnlyProperty();
Assert.Throws<RuntimeBinderException>(() => d.P = 1);
}

[Fact]
public void InitOnlyProperty_SetAccessor()
{
dynamic d = new InitOnlyProperty();
Assert.Throws<RuntimeBinderException>(() => d.set_P(1));
}

[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public void InitOnlyProperty_SetInConstructor()
{
Assert.Throws<RuntimeBinderException>(() => new InitOnlyProperty(1));
}

[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public void InitOnlyProperty_SetInInitAccessor()
{
Assert.Throws<RuntimeBinderException>(() => new InitOnlyProperty() { Q = 1 });
}

[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public void InitOnlyProperty_Increment()
{
dynamic d = new InitOnlyProperty();
Assert.Throws<RuntimeBinderException>(() => d.P++);
}

[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public void InitOnlyProperty_CompoundAssignment()
{
dynamic d = new InitOnlyProperty();
Assert.Throws<RuntimeBinderException>(() => d.P += 1);
}
}
}
9 changes: 9 additions & 0 deletions src/libraries/Microsoft.CSharp/tests/IsExternalInit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Runtime.CompilerServices
{
public sealed class IsExternalInit
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<Compile Include="IntegerBinaryOperationTests.cs" />
<Compile Include="IntegerUnaryOperationTests.cs" />
<Compile Include="IsEventTests.cs" />
<Compile Include="IsExternalInit.cs" />
<Compile Include="NamedArgumentTests.cs" />
<Compile Include="NullableEnumUnaryOperationTest.cs" />
<Compile Include="RuntimeBinderExceptionTests.cs" />
Expand Down

0 comments on commit d9fe9db

Please sign in to comment.