Пример #1
0
        /// <summary>
        /// Parses the instruction.
        /// </summary>
        /// <param name="mainProcessor">The processor.</param>
        /// <param name="asmCollection">The papyrus assembly collection.</param>
        /// <param name="instruction">The instruction.</param>
        /// <param name="targetMethod">The target method.</param>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        /// <exception cref="MissingVariableException"></exception>
        public IEnumerable <PapyrusInstruction> Process(
            IClrInstructionProcessor mainProcessor,
            IReadOnlyCollection <PapyrusAssemblyDefinition> asmCollection,
            Instruction instruction,
            MethodDefinition targetMethod, TypeDefinition type)
        {
            bool isStructAccess;
            var  outputInstructions = new List <PapyrusInstruction>();

            if (InstructionHelper.NextInstructionIs(instruction, Code.Ldnull) &&
                InstructionHelper.NextInstructionIs(instruction.Next, Code.Cgt_Un))
            {
                var stack = mainProcessor.EvaluationStack;
                //var itemToCheck = stack.Pop().Value;
                var itemToCheck = instruction.Operand as FieldReference;

                if (itemToCheck != null)
                {
                    mainProcessor.EvaluationStack.Push(
                        new EvaluationStackItem
                    {
                        Value    = itemToCheck,
                        TypeName = ""
                    }
                        );
                    mainProcessor.EvaluationStack.Push(
                        new EvaluationStackItem
                    {
                        Value    = null,
                        TypeName = "none"
                    }
                        );

                    mainProcessor.SkipToOffset =
                        InstructionHelper.NextInstructionIsOffset(instruction.Next, Code.Cgt_Un) - 1;

                    //bool structAccess;
                    //var targetVar = mainInstructionProcessor.GetTargetVariable(tarInstruction, null, out structAccess, "Bool");

                    //if (mainInstructionProcessor.SkipNextInstruction)
                    //{
                    //    mainInstructionProcessor.SkipToOffset += 2;
                    //    mainInstructionProcessor.SkipNextInstruction = false;
                    //}

                    //outputInstructions.Add(mainInstructionProcessor.CreatePapyrusInstruction(PapyrusOpCodes.Is,
                    //    mainInstructionProcessor.CreateVariableReferenceFromName(targetVar), fieldDef,
                    //    mainInstructionProcessor.CreateVariableReferenceFromName(typeToCheckAgainst.Name)));
                    return(outputInstructions);
                }
            }

            if (InstructionHelper.IsLoadMethodRef(instruction.OpCode.Code))
            {
                // Often used for delegates or Action/func parameters, when loading a reference pointer to a method and pushing it to the stack.
                // To maintain the evaluation stack, this could add a dummy item, but for now im not going to do so.
            }

            if (InstructionHelper.IsLoadLength(instruction.OpCode.Code))
            {
                var popCount = Utility.GetStackPopCount(instruction.OpCode.StackBehaviourPop);
                if (mainProcessor.EvaluationStack.Count >= popCount)
                {
                    var val = mainProcessor.EvaluationStack.Pop();
                    if (val.TypeName.EndsWith("[]"))
                    {
                        if (val.Value is PapyrusPropertyDefinition)
                        {
                            // for now, so if this exception is thrown, i will have to remember I have to fix it.
                            throw new NotImplementedException();
                        }

                        if (val.Value is PapyrusVariableReference || val.Value is PapyrusFieldDefinition ||
                            val.Value is PapyrusParameterDefinition)
                        {
                            int variableIndex;

                            var storeInstruction =
                                mainProcessor.GetNextStoreLocalVariableInstruction(instruction,
                                                                                   out variableIndex);

                            if (storeInstruction != null ||
                                InstructionHelper.IsConverToNumber(instruction.Next.OpCode.Code))
                            {
                                if (InstructionHelper.IsConverToNumber(instruction.Next.OpCode.Code))
                                {
                                    mainProcessor.SkipNextInstruction = false;
                                    mainProcessor.SkipToOffset        = 0;

                                    var targetVariableName = mainProcessor.GetTargetVariable(instruction,
                                                                                             null, out isStructAccess, "Int", true);

                                    var allVars        = mainProcessor.PapyrusMethod.GetVariables();
                                    var targetVariable = allVars.FirstOrDefault(v => v.Name.Value == targetVariableName);

                                    if (targetVariable == null &&
                                        mainProcessor.PapyrusCompilerOptions == PapyrusCompilerOptions.Strict)
                                    {
                                        throw new MissingVariableException(targetVariableName);
                                    }
                                    if (targetVariable != null)
                                    {
                                        mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                                        {
                                            TypeName = targetVariable.TypeName.Value,
                                            Value    = targetVariable
                                        });

                                        outputInstructions.Add(
                                            mainProcessor.CreatePapyrusInstruction(
                                                PapyrusOpCodes.ArrayLength,
                                                mainProcessor.CreateVariableReference(
                                                    PapyrusPrimitiveType.Reference, targetVariableName), val.Value));
                                    }
                                }
                                else
                                {
                                    var allVars = mainProcessor.PapyrusMethod.GetVariables();

                                    mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                                    {
                                        TypeName = allVars[variableIndex].TypeName.Value,
                                        Value    = allVars[variableIndex]
                                    });

                                    outputInstructions.Add(
                                        mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.ArrayLength,
                                                                               allVars[variableIndex], val.Value));

                                    //return "ArrayLength " + allVars[variableIndex].Name + " " +
                                    //       (val.Value as VariableReference).Name;
                                }
                            }
                        }
                    }
                }
                // ArrayLength <outputVariableName> <arrayName>
            }


            if (InstructionHelper.IsLoadArgs(instruction.OpCode.Code))
            {
                var index = (int)mainProcessor.GetNumericValue(instruction);
                if (targetMethod.IsStatic && index == 0 && targetMethod.Parameters.Count == 0)
                {
                    mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                    {
                        IsThis   = true,
                        Value    = type,
                        TypeName = type.FullName
                    });
                }
                else
                {
                    if (targetMethod.HasThis && index == 0)
                    {
                        return(outputInstructions);
                    }

                    if (!targetMethod.IsStatic && index > 0)
                    {
                        index--;
                    }
                    if (index < mainProcessor.PapyrusMethod.Parameters.Count)
                    {
                        mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                        {
                            Value    = mainProcessor.PapyrusMethod.Parameters[index],
                            TypeName = mainProcessor.PapyrusMethod.Parameters[index].TypeName.Value
                        });
                    }
                }
            }
            if (InstructionHelper.IsLoadInteger(instruction.OpCode.Code))
            {
                var index = mainProcessor.GetNumericValue(instruction);
                mainProcessor.EvaluationStack.Push(new EvaluationStackItem {
                    Value = index, TypeName = "Int"
                });
            }

            if (InstructionHelper.IsLoadNull(instruction.OpCode.Code))
            {
                mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                {
                    Value    = "None",
                    TypeName = "None"
                });
            }

            if (InstructionHelper.IsLoadLocalVariable(instruction.OpCode.Code))
            {
                var index        = (int)mainProcessor.GetNumericValue(instruction);
                var allVariables = mainProcessor.PapyrusMethod.GetVariables();
                if (index < allVariables.Count)
                {
                    mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                    {
                        Value    = allVariables[index],
                        TypeName = allVariables[index].TypeName.Value
                    });
                }
            }

            if (InstructionHelper.IsLoadString(instruction.OpCode.Code))
            {
                var value = StringUtility.AsString(instruction.Operand);

                mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                {
                    Value    = value,
                    TypeName = "String"
                });
            }

            if (InstructionHelper.IsLoadFieldObject(instruction.OpCode.Code))
            {
                if (instruction.Operand is FieldReference)
                {
                    var fieldRef = instruction.Operand as FieldReference;


                    PapyrusFieldDefinition targetField = null;

                    targetField = mainProcessor.PapyrusType.Fields.FirstOrDefault(
                        f => f.Name.Value == "::" + fieldRef.Name.Replace('<', '_').Replace('>', '_'));

                    if (targetField == null)
                    {
                        targetField = mainProcessor.GetDelegateField(fieldRef);
                    }


                    if (targetField != null)
                    {
                        mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                        {
                            Value    = targetField,
                            TypeName = targetField.TypeName
                        });
                    }

                    if (InstructionHelper.PreviousInstructionWas(instruction, Code.Ldflda) &&
                        fieldRef.FullName.Contains("/"))
                    {
                        var targetStructVariable = mainProcessor.EvaluationStack.Pop().Value;

                        var structRef = new PapyrusStructFieldReference(mainProcessor.PapyrusAssembly, null)
                        {
                            StructSource   = targetStructVariable,
                            StructVariable = mainProcessor.CreateVariableReferenceFromName(fieldRef.Name)
                        };

                        mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                        {
                            Value    = structRef,
                            TypeName = "$StructAccess$"
                        });

                        return(outputInstructions);

                        //    // The target field is not inside the declared type.
                        //    // Most likely, this is a get field from struct.
                        //    if (fieldRef.FieldType.FullName.Contains("/"))
                        //    {
                        //        var location = fieldRef.FieldType.FullName.Split("/").LastOrDefault();

                        //        var targetStruct = mainInstructionProcessor.PapyrusType.NestedTypes.FirstOrDefault(n => n.Name.Value == location);
                        //        if (targetStruct != null)
                        //        {

                        //            targetField = mainInstructionProcessor.PapyrusType.Fields.FirstOrDefault(
                        //                f => f.Name.Value == "::" + fieldRef.Name);
                        //            // var stack = mainInstructionProcessor.EvaluationStack;
                        //            // TODO: Add support for getting values from Structs
                        //            //
                        //            // CreatePapyrusInstruction(PapyrusOpCode.StructGet, ...)
                        //        }
                        //    }
                    }
                    //else
                    //{
                    //    targetField = mainInstructionProcessor.PapyrusType.Fields.FirstOrDefault(
                    //        f => f.Name.Value == "::" + fieldRef.Name.Replace('<', '_').Replace('>', '_'));
                    //}
                }
            }


            if (InstructionHelper.IsLoadElement(instruction.OpCode.Code))
            {
                // TODO: Load Element (Arrays, and what not)
                var popCount = Utility.GetStackPopCount(instruction.OpCode.StackBehaviourPop);
                if (mainProcessor.EvaluationStack.Count >= popCount)
                {
                    var itemIndex = mainProcessor.EvaluationStack.Pop();
                    var itemArray = mainProcessor.EvaluationStack.Pop();

                    object targetItemIndex = null;

                    var targetItemArray = itemArray.Value;

                    if (itemIndex.Value != null)
                    {
                        targetItemIndex = itemIndex.Value;
                    }

                    // 128 is the array size limit for Skyrim
                    if (mainProcessor.PapyrusAssembly.VersionTarget == PapyrusVersionTargets.Skyrim)
                    {
                        if ((targetItemIndex as int?) > 128)
                        {
                            targetItemIndex = 128;
                        }
                    }

                    // We want to use the Array Element together with a Method Call?
                    var isBoxing = InstructionHelper.IsBoxing(instruction.Next.OpCode.Code);
                    if (isBoxing || InstructionHelper.IsCallMethod(instruction.Next.OpCode.Code))
                    {
                        if (isBoxing)
                        {
                            var sourceArray = targetItemArray as PapyrusVariableReference;
                            if (sourceArray != null)
                            {
                                // Since we apply our logic necessary for this "boxing" right here
                                // we can skip the next instruction to avoid unexpected behaviour.
                                mainProcessor.SkipNextInstruction = true;

                                // Create a new Temp Variable
                                // Assign our value to this temp variable and push it to the stack
                                // so that the next instruction can take care of it.
                                var tempVariableType = sourceArray.TypeName.Value.Replace("[]", "");

                                var destinationTempVar = mainProcessor.GetTargetVariable(instruction, null,
                                                                                         out isStructAccess, tempVariableType, true);

                                var varRef =
                                    mainProcessor.PapyrusMethod.GetVariables()
                                    .FirstOrDefault(n => n.Name.Value == destinationTempVar);

                                mainProcessor.EvaluationStack.Push(new EvaluationStackItem
                                {
                                    Value = varRef ?? (object)destinationTempVar,
                                    // Should be the actual variable reference
                                    TypeName = tempVariableType
                                });

                                outputInstructions.Add(
                                    mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.ArrayGetElement,
                                                                           mainProcessor.CreateVariableReference(
                                                                               PapyrusPrimitiveType.Reference, destinationTempVar),
                                                                           targetItemArray,
                                                                           targetItemIndex));
                            }
                        }
                        else
                        {
                            // Get the method reference and then create a temp variable that
                            // we can use for assigning the value to.
                            var methodRef = instruction.Next.Operand as MethodReference;
                            if (methodRef != null && methodRef.HasParameters)
                            {
                                var sourceArray = targetItemArray as PapyrusVariableReference;
                                if (sourceArray != null)
                                {
                                    var tempVariableType   = sourceArray.TypeName.Value.Replace("[]", "");
                                    var destinationTempVar = mainProcessor.GetTargetVariable(instruction,
                                                                                             methodRef, out isStructAccess,
                                                                                             tempVariableType);

                                    // "ArrayGetElement " + destinationTempVar + " " + targetItemArray + " " + targetItemIndex;
                                    outputInstructions.Add(
                                        mainProcessor.CreatePapyrusInstruction(
                                            PapyrusOpCodes.ArrayGetElement,
                                            mainProcessor.CreateVariableReference(
                                                PapyrusPrimitiveType.Reference, destinationTempVar),
                                            targetItemArray,
                                            targetItemIndex));
                                }
                            }
                        }
                    }
                    else
                    {
                        // Otherwise we just want to store it somewhere.
                        int destinationVariableIndex;
                        // Get the target variable by finding the next store instruction and returning the variable index.
                        mainProcessor.GetNextStoreLocalVariableInstruction(instruction,
                                                                           out destinationVariableIndex);
                        var destinationVar =
                            mainProcessor.PapyrusMethod.GetVariables()[destinationVariableIndex];

                        // ArrayGetElement targetVariable targetItemArray targetItemIndex
                        outputInstructions.Add(
                            mainProcessor.CreatePapyrusInstruction(PapyrusOpCodes.ArrayGetElement,
                                                                   destinationVar,
                                                                   targetItemArray,
                                                                   targetItemIndex)
                            );
                    }
                }
            }
            return(outputInstructions);
        }