Files
UnrealEngine/Engine/Source/Programs/Shared/EpicGames.BuildGraph/Expressions/BgThunk.cs
2025-05-18 13:04:45 +08:00

149 lines
4.0 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
namespace EpicGames.BuildGraph.Expressions
{
/// <summary>
/// Thunks to a native method from within an expression. These objects contain an index into a thunk table that is not persisted to the bytecode.
/// </summary>
public class BgThunk : BgExpr
{
/// <summary>
/// Method to call
/// </summary>
public MethodInfo Method { get; }
/// <summary>
/// Arguments for the method
/// </summary>
public IReadOnlyList<object?> Arguments { get; }
/// <summary>
/// Creates a method closure from the given expression
/// </summary>
/// <param name="expr">Method call expression</param>
/// <returns></returns>
public static BgThunk Create(Expression<Func<Task>> expr)
{
MethodCallExpression call = (MethodCallExpression)expr.Body;
return new BgThunk(call);
}
/// <summary>
/// Creates a method closure from the given expression
/// </summary>
/// <typeparam name="TRet">Type of the return value</typeparam>
/// <param name="expr">Method call expression</param>
/// <returns></returns>
public static BgThunk<TRet> Create<TRet>(Expression<Func<Task<TRet>>> expr)
{
MethodCallExpression call = (MethodCallExpression)expr.Body;
return new BgThunk<TRet>(call);
}
/// <summary>
/// Creates a method closure from the given expression
/// </summary>
/// <typeparam name="TArg">Parameter to the method</typeparam>
/// <param name="expr">Method call expression</param>
/// <returns></returns>
public static BgThunk Create<TArg>(Expression<Func<TArg, Task>> expr)
{
MethodCallExpression call = (MethodCallExpression)expr.Body;
return new BgThunk(call);
}
/// <summary>
/// Creates a method closure from the given expression
/// </summary>
/// <typeparam name="TArg">Parameter to the method</typeparam>
/// <typeparam name="TRet">Type of the return value</typeparam>
/// <param name="expr">Method call expression</param>
/// <returns></returns>
public static BgThunk<TRet> Create<TArg, TRet>(Expression<Func<TArg, Task<TRet>>> expr)
{
MethodCallExpression call = (MethodCallExpression)expr.Body;
return new BgThunk<TRet>(call);
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="call">Call expression</param>
public BgThunk(MethodCallExpression call)
: base(BgExprFlags.None)
{
Method = call.Method;
Arguments = GetArguments(call);
}
static object?[] GetArguments(MethodCallExpression call)
{
object?[] args = new object?[call.Arguments.Count];
for (int idx = 0; idx < call.Arguments.Count; idx++)
{
Expression argumentExpr = call.Arguments[idx];
if (argumentExpr is ParameterExpression parameterExpr)
{
if (parameterExpr.Type != typeof(BgContext))
{
throw new BgNodeException($"Unable to determine type of parameter '{parameterExpr.Name}'");
}
}
else
{
Delegate compiled = Expression.Lambda(argumentExpr).Compile();
args[idx] = compiled.DynamicInvoke();
}
}
return args;
}
/// <inheritdoc/>
public override void Write(BgBytecodeWriter writer)
{
writer.WriteOpcode(BgOpcode.Thunk);
writer.WriteThunk(new BgThunkDef(Method, Arguments));
ParameterInfo[] parameters = Method.GetParameters();
writer.WriteUnsignedInteger(parameters.Length);
for (int idx = 0; idx < parameters.Length; idx++)
{
if (typeof(BgExpr).IsAssignableFrom(parameters[idx].ParameterType))
{
writer.WriteExpr((BgExpr)Arguments[idx]!);
}
else
{
writer.WriteOpcode(BgOpcode.Null);
}
}
}
/// <inheritdoc/>
public override BgString ToBgString() => "{Method}";
}
/// <summary>
/// Wraps a native method that returns a value
/// </summary>
/// <typeparam name="T"></typeparam>
public class BgThunk<T> : BgThunk
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="expr"></param>
public BgThunk(MethodCallExpression expr)
: base(expr)
{
}
}
}