Skip to content
Merged
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
47 changes: 32 additions & 15 deletions src/FastExpressionCompiler/FastExpressionCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1442,24 +1442,18 @@ public static Result TryCollectInfo(ref CompilerContext context, Expression expr
#endif
if (elemCount == 0)
return r;
if (expr.NodeType == ExpressionType.NewArrayBounds)
var isArrayInitializer = expr.NodeType == ExpressionType.NewArrayInit;
for (var i = 0; i < elemCount; ++i)
{
for (var i = 0; i < elemCount; ++i)
{
var precedingArrayElementTryCount = context.TryExpressionCount;
if ((r = TryCollectInfo(ref context, arrElems.GetArgument(i), nestedLambda, ref rootNestedLambdas, scopeIdx)) != Result.OK)
return r;
if (i != 0 && context.TryExpressionCount != precedingArrayElementTryCount)
context.ArgsContainingComplexExpression.Set.AddOrGetEntryRef(expr, out _);
}
return r;
}
for (var i = 0; i < elemCount - 1; i++)
var precedingArrayElementTryCount = context.TryExpressionCount;
if ((r = TryCollectInfo(ref context, arrElems.GetArgument(i), nestedLambda, ref rootNestedLambdas, scopeIdx)) != Result.OK)
return r;
expr = arrElems.GetArgument(elemCount - 1);
enterable = false;
continue;
// Initializers keep the array on the stack; bounds only have
// preceding values after the first bound has been evaluated.
if ((isArrayInitializer || i != 0) && context.TryExpressionCount != precedingArrayElementTryCount)
context.ArgsContainingComplexExpression.Set.AddOrGetEntryRef(expr, out _);
}
return r;

