コード例 #1
0
        // void WrapMethodInTryCatch(MethodDefinition method)
        // {
        //     var il = method.Body.GetILProcessor();
        //     var top = method.Body.Instructions.First();
        //     var ret = method.Body.Instructions.Last();
        //     var leaveTry = Create(OpCodes.Leave, ret);
        //     var leaveCatch = Create(OpCodes.Leave, ret);
        //     var ldthis = Create(OpCodes.Ldarg_0);
        //     var logException = MethodCallInstruction(typeof(UnityEngine.Debug).GetMethod(nameof(UnityEngine.Debug.LogException), new[] { typeof(System.Exception), typeof(UnityEngine.Object) }));
        //     il.InsertBefore(ret, leaveTry);
        //     il.InsertBefore(ret, ldthis);
        //     il.InsertBefore(ret, logException);
        //     il.InsertBefore(ret, leaveCatch);
        //     var handler = new ExceptionHandler(ExceptionHandlerType.Catch)
        //     {
        //         TryStart = top,
        //         TryEnd = ldthis,
        //         HandlerStart = ldthis,
        //         HandlerEnd = ret,
        //         CatchType = module.ImportReference(typeof(System.Exception)),
        //     };
        //     il.Body.ExceptionHandlers.Add(handler);
        // }

        void InsertInjectionCall(MethodDefinition method, FieldDefinition field, CustomAttribute attribute)
        {
            var  il         = method.Body.GetILProcessor();
            var  top        = method.Body.Instructions[0];
            bool IsOptional = attribute.PropertyEquals(key: nameof(Inject.Optional), value: true);

            TypeReference UnwrapArrayType(TypeReference type)
            => type.IsArray ? type.GetElementType() : type;

            var elementType = field.FieldType;

            var(resolveMethodName, useGenericMethodCall, canBeSerialized) = GetNameOfResolveMethodInfo(ref elementType, attribute);
            elementType = UnwrapArrayType(elementType);

            var hasSerializeFieldAttribute = field.HasAttribute <UnityEngine.SerializeField>();

            if (!canBeSerialized && hasSerializeFieldAttribute)
            {
                throw new System.NotSupportedException($"Field of type {field.FieldType.Name} marked with [{attribute.AttributeType.Name}] should not be serialized.");
            }

            if (hasSerializeFieldAttribute)
#if ODIN_INSPECTOR
            { field.AddAttribute <Sirenix.OdinInspector.ReadOnlyAttribute>(module); }
#else
            { field.AddAttribute <ReadOnlyAttribute>(module); }
#endif

            Instruction first, last;
            {
                il.InsertBefore(top, first = Create(OpCodes.Ldarg_0));
                // store field
                {
                    il.InsertBefore(top, Create(OpCodes.Ldarg_0));
                    if (useGenericMethodCall)
                    {
                        il.InsertBefore(top, Create(OpCodes.Ldarg_0));
                        il.InsertBefore(top, Create(OpCodes.Call, module.ImportReference(typeof(Internal.InjectHelpers).GetMethod(resolveMethodName, flags)).MakeGenericInstance(elementType)));
                    }
                    else
                    {
                        // load dependency resolution result
                        {
                            il.InsertBefore(top, Create(OpCodes.Ldarg_0));
                            // load type
                            {
                                il.InsertBefore(top, Create(OpCodes.Ldtoken, module.ImportReference(elementType)));
                                il.InsertBefore(top, MethodCallInstruction(typeof(System.Type).GetMethod(nameof(System.Type.GetTypeFromHandle), flags)));
                            }
                            il.InsertBefore(top, MethodCallInstruction(typeof(Internal.InjectHelpers).GetMethod(resolveMethodName, flags)));
                        }
                        if (!field.FieldType.IsValueType)
                        {
                            bool isInterface; // enable interface serialization
                            try
                            {
                                isInterface = field.FieldType.Resolve().IsInterface;
                            }
                            catch
                            {
                                isInterface = false;
                            }
                            if (isInterface)
                            {
                                il.InsertBefore(top, Create(OpCodes.Isinst, module.ImportReference(typeof(UnityEngine.Object))));
                            }
                            else
                            {
                                il.InsertBefore(top, Create(OpCodes.Isinst, field.FieldType));
                            }
                        }
                    }
                    il.InsertBefore(top, Create(OpCodes.Stfld, field));
                }
                // no-op (jump label)
                il.InsertBefore(top, last = Create(OpCodes.Nop));
                if (!IsOptional && !field.FieldType.IsValueType)
                {
                    il.InsertBefore(last, Create(OpCodes.Ldfld, field));
                    il.InsertBefore(last, Create(OpCodes.Brtrue_S, last));
                    il.InsertBefore(last, Create(OpCodes.Ldstr,
                                                 FormatLog($"Failed to initialize @*|{field.FieldType.Name} {field.Name}|*@ in component @*|{method.DeclaringType.FullName}|*@.\n|Component injection using *[{attribute.AttributeType.FullName.Replace('/', '.')}]*|"))
                                    );
                    il.InsertBefore(last, Create(OpCodes.Ldarg_0));
                    if (hasSerializeFieldAttribute)
                    {
                        il.InsertBefore(last, MethodCallInstruction(typeof(UnityEngine.Debug).GetMethod(nameof(UnityEngine.Debug.LogError), new[] { typeof(string), typeof(UnityEngine.Object) })));
                    }
                    else
                    {
                        il.InsertBefore(last, MethodCallInstruction(typeof(UnityEngine.Debug).GetMethod(nameof(UnityEngine.Debug.LogWarning), new[] { typeof(string), typeof(UnityEngine.Object) })));
                    }
                }
            }

            // isdirty editor injection guard
            if (hasSerializeFieldAttribute)
            {
                il.InsertBefore(first, Create(OpCodes.Ldarg_0));
                il.InsertBefore(first, MethodCallInstruction(typeof(Internal.InjectHelpers).GetMethod(nameof(ShouldUpdateInjectedComponents), flags)));
                il.InsertBefore(first, Create(OpCodes.Brfalse, last));
            }

            WrapBlockInTryCatch(il, first, last);
        }