Skip to content
Closed
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
44 changes: 22 additions & 22 deletions src/FastExpressionCompiler.LightExpression/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public abstract class Expression
/// and available in the `closure` structure. Find the expression examples below by searching `IsIntrinsic => true`.</summary>
[RequiresUnreferencedCode(Trimming.Message)]

public virtual bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1) => false;
public virtual bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1) => false;

public virtual bool IsCustomToCSharpString => false;

Expand Down Expand Up @@ -3337,9 +3337,9 @@ public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaI
ExpressionCompiler.TryCollectInfo(ref context, Operand, nestedLambda, ref rootNestedLambdas);

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
if (!EmittingVisitor.TryEmit(Operand, il, ref context, parent, byRefIndex))
if (!EmittingVisitor.TryEmit(Operand, il, ref context, context.CurrentParentFlags, byRefIndex))
return false;
il.Demit(OpCodes.Ldftn, Operand.Type.FindDelegateInvokeMethod());
il.Demit(OpCodes.Newobj, Type.GetConstructors()[0]);
Expand Down Expand Up @@ -3379,9 +3379,9 @@ public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaI
ExpressionCompiler.TryCollectInfo(ref context, Operand, nestedLambda, ref rootNestedLambdas);

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
if (!EmittingVisitor.TryEmit(Operand, il, ref context, parent, byRefIndex))
if (!EmittingVisitor.TryEmit(Operand, il, ref context, context.CurrentParentFlags, byRefIndex))
return false;
if (Type == typeof(object))
{
Expand Down Expand Up @@ -3930,7 +3930,7 @@ internal NoArgsNewClassIntrinsicExpression(ConstructorInfo constructor) : base(c
public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaInfo nestedLambda, ref SmallList<NestedLambdaInfo> rootNestedLambdas) => 0;

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
il.Demit(OpCodes.Newobj, Constructor);
return true;
Expand All @@ -3957,9 +3957,9 @@ public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaI
ExpressionCompiler.TryCollectInfo(ref context, Argument, nestedLambda, ref rootNestedLambdas);

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
var ok = EmittingVisitor.TryEmit(Argument, il, ref context, parent | ParentFlags.CtorCall, -1);
var ok = EmittingVisitor.TryEmit(Argument, il, ref context, context.CurrentParentFlags | ParentFlags.CtorCall, -1);
il.Demit(OpCodes.Newobj, Constructor);
return ok;
}
Expand Down Expand Up @@ -3993,9 +3993,9 @@ public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaI
}

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
var f = parent | ParentFlags.CtorCall;
var f = context.CurrentParentFlags | ParentFlags.CtorCall;
var ok =
EmittingVisitor.TryEmit(Argument0, il, ref context, f, -1) &&
EmittingVisitor.TryEmit(Argument1, il, ref context, f, -1);
Expand Down Expand Up @@ -4036,9 +4036,9 @@ public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaI
}

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
var f = parent | ParentFlags.CtorCall;
var f = context.CurrentParentFlags | ParentFlags.CtorCall;
var ok =
EmittingVisitor.TryEmit(Argument0, il, ref context, f, -1) &&
EmittingVisitor.TryEmit(Argument1, il, ref context, f, -1) &&
Expand Down Expand Up @@ -4082,9 +4082,9 @@ public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaI
}

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
var f = parent | ParentFlags.CtorCall;
var f = context.CurrentParentFlags | ParentFlags.CtorCall;
var ok =
EmittingVisitor.TryEmit(Argument0, il, ref context, f, -1) &&
EmittingVisitor.TryEmit(Argument1, il, ref context, f, -1) &&
Expand Down Expand Up @@ -4132,9 +4132,9 @@ public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaI
}

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
var f = parent | ParentFlags.CtorCall;
var f = context.CurrentParentFlags | ParentFlags.CtorCall;
var ok =
EmittingVisitor.TryEmit(Argument0, il, ref context, f, -1) &&
EmittingVisitor.TryEmit(Argument1, il, ref context, f, -1) &&
Expand Down Expand Up @@ -4186,9 +4186,9 @@ public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaI
}

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
var f = parent | ParentFlags.CtorCall;
var f = context.CurrentParentFlags | ParentFlags.CtorCall;
var ok =
EmittingVisitor.TryEmit(Argument0, il, ref context, f, -1) &&
EmittingVisitor.TryEmit(Argument1, il, ref context, f, -1) &&
Expand Down Expand Up @@ -4243,9 +4243,9 @@ public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaI
}

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
var f = parent | ParentFlags.CtorCall;
var f = context.CurrentParentFlags | ParentFlags.CtorCall;
var ok =
EmittingVisitor.TryEmit(Argument0, il, ref context, f, -1) &&
EmittingVisitor.TryEmit(Argument1, il, ref context, f, -1) &&
Expand Down Expand Up @@ -4285,9 +4285,9 @@ public override Result TryCollectInfo(ref CompilerContext context, NestedLambdaI
}