case ExpressionType.MemberInit:
return TryCollectMemberInitExprConstants(ref context, (MemberInitExpression)expr, nestedLambda, ref rootNestedLambdas, scopeIdx);
Expand Down Expand Up @@ -4635,6 +4629,29 @@ private static bool EmitNewArrayInit(NewArrayExpression expr, ILGenerator il, re
il.Demit(OpCodes.Newarr, elemType);

var isElemOfValueType = elemType.IsValueType;
if (context.ArgsContainingComplexExpression.Set.ContainsKey(expr))
{
var array = EmitStoreLocalVariable(il, arrayType);
var value = il.GetNextLocalVarIndex(elemType);
for (var i = 0; i < elemCount; ++i)
{
if (!TryEmit(elems.GetArgument(i), il, ref context, ParentFlags.AssignmentRightValue))
return false;
EmitStoreLocalVariable(il, value);
EmitLoadLocalVariable(il, array);
EmitLoadConstantInt(il, i);
if (isElemOfValueType)
il.Demit(OpCodes.Ldelema, elemType);
EmitLoadLocalVariable(il, value);
if (isElemOfValueType)
il.Demit(OpCodes.Stobj, elemType);
else
il.Demit(OpCodes.Stelem_Ref);
}
EmitLoadLocalVariable(il, array);
context.LastEmitIsAddress = false;
return il.EmitPopIfIgnoreResult(parent);
}
for (var i = 0; i < elemCount; i++)
{
il.Demit(OpCodes.Dup);
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 @@ -46,6 +46,7 @@ static void RunLightExpressionTests(object state)
t.Run(new LightExpression.UnitTests.TryResultValueRegressionTests());
t.Run(new LightExpression.UnitTests.NonVectorArrayRegressionTests());
t.Run(new LightExpression.UnitTests.UnboxStorageRegressionTests());
t.Run(new LightExpression.UnitTests.ArrayInitializerExceptionRegressionTests());
t.Run(new LightExpression.UnitTests.VoidSwitchConversionRegressionTests());
t.Run(new LightExpression.UnitTests.ReadonlyRefFieldRegressionTests());
t.Run(new LightExpression.UnitTests.LiftedUserDefinedLogicalRegressionTests());
Expand Down Expand Up @@ -102,6 +103,7 @@ static void RunLightExpressionTests(object state)
fecTests.Run(new TryResultValueRegressionTests());
fecTests.Run(new NonVectorArrayRegressionTests());
fecTests.Run(new UnboxStorageRegressionTests());
fecTests.Run(new ArrayInitializerExceptionRegressionTests());
fecTests.Run(new ReadonlyRefFieldRegressionTests());
fecTests.Run(new LiftedUserDefinedLogicalRegressionTests());
fecTests.Run(new UserDefinedLogicalRegressionTests());
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 @@ -75,6 +75,7 @@ public static void Main(string[] args)
lt.Run(new LightExpression.UnitTests.TryResultValueRegressionTests());
lt.Run(new LightExpression.UnitTests.NonVectorArrayRegressionTests());
lt.Run(new LightExpression.UnitTests.UnboxStorageRegressionTests());
lt.Run(new LightExpression.UnitTests.ArrayInitializerExceptionRegressionTests());
lt.Run(new LightExpression.UnitTests.VoidSwitchConversionRegressionTests());
lt.Run(new LightExpression.UnitTests.ReadonlyRefFieldRegressionTests());
lt.Run(new LightExpression.UnitTests.LiftedUserDefinedLogicalRegressionTests());
Expand Down Expand Up @@ -128,6 +129,7 @@ public static void Main(string[] args)
st.Run(new TryResultValueRegressionTests());
st.Run(new NonVectorArrayRegressionTests());
st.Run(new UnboxStorageRegressionTests());
st.Run(new ArrayInitializerExceptionRegressionTests());
st.Run(new ReadonlyRefFieldRegressionTests());
st.Run(new LiftedUserDefinedLogicalRegressionTests());
st.Run(new UserDefinedLogicalRegressionTests());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using S = System.Linq.Expressions.Expression;
#if LIGHT_EXPRESSION
namespace FastExpressionCompiler.LightExpression.UnitTests;
#else
namespace FastExpressionCompiler.UnitTests;
#endif

public struct ArrayInitializerExceptionRegressionTests : ITestX
{
public void Run(TestRun t) { Array_elements_support_exception_regions(t); Element_effects_ignored_results_and_throws_keep_order(t); }

public void Array_elements_support_exception_regions(TestContext t)
{
CheckElements(t, new[] { 2, 3, 5 });
CheckElements(t, new[] { "a", null, "c" });
CheckElements(t, new[] { 1.25m, decimal.MinValue, decimal.MaxValue });
CheckElements(t, new int?[] { null, 3, 5 });
}


public void Element_effects_ignored_results_and_throws_keep_order(TestContext t)
{
foreach (var fail in new[] { false, true })
foreach (var ignored in new[] { false, true })
{
var events = new List<int>();
Func<int> first = () => { events.Add(0); return 2; };
Func<int> second = () => { events.Add(1); if (fail) throw new InvalidOperationException(); return 3; };
Action finalizer = () => events.Add(2);
Func<int> third = () => { events.Add(3); return 5; };
var body = S.NewArrayInit(typeof(int), S.Invoke(S.Constant(first)),
S.TryFinally(S.Invoke(S.Constant(second)), S.Invoke(S.Constant(finalizer))),
S.Invoke(S.Constant(third)));
var fast = ignored
? (Delegate)S.Lambda<Action>(S.Block(body, S.Empty())).FromSysExpression().CompileFast(true)
: S.Lambda<Func<int[]>>(body).FromSysExpression().CompileFast(true);
if (!t.IsNotNull(fast)) return;
Action invoke = () => { if (ignored) ((Action)fast)(); else ((Func<int[]>)fast)(); };
if (fail) t.Throws<InvalidOperationException>(invoke); else invoke();
t.AreEqual(fail ? "0,1,2" : "0,1,2,3", string.Join(",", events));
}
}


private static void CheckElements<T>(TestContext t, T[] values)
{
var x = S.Parameter(typeof(T));
var y = S.Parameter(typeof(T));
var z = S.Parameter(typeof(T));
for (var site = 0; site < 3; ++site)
{
System.Linq.Expressions.Expression[] elements = { x, y, z };
elements[site] = S.TryFinally(elements[site], S.Empty());
var expression = S.Lambda<Func<T, T, T, T[]>>(S.NewArrayInit(typeof(T), elements), x, y, z);
var fast = expression.FromSysExpression().CompileFast(true);
if (!t.IsNotNull(fast)) return;
var actual = fast(values[0], values[1], values[2]);
for (var i = 0; i < values.Length; ++i) t.AreEqual(values[i], actual[i]);
}
}

}
Loading