/// <summary> /// Creates a papyrus call instruction. /// </summary> /// <param name="mainInstructionProcessor">The main instruction processor.</param> /// <param name="callOpCode">The call op code.</param> /// <param name="methodRef">The method reference.</param> /// <param name="callerLocation">The caller location.</param> /// <param name="destinationVariable">The destination variable.</param> /// <param name="parameters">The parameters.</param> /// <param name="structGets">The structure gets.</param> /// <param name="methodName">Name of the method.</param> /// <returns></returns> public PapyrusInstruction CreatePapyrusCallInstruction( IClrInstructionProcessor mainInstructionProcessor, PapyrusOpCodes callOpCode, MethodReference methodRef, string callerLocation, string destinationVariable, List <object> parameters, out List <PapyrusInstruction> structGets, string methodName = null) { structGets = new List <PapyrusInstruction>(); var inst = new PapyrusInstruction { OpCode = callOpCode }; var param = parameters.ToArray(); for (var index = 0; index < param.Length; index++) { var p = param[index]; PapyrusStructFieldReference structRef = null; var evalItem = p as EvaluationStackItem; if (evalItem != null) { structRef = evalItem.Value as PapyrusStructFieldReference; } if (structRef == null) { structRef = p as PapyrusStructFieldReference; } if (structRef != null) { var structSource = structRef.StructSource as PapyrusFieldDefinition; var structField = structRef.StructVariable; var fieldType = GetStructFieldType(papyrusAssemblyCollection, structSource, structField); // 1. Create Temp Var bool isStructAccess; var tempVar = mainInstructionProcessor.GetTargetVariable(currentInstruction, null, out isStructAccess, fieldType, true); param[index] = mainInstructionProcessor.CreateVariableReferenceFromName(tempVar); // 2. StructGet -> tempVar // 3. Assign var <- tempVar structGets.Add(mainInstructionProcessor.CreatePapyrusInstruction( PapyrusOpCodes.StructGet, mainInstructionProcessor.CreateVariableReferenceFromName(tempVar), structSource, structField)); } } methodName = methodName ?? methodRef.Name; if (callOpCode == PapyrusOpCodes.Callstatic) { inst.Arguments.AddRange(mainInstructionProcessor.ParsePapyrusParameters(new object[] { mainInstructionProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference, callerLocation), mainInstructionProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference, methodName), mainInstructionProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference, destinationVariable) })); } else { inst.Arguments.AddRange(mainInstructionProcessor.ParsePapyrusParameters(new object[] { mainInstructionProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference, methodName), mainInstructionProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference, callerLocation), mainInstructionProcessor.CreateVariableReference(PapyrusPrimitiveType.Reference, destinationVariable) })); } inst.OperandArguments.AddRange(EnsureParameterTypes(mainInstructionProcessor, methodRef.Parameters, mainInstructionProcessor.ParsePapyrusParameters(param))); inst.Operand = methodRef; return(inst); }
/// <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); }
private IEnumerable <PapyrusInstruction> ProcessPropertyAccess(IClrInstructionProcessor mainInstructionProcessor, Instruction instruction, MethodReference methodRef, MethodDefinition methodDefinition, List <object> parameters) { bool isStructAccess; var instructions = new List <PapyrusInstruction>(); if (methodRef is MethodDefinition) { methodDefinition = methodRef as MethodDefinition; } // If the property access is from outside the same class (Property exists outside the calling class) if (methodDefinition == null && methodRef != null) { var type = methodRef.DeclaringType; var methodType = methodRef.Name.Remove(3); var targetPropertyName = methodRef.Name.Substring(4); if (methodType == "set") { var param = parameters; var stack = mainInstructionProcessor.EvaluationStack; var locationVariable = stack.Pop().Value; instructions.Add(mainInstructionProcessor.CreatePapyrusInstruction(PapyrusOpCodes.PropSet, mainInstructionProcessor.CreateVariableReferenceFromName(targetPropertyName), locationVariable, param.First() )); } else if (methodType == "get") { var stack = mainInstructionProcessor.EvaluationStack; var locationVariable = stack.Pop().Value; var destinationVariable = mainInstructionProcessor.GetTargetVariable(instruction, methodRef, out isStructAccess); if (isStructAccess) { var structRef = mainInstructionProcessor.EvaluationStack.Pop().Value as PapyrusStructFieldReference; if (structRef != null) { // (Get Property Value and Assign Temp then do StructSet using Temp) instructions.Add(mainInstructionProcessor.CreatePapyrusInstruction(PapyrusOpCodes.PropGet, mainInstructionProcessor.CreateVariableReferenceFromName(targetPropertyName), locationVariable, mainInstructionProcessor.CreateVariableReferenceFromName(destinationVariable) )); // StructSet instructions.Add(mainInstructionProcessor.CreatePapyrusInstruction(PapyrusOpCodes.StructSet, structRef.StructSource, structRef.StructVariable, mainInstructionProcessor.CreateVariableReferenceFromName(destinationVariable))); // Skip next instruction as it should be stfld and we already store the field here. mainInstructionProcessor.SkipNextInstruction = true; } } else { instructions.Add(mainInstructionProcessor.CreatePapyrusInstruction(PapyrusOpCodes.PropGet, mainInstructionProcessor.CreateVariableReferenceFromName(targetPropertyName), locationVariable, mainInstructionProcessor.CreateVariableReferenceFromName(destinationVariable) )); } } else { throw new InvalidPropertyAccessException(); } return(instructions); } // Property Access within same class (Property exists within the same class) if (methodDefinition != null) { var targetPropertyName = methodRef.Name.Substring(4); var matchingProperty = mainInstructionProcessor.PapyrusType.Properties.FirstOrDefault( p => (p.SetMethod != null && p.SetMethod.Name.Value.ToLower().Equals(methodRef.Name.ToLower())) || (p.GetMethod != null && p.GetMethod.Name.Value.ToLower().Equals(methodRef.Name.ToLower())) || p.Name.Value.ToLower() == targetPropertyName.ToLower()); if (matchingProperty != null) { if (methodDefinition.IsSetter) { var param = parameters; var firstParam = param.First(); PapyrusStructFieldReference structRef = null; var eva1 = firstParam as PapyrusStructFieldReference; var eval = firstParam as EvaluationStackItem; if (eval != null) { structRef = eval.Value as PapyrusStructFieldReference; } if (eva1 != null) { structRef = eva1; } if (structRef != null) { // Create Temp Var // StructGet -> TempVar // PropSet TempVar var structSource = structRef.StructSource as PapyrusFieldDefinition; var structField = structRef.StructVariable; var fieldType = GetStructFieldType(papyrusAssemblyCollection, structSource, structField); // 1. Create Temp Var var tempVar = mainInstructionProcessor.GetTargetVariable(instruction, null, out isStructAccess, fieldType, true); // 2. StructGet -> tempVar // 3. Assign var <- tempVar instructions.Add(mainInstructionProcessor.CreatePapyrusInstruction( PapyrusOpCodes.StructGet, mainInstructionProcessor.CreateVariableReferenceFromName(tempVar), structSource, structField)); instructions.Add(mainInstructionProcessor.CreatePapyrusInstruction(PapyrusOpCodes.PropSet, mainInstructionProcessor.CreateVariableReferenceFromName(matchingProperty.Name.Value), mainInstructionProcessor.CreateVariableReferenceFromName("self"), mainInstructionProcessor.CreateVariableReferenceFromName(tempVar) )); } else { instructions.Add(mainInstructionProcessor.CreatePapyrusInstruction(PapyrusOpCodes.PropSet, mainInstructionProcessor.CreateVariableReferenceFromName(matchingProperty.Name.Value), mainInstructionProcessor.CreateVariableReferenceFromName("self"), firstParam )); } } else if (methodDefinition.IsGetter) { var destinationVariable = mainInstructionProcessor.GetTargetVariable(instruction, methodRef, out isStructAccess); var locationVariable = mainInstructionProcessor.CreateVariableReferenceFromName("self"); var targetProperty = mainInstructionProcessor.CreateVariableReferenceFromName(matchingProperty.Name.Value); if (isStructAccess) { var structRef = mainInstructionProcessor.EvaluationStack.Pop().Value as PapyrusStructFieldReference; if (structRef != null) { // (Get Property Value and Assign Temp then do StructSet using Temp) instructions.Add( mainInstructionProcessor.CreatePapyrusInstruction(PapyrusOpCodes.PropGet, targetProperty, locationVariable, mainInstructionProcessor.CreateVariableReferenceFromName(destinationVariable) )); // StructSet instructions.Add( mainInstructionProcessor.CreatePapyrusInstruction(PapyrusOpCodes.StructSet, structRef.StructSource, structRef.StructVariable, mainInstructionProcessor.CreateVariableReferenceFromName(destinationVariable))); // Skip next instruction as it should be stfld and we already store the field here. mainInstructionProcessor.SkipNextInstruction = true; } } else { var dest = mainInstructionProcessor.CreateVariableReferenceFromName(destinationVariable); instructions.Add(mainInstructionProcessor.CreatePapyrusInstruction(PapyrusOpCodes.PropGet, targetProperty, locationVariable, dest )); if (InstructionHelper.NextInstructionIs(instruction, InstructionHelper.IsConditional)) { mainInstructionProcessor.EvaluationStack.Push(new EvaluationStackItem { Value = dest, TypeName = Utility.GetPapyrusReturnType(methodRef.ReturnType) }); } } } } } return(instructions); }