using System.CodeDom; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Unity.VisualScripting { [AotStubWriter(typeof(MethodInfo))] public class MethodInfoStubWriter : MethodBaseStubWriter { public MethodInfoStubWriter(MethodInfo methodInfo) : base(methodInfo) { } public override IEnumerable GetStubStatements() { /* * Required output: * 1. Create a target expression * 2. Call its method with the correct number of args * 3. Call its optimized method with the correct number of args * 4. Call its optimized method with an args array */ var targetType = new CodeTypeReference(manipulator.targetType, CodeTypeReferenceOptions.GlobalReference); var declaringType = new CodeTypeReference(stub.DeclaringType, CodeTypeReferenceOptions.GlobalReference); CodeExpression targetValue; CodeExpression targetReference; if (manipulator.requiresTarget && !manipulator.isExtension) { // default(Material) targetValue = new CodeDefaultValueExpression(targetType); // 1. Material target = default(Material); yield return new CodeVariableDeclarationStatement(targetType, "target", targetValue); targetReference = new CodeVariableReferenceExpression("target"); } else { // null targetValue = new CodePrimitiveExpression(null); if (manipulator.isExtension) { // 1. ShortcutExtensions targetReference = new CodeTypeReferenceExpression(declaringType); } else { // 1. Material targetReference = new CodeTypeReferenceExpression(targetType); } } // target.SetColor var methodReference = new CodeMethodReferenceExpression(targetReference, manipulator.name); var arguments = new List(); var includesOutOrRef = false; foreach (var parameterInfo in stub.GetParameters()) { var parameterType = new CodeTypeReference(parameterInfo.UnderlyingParameterType(), CodeTypeReferenceOptions.GlobalReference); var argumentName = $"arg{arguments.Count}"; // arg0 = default(string) // arg1 = default(Color) yield return new CodeVariableDeclarationStatement(parameterType, argumentName, new CodeDefaultValueExpression(parameterType)); FieldDirection direction; if (parameterInfo.HasOutModifier()) { direction = FieldDirection.Out; includesOutOrRef = true; } else if (parameterInfo.ParameterType.IsByRef && !parameterInfo.IsIn) { direction = FieldDirection.Ref; includesOutOrRef = true; } else { direction = FieldDirection.In; } var argument = new CodeDirectionExpression(direction, new CodeVariableReferenceExpression(argumentName)); arguments.Add(argument); } if (operatorTypes.ContainsKey(manipulator.name)) { // arg0 * arg1 var operation = new CodeBinaryOperatorExpression(arguments[0], operatorTypes[manipulator.name], arguments[1]); // 2. var operator = arg0 * arg1; yield return new CodeVariableDeclarationStatement(manipulator.type, "operator", operation); } else if (manipulator.isConversion) { // (Vector3)arg0 var cast = new CodeCastExpression(manipulator.type, arguments[0]); // 2. var conversion = (Vector3)arg0; yield return new CodeVariableDeclarationStatement(manipulator.type, "conversion", cast); } else if (manipulator.isPubliclyInvocable && !(manipulator.isOperator || manipulator.isConversion)) { // 2. target.SetColor(arg0, arg1); yield return new CodeExpressionStatement(new CodeMethodInvokeExpression(methodReference, arguments.ToArray())); } var optimizedInvokerType = new CodeTypeReference(stub.Prewarm().GetType(), CodeTypeReferenceOptions.GlobalReference); // var invoker = new InstanceActionInvoker(default(MethodInfo)); yield return new CodeVariableDeclarationStatement(optimizedInvokerType, "optimized", new CodeObjectCreateExpression(optimizedInvokerType, new CodeDefaultValueExpression(new CodeTypeReference(typeof(MethodInfo), CodeTypeReferenceOptions.GlobalReference)))); // [default(Material), arg0, arg1] var argumentsWithTarget = targetValue.Yield().Concat(arguments).ToArray(); // Ref and out parameters are not supported in the numbered argument signatures if (!includesOutOrRef) { // 3. invoker.Invoke(default(Material), arg0, arg1); yield return new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("optimized"), nameof(IOptimizedInvoker.Invoke), argumentsWithTarget)); } // 4. invoker.Invoke(default(Material), default(object[])); yield return new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("optimized"), nameof(IOptimizedInvoker.Invoke), new CodeDefaultValueExpression(new CodeTypeReference(typeof(object[]))))); } public static readonly Dictionary operatorTypes = new Dictionary { { "op_Addition", CodeBinaryOperatorType.Add }, { "op_Subtraction", CodeBinaryOperatorType.Subtract }, { "op_Multiply", CodeBinaryOperatorType.Multiply }, { "op_Division", CodeBinaryOperatorType.Divide }, { "op_Modulus", CodeBinaryOperatorType.Modulus }, { "op_BitwiseAnd", CodeBinaryOperatorType.BitwiseAnd }, { "op_BitwiseOr", CodeBinaryOperatorType.BitwiseOr }, { "op_LogicalAnd", CodeBinaryOperatorType.BooleanAnd }, { "op_LogicalOr", CodeBinaryOperatorType.BooleanOr }, { "op_Assign", CodeBinaryOperatorType.Assign }, { "op_Equality", CodeBinaryOperatorType.IdentityEquality }, { "op_GreaterThan", CodeBinaryOperatorType.GreaterThan }, { "op_LessThan", CodeBinaryOperatorType.LessThan }, { "op_Inequality", CodeBinaryOperatorType.IdentityInequality }, { "op_GreaterThanOrEqual", CodeBinaryOperatorType.GreaterThanOrEqual }, { "op_LessThanOrEqual", CodeBinaryOperatorType.LessThanOrEqual } }; } }