Beispiel #1
0
        private void InsertSetMethodCallIntoPropertySetter(
            PropertyDefinition propertyDefinition,
            MethodReference methodReference,
            ICustomAttribute attribute)
        {
            TypeReference methodDeclaringType = methodReference.DeclaringType;

            MethodBody methodBody = propertyDefinition.SetMethod.Body;
            Collection <Instruction> instructions = methodBody.Instructions;

            FieldReference     backingField      = propertyDefinition.FindBackingField();
            List <Instruction> storeInstructions = instructions.Where(
                instruction => instruction.OpCode == OpCodes.Stfld &&
                (instruction.Operand as FieldReference)?.FullName == backingField.FullName)
                                                   .ToList();

            foreach (Instruction storeInstruction in storeInstructions)
            {
                Instruction targetInstruction;
                int         instructionIndex;
                bool        needsPlayingCheck          = _isCompilingForEditor;
                bool        needsActiveAndEnabledCheck = methodDeclaringType.Resolve().IsSubclassOf(_behaviourReference);

                /*
                 * if (Application.isPlaying)                         // Only if compiling for Editor
                 * {
                 *   if (this.isActiveAndEnabled)                   // Only if in a Behaviour
                 *   {
                 *       this.Method();
                 *   }
                 * }
                 */

                bool IsPlayingCheck(Instruction instruction) =>
                instruction.OpCode == OpCodes.Call &&
                (instruction.Operand as MethodReference)?.FullName
                == _isApplicationPlayingGetterReference.FullName;

                bool IsActiveAndEnabledCheck(Instruction instruction) =>
                instruction.OpCode == OpCodes.Call &&
                (instruction.Operand as MethodReference)?.FullName
                == _isActiveAndEnabledGetterReference.FullName;

                if (attribute.AttributeType.FullName == _fullBeforeChangeAttributeName)
                {
                    targetInstruction = storeInstruction.Previous.Previous;
                    instructionIndex  = instructions.IndexOf(targetInstruction) - 1;

                    Instruction testInstruction = targetInstruction.Previous;

                    void TryFindExistingCheck(ref bool needsCheck, Func <Instruction, bool> predicate)
                    {
                        if (!needsCheck)
                        {
                            return;
                        }

                        while (testInstruction != null)
                        {
                            if (predicate(testInstruction))
                            {
                                needsCheck = false;
                                return;
                            }

                            testInstruction = testInstruction.Previous;
                        }

                        while (testInstruction != null &&
                               (testInstruction.OpCode == OpCodes.Brfalse ||
                                testInstruction.OpCode == OpCodes.Brfalse_S))
                        {
                            testInstruction = testInstruction.Next;
                        }

                        if (testInstruction != null)
                        {
                            instructionIndex = instructions.IndexOf(testInstruction) - 1;
                        }
                    }

                    TryFindExistingCheck(ref needsActiveAndEnabledCheck, IsActiveAndEnabledCheck);
                    TryFindExistingCheck(ref needsPlayingCheck, IsPlayingCheck);
                }
                else
                {
                    targetInstruction = storeInstruction.Next;
                    instructionIndex  = instructions.IndexOf(targetInstruction) - 1;

                    Instruction testInstruction = storeInstruction.Next;

                    void TryFindExistingCheck(ref bool needsCheck, Func <Instruction, bool> predicate)
                    {
                        if (!needsCheck)
                        {
                            return;
                        }

                        while (testInstruction != null)
                        {
                            if (predicate(testInstruction))
                            {
                                needsCheck       = false;
                                instructionIndex = instructions.IndexOf(testInstruction) + 1;
                                return;
                            }

                            testInstruction = testInstruction.Next;
                        }
                    }

                    TryFindExistingCheck(ref needsPlayingCheck, IsPlayingCheck);
                    TryFindExistingCheck(ref needsActiveAndEnabledCheck, IsActiveAndEnabledCheck);
                }

                if (needsPlayingCheck)
                {
                    // Call Application.isPlaying getter
                    instructions.Insert(
                        ++instructionIndex,
                        Instruction.Create(OpCodes.Call, _isApplicationPlayingGetterReference));
                    // Bail out if false
                    instructions.Insert(++instructionIndex, Instruction.Create(OpCodes.Brfalse, targetInstruction));
                }

                if (needsActiveAndEnabledCheck)
                {
                    // Load this (for getter call)
                    instructions.Insert(++instructionIndex, Instruction.Create(OpCodes.Ldarg_0));
                    // Call Behaviour.isActiveAndEnabled getter
                    instructions.Insert(
                        ++instructionIndex,
                        Instruction.Create(OpCodes.Call, _isActiveAndEnabledGetterReference));
                    // Bail out if false
                    instructions.Insert(++instructionIndex, Instruction.Create(OpCodes.Brfalse, targetInstruction));
                }

                TypeDefinition definitionDeclaringType = propertyDefinition.DeclaringType;
                if (methodDeclaringType.FullName != definitionDeclaringType.FullName)
                {
                    MethodDefinition baseMethodDefinition =
                        definitionDeclaringType.Methods.FirstOrDefault(
                            definition => definition.FullName == methodReference.FullName);
                    if (baseMethodDefinition == null)
                    {
                        MethodDefinition methodDefinition = methodReference.Resolve();
                        baseMethodDefinition = new MethodDefinition(
                            methodReference.Name,
                            methodDefinition.Attributes,
                            methodReference.ReturnType);
                        baseMethodDefinition.CustomAttributes.Add(
                            new CustomAttribute(_compilerGeneratedAttributeConstructorReference));
                        definitionDeclaringType.Methods.Add(baseMethodDefinition);

                        // Return
                        baseMethodDefinition.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));

                        if (!baseMethodDefinition.IsFamily)
                        {
                            baseMethodDefinition.IsFamily = true;
                        }

                        if (!baseMethodDefinition.IsVirtual && !baseMethodDefinition.IsNewSlot)
                        {
                            baseMethodDefinition.IsNewSlot = true;
                        }

                        baseMethodDefinition.IsFinal     = false;
                        baseMethodDefinition.IsVirtual   = true;
                        baseMethodDefinition.IsHideBySig = true;

                        methodDefinition.IsPrivate           = baseMethodDefinition.IsPrivate;
                        methodDefinition.IsFamily            = baseMethodDefinition.IsFamily;
                        methodDefinition.IsFamilyAndAssembly = baseMethodDefinition.IsFamilyAndAssembly;

                        methodDefinition.IsFinal     = false;
                        methodDefinition.IsVirtual   = true;
                        methodDefinition.IsNewSlot   = false;
                        methodDefinition.IsReuseSlot = true;
                        methodDefinition.IsHideBySig = true;

                        LogInfo(
                            $"Changed the method '{methodDefinition.FullName}' to override the"
                            + $" newly added base method '{baseMethodDefinition.FullName}'.");
                    }

                    methodReference = baseMethodDefinition;
                }

                // Load this (for method call)
                instructions.Insert(++instructionIndex, Instruction.Create(OpCodes.Ldarg_0));
                // Call method
                instructions.Insert(
                    ++instructionIndex,
                    Instruction.Create(OpCodes.Callvirt, methodReference.CreateGenericMethodIfNeeded()));

                methodBody.OptimizeMacros();

                LogInfo(
                    $"Inserted a call to the method '{methodReference.FullName}' into"
                    + $" the setter of the property '{propertyDefinition.FullName}'.");
            }
        }