// 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
{
///
/// 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.
///
public class BgThunk : BgExpr
{
///
/// Method to call
///
public MethodInfo Method { get; }
///
/// Arguments for the method
///
public IReadOnlyList Arguments { get; }
///
/// Creates a method closure from the given expression
///
/// Method call expression
///
public static BgThunk Create(Expression> expr)
{
MethodCallExpression call = (MethodCallExpression)expr.Body;
return new BgThunk(call);
}
///
/// Creates a method closure from the given expression
///
/// Type of the return value
/// Method call expression
///
public static BgThunk Create(Expression>> expr)
{
MethodCallExpression call = (MethodCallExpression)expr.Body;
return new BgThunk(call);
}
///
/// Creates a method closure from the given expression
///
/// Parameter to the method
/// Method call expression
///
public static BgThunk Create(Expression> expr)
{
MethodCallExpression call = (MethodCallExpression)expr.Body;
return new BgThunk(call);
}
///
/// Creates a method closure from the given expression
///
/// Parameter to the method
/// Type of the return value
/// Method call expression
///
public static BgThunk Create(Expression>> expr)
{
MethodCallExpression call = (MethodCallExpression)expr.Body;
return new BgThunk(call);
}
///
/// Constructor
///
/// Call expression
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;
}
///
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);
}
}
}
///
public override BgString ToBgString() => "{Method}";
}
///
/// Wraps a native method that returns a value
///
///
public class BgThunk : BgThunk
{
///
/// Constructor
///
///
public BgThunk(MethodCallExpression expr)
: base(expr)
{
}
}
}