[RequiresUnreferencedCode(Trimming.Message)]
public override bool TryEmit(ref CompilerContext context, ILGenerator il, ParentFlags parent, int byRefIndex = -1)
public override bool TryEmit(ref CompilerContext context, ILGenerator il, int byRefIndex = -1)
{
var f = parent | ParentFlags.CtorCall;
var f = context.CurrentParentFlags | ParentFlags.CtorCall;
var args = Args;
for (var i = 0; i < args.Count; i++)
if (!EmittingVisitor.TryEmit(args[i], il, ref context, f, -1))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;

// Reuse the repository signing key so the signed LightExpression issue-tests assembly can override internal LightExpression members.
[assembly: InternalsVisibleTo("FastExpressionCompiler.LightExpression.IssueTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100C3EE5DD15505AED491F6EFFE157E3EC3694E4EC3A532D3C16E497AB1B0C3CA9FB2959D870E24831B600B576E66B82DDA14F0FD88860D8EA05547454B7FC77201D2082FB320D5E609BBAF853A16D5AC459A9585AF6B48C796B22EBB70472C5412C997F68D6E5A044DE3B0DE7B95D1569EE57BF72469F23C748F5879E50A8D50B2")]
148 changes: 102 additions & 46 deletions src/FastExpressionCompiler/FastExpressionCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -611,13 +611,19 @@ public struct LabelInfo
}

