// 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) { } } }