Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/FastExpressionCompiler/FastExpressionCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4852,7 +4852,13 @@ private static bool EmitBinding(MemberBinding binding, ILGenerator il, ref Compi
var memberType = member is FieldInfo f ? f.FieldType : ((PropertyInfo)member).PropertyType;
var copyVar = -1;
if (member is FieldInfo field)
il.Demit(isStatic ? (memberType.IsValueType ? OpCodes.Ldsflda : OpCodes.Ldsfld) : memberType.IsValueType ? OpCodes.Ldflda : OpCodes.Ldfld, field);
{
// Readonly value fields are initialized through a defensive copy.
var address = memberType.IsValueType && !field.IsInitOnly;
il.Demit(isStatic ? (address ? OpCodes.Ldsflda : OpCodes.Ldsfld) : address ? OpCodes.Ldflda : OpCodes.Ldfld, field);
if (memberType.IsValueType && !address)
EmitStoreAndLoadLocalVariableAddress(il, memberType);
}
else if (memberType.IsValueType)
{
// the getter returns a copy, so change the local copy and write it back by the setter
Expand Down
2 changes: 2 additions & 0 deletions test/FastExpressionCompiler.TestsRunner.Net472/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ static void RunLightExpressionTests(object state)
t.Run(new LightExpression.UnitTests.RuntimeSwitchExceptionRegressionTests());
t.Run(new LightExpression.UnitTests.VoidLabelValueRegressionTests());
t.Run(new LightExpression.UnitTests.ReferenceInstanceCallRegressionTests());
t.Run(new LightExpression.UnitTests.ReadonlyNestedBindingTests());
t.Run(new LightExpression.UnitTests.MultiRankArrayReceiverRegressionTests());
t.Run(new LightExpression.UnitTests.ArrayIndexExceptionRegressionTests());
t.Run(new LightExpression.UnitTests.ReadonlyValueReceiverRegressionTests());
Expand Down Expand Up @@ -113,6 +114,7 @@ static void RunLightExpressionTests(object state)
fecTests.Run(new RuntimeSwitchExceptionRegressionTests());
fecTests.Run(new VoidLabelValueRegressionTests());
fecTests.Run(new ReferenceInstanceCallRegressionTests());
fecTests.Run(new ReadonlyNestedBindingTests());
fecTests.Run(new MultiRankArrayReceiverRegressionTests());
fecTests.Run(new ArrayIndexExceptionRegressionTests());
fecTests.Run(new ReadonlyValueReceiverRegressionTests());
Expand Down
2 changes: 2 additions & 0 deletions test/FastExpressionCompiler.TestsRunner/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public static void Main(string[] args)
lt.Run(new LightExpression.UnitTests.RuntimeSwitchExceptionRegressionTests());
lt.Run(new LightExpression.UnitTests.VoidLabelValueRegressionTests());
lt.Run(new LightExpression.UnitTests.ReferenceInstanceCallRegressionTests());
lt.Run(new LightExpression.UnitTests.ReadonlyNestedBindingTests());
lt.Run(new LightExpression.UnitTests.MultiRankArrayReceiverRegressionTests());
lt.Run(new LightExpression.UnitTests.ArrayIndexExceptionRegressionTests());
lt.Run(new LightExpression.UnitTests.ReadonlyValueReceiverRegressionTests());
Expand Down Expand Up @@ -139,6 +140,7 @@ public static void Main(string[] args)
st.Run(new RuntimeSwitchExceptionRegressionTests());
st.Run(new VoidLabelValueRegressionTests());
st.Run(new ReferenceInstanceCallRegressionTests());
st.Run(new ReadonlyNestedBindingTests());
st.Run(new MultiRankArrayReceiverRegressionTests());
st.Run(new ArrayIndexExceptionRegressionTests());
st.Run(new ReadonlyValueReceiverRegressionTests());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using S = System.Linq.Expressions.Expression;
#if LIGHT_EXPRESSION
using E = FastExpressionCompiler.LightExpression.Expression;
namespace FastExpressionCompiler.LightExpression.UnitTests;
#else
using E = System.Linq.Expressions.Expression;
namespace FastExpressionCompiler.UnitTests;
#endif
public struct ReadonlyNestedBindingTests : ITestX
{
public struct Cell {public int Number;}
public sealed class Holder {public readonly Cell Readonly=new Cell{Number=2};public Cell Writable;}
public void Run(TestRun t) { Readonly_value_fields_use_defensive_copies(t); }
public void Readonly_value_fields_use_defensive_copies(TestContext t) {
foreach(var name in new[]{"Readonly","Writable"}) {
var field=typeof(Holder).GetField(name);var tree=E.Lambda<Func<Holder>>(E.MemberInit(E.New(typeof(Holder)),E.MemberBind(field,E.Bind(typeof(Cell).GetField("Number"),E.Constant(7)))));
var system=(Func<Holder>)tree.CompileSys();var expected=system();var expectedValue=((Cell)field.GetValue(expected)).Number;
foreach(var flags in new[]{CompilerFlags.Default,CompilerFlags.NoInvocationLambdaInlining,CompilerFlags.EnableDelegateDebugInfo}) {var fast=tree.CompileFast(true,flags);if(t.IsNotNull(fast))t.AreEqual(expectedValue,((Cell)field.GetValue(fast())).Number);}
}
}

}
Loading