/// <summary>Track the info required to build a closure object + some context information not directly related to closure.</summary>
public struct CompilerContext
{
/// <summary>Tracks that the last emit was an address</summary>
public bool LastEmitIsAddress;

// Tracks the current block nesting count in the stack of blocks in Collect and Emit phase
private ushort _blockCount;
public struct CompilerContext
{
/// <summary>Tracks that the last emit was an address</summary>
public bool LastEmitIsAddress;

/// <summary>Tracks the expressions currently being emitted; the most recently entered expression is at the end.</summary>
internal SmallList<Expression, Stack16<Expression>, NoArrayPool<Expression>> EmitExpressions;

/// <summary>Tracks the parent flags for the corresponding entries in <see cref="EmitExpressions"/>.</summary>
internal SmallList<ParentFlags, Stack16<ParentFlags>, NoArrayPool<ParentFlags>> EmitParentFlags;

// Tracks the current block nesting count in the stack of blocks in Collect and Emit phase
private ushort _blockCount;

/// <summary>Tracks the use of the variables in the blocks stack per variable,
/// (uint) contains (ushort) BlockIndex in the upper bits and (ushort) VarIndex in the lower bits.
Expand Down Expand Up @@ -666,20 +672,63 @@ public struct CompilerContext
public CompilerFlags CompilerFlags;

/// <summary>Populates the info</summary>
public CompilerContext(ClosureStatus status, ParamExprs paramExprs, CompilerFlags compilerFlags)
{
Status = status;
Constants = new SmallList<object>();
LastEmitIsAddress = false;
public CompilerContext(ClosureStatus status, ParamExprs paramExprs, CompilerFlags compilerFlags)
{
Status = status;
Constants = new SmallList<object>();
LastEmitIsAddress = false;
CurrentInlinedLambdaInvokeIndex = -1;
ParamExprs = paramExprs;
CompilerFlags = compilerFlags;
}

/// <summary>Populates info directly with provided closure object and constants.</summary>
public CompilerContext(ClosureStatus status, object[] constValues, ParamExprs paramExprs, CompilerFlags compilerFlags)
{
Status = status;
ParamExprs = paramExprs;
CompilerFlags = compilerFlags;
}

/// <summary>The number of expressions in the current emit stack.</summary>
public int EmitExpressionCount
{
[MethodImpl((MethodImplOptions)256)]
get => EmitExpressions.Count;
}

/// <summary>The currently emitted expression or <c>null</c> when the emit stack is empty.</summary>
public Expression CurrentEmitExpression
{
[MethodImpl((MethodImplOptions)256)]
get => EmitExpressions.Count != 0 ? EmitExpressions.GetLastSurePresentItem() : null;
}

/// <summary>The parent flags for the currently emitted expression.</summary>
public ParentFlags CurrentParentFlags
{
[MethodImpl((MethodImplOptions)256)]
get => EmitParentFlags.Count != 0 ? EmitParentFlags.GetLastSurePresentItem() : ParentFlags.Empty;
}

/// <summary>Gets the current expression or its parent by zero-based distance from the current expression.</summary>
[MethodImpl((MethodImplOptions)256)]
public Expression GetEmitExpression(int indexFromCurrent = 0)
{
var exprIndex = EmitExpressions.Count - 1 - indexFromCurrent;
return exprIndex >= 0 ? EmitExpressions.GetSurePresentRef(exprIndex) : null;
}

[MethodImpl((MethodImplOptions)256)]
internal void PushEmitContext(Expression expr, ParentFlags parent)
{
EmitExpressions.Add(expr);
EmitParentFlags.Add(parent);
}

[MethodImpl((MethodImplOptions)256)]
internal void PopEmitContext()
{
EmitExpressions.RemoveLastSurePresentItem();
EmitParentFlags.RemoveLastSurePresentItem();
}

/// <summary>Populates info directly with provided closure object and constants.</summary>
public CompilerContext(ClosureStatus status, object[] constValues, ParamExprs paramExprs, CompilerFlags compilerFlags)
{
Status = status;

Constants = new SmallList<object>(constValues ?? Tools.Empty<object>());
if (constValues != null)
Expand All @@ -688,9 +737,9 @@ public CompilerContext(ClosureStatus status, object[] constValues, ParamExprs pa
LastEmitIsAddress = false;
CurrentInlinedLambdaInvokeIndex = -1;

ParamExprs = paramExprs;
CompilerFlags = compilerFlags;
}
ParamExprs = paramExprs;
CompilerFlags = compilerFlags;
}

[MethodImpl((MethodImplOptions)256)]
public bool ContainsConstantsOrNestedLambdas() => Constants.Count != 0 | NestedLambdas.Count != 0;
Expand Down Expand Up @@ -1890,22 +1939,25 @@ public static class EmittingVisitor
private static readonly MethodInfo _objectEqualsMethod =
((Func<object, object, bool>)object.Equals).Method;

public static bool TryEmit(Expression expr, ILGenerator il, ref CompilerContext context, ParentFlags parent, int byRefIndex = -1)
{
var exprType = expr.Type;
while (true)
{
context.LastEmitIsAddress = false;
#if LIGHT_EXPRESSION
if (expr.IsIntrinsic)
return expr.TryEmit(ref context, il, parent, byRefIndex);
#endif
var nodeType = expr.NodeType;
switch (nodeType)
{
case ExpressionType.Parameter:
return (parent & ParentFlags.IgnoreResult) != 0 ||
TryEmitParameter((ParameterExpression)expr, il, ref context, parent, byRefIndex);
public static bool TryEmit(Expression expr, ILGenerator il, ref CompilerContext context, ParentFlags parent, int byRefIndex = -1)
{
context.PushEmitContext(expr, parent);
try
{
var exprType = expr.Type;
while (true)
{
context.LastEmitIsAddress = false;
#if LIGHT_EXPRESSION
if (expr.IsIntrinsic)
return expr.TryEmit(ref context, il, byRefIndex);
#endif
var nodeType = expr.NodeType;
switch (nodeType)
{
case ExpressionType.Parameter:
return (parent & ParentFlags.IgnoreResult) != 0 ||
TryEmitParameter((ParameterExpression)expr, il, ref context, parent, byRefIndex);

case ExpressionType.TypeAs:
case ExpressionType.IsTrue:
Expand Down Expand Up @@ -2236,13 +2288,17 @@ public static bool TryEmit(Expression expr, ILGenerator il, ref CompilerContext
case ExpressionType.DebugInfo: // todo: @feature - is not supported yet
return true; // todo: @unclear - just ignoring the info for now

case ExpressionType.Quote: // todo: @feature - is not supported yet
default:
return false;

}
}
}
case ExpressionType.Quote: // todo: @feature - is not supported yet
default:
return false;
}
}
}
finally
{
context.PopEmitContext();
}
}

private static bool TryEmitNew(Expression expr, ILGenerator il, ref CompilerContext context, ParentFlags parent)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks Condition="'$(DevMode)' != 'true'">net472;net6.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks Condition="'$(DevMode)' == 'true'">net472;net9.0</TargetFrameworks>

<DefineConstants>LIGHT_EXPRESSION</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks Condition="'$(DevMode)' != 'true'">net472;net6.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks Condition="'$(DevMode)' == 'true'">net472;net9.0</TargetFrameworks>

<DefineConstants>LIGHT_EXPRESSION</DefineConstants>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\FastExpressionCompiler.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<ItemGroup>
<None Remove="*.ncrunchproject"/>
Expand Down
Loading
Loading