public static bool IsLdloc(this CodeInstruction instruction, LocalVar local) { if (!instruction.IsLdloc(local.builder)) { return(false); } if (local.builder != null) { return(true); } return(LocalVar.OpCodeToInt[instruction.opcode] == local.index); }
public static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions) { var instructionList = instructions.ToList(); var GetValue = AccessTools.Method(typeof(ToolExtensions), nameof(ToolExtensions.GetValue), new Type[] { typeof(ToolComp), typeof(ToolType), typeof(float) }); var NoToolWorkSpeed = AccessTools.Method(typeof(Utility), nameof(Utility.NoToolWorkSpeed)); LocalVar currVal = null; LocalVar toolType = null; for (int i = 0; i < instructionList.Count; i++) { var instruction = instructionList[i]; if (instruction.Calls(GetValue)) { if (!instructionList[i + 1].IsStloc() || !instructionList[i - 2].IsLdloc()) { Log.Error("ST_BaseMessage".Translate() + "ST_Error_Harmony_ToolsUsedHandler".Translate()); break; } currVal = instructionList[i + 1].ToLocalVar(); toolType = instructionList[i - 2].ToLocalVar(); } if (currVal != null && instruction.IsLdloc(currVal) && instructionList[i + 1].LoadsConstant(1f)) { CodeInstruction loadToolType; if (toolType.builder != null) { loadToolType = new CodeInstruction(OpCodes.Ldloc_S, toolType.builder); } else { loadToolType = new CodeInstruction(OpCodes.Ldloc_S, toolType.index); } instructionList.RemoveAt(i + 1); instructionList.InsertRange(i + 1, new List <CodeInstruction>() { loadToolType, new CodeInstruction(OpCodes.Call, NoToolWorkSpeed), }); break; } } return(instructionList); }
private MsgTypedValue VariableToTypedValue(LocalVar lvar, CoalescedFrame frame) { // TODO - lvar.Type? if (lvar.Name == "_") { var tv = new MsgTypedValue(); tv.TypeId = (UInt32)Value.Type.None; return(tv); } else { var frameVar = frame.Variables.FirstOrDefault(v => v.Name == lvar.Name); if (frameVar == null) { throw new RequestFailedException($"Variable does not exist: \"{lvar.Name}\""); } return(frameVar.TypedValue); } }
private static void FindLvtEntry(LocalVar lv, ClassFile.Method method, int instructionIndex) { ClassFile.Method.LocalVariableTableEntry[] lvt = method.LocalVariableTableAttribute; if (lvt != null) { int pc = method.Instructions[instructionIndex].PC; int nextPC = method.Instructions[instructionIndex + 1].PC; bool isStore = IsStoreLocal(method.Instructions[instructionIndex].NormalizedOpCode); foreach (ClassFile.Method.LocalVariableTableEntry e in lvt) { // TODO validate the contents of the LVT entry if (e.index == lv.local && (e.start_pc <= pc || (e.start_pc == nextPC && isStore)) && e.start_pc + e.length > pc) { lv.name = e.name; lv.start_pc = e.start_pc; lv.end_pc = e.start_pc + e.length; break; } } } }
private static LocalVar MergeLocals(List <LocalVar> locals, Dictionary <long, LocalVar> localByStoreSite, LocalVar l1, LocalVar l2) { Debug.Assert(l1 != l2); Debug.Assert(l1.local == l2.local); for (int i = 0; i < locals.Count; i++) { if (locals[i] == l2) { locals.RemoveAt(i); i--; } } Dictionary <long, LocalVar> temp = new Dictionary <long, LocalVar>(localByStoreSite); localByStoreSite.Clear(); foreach (KeyValuePair <long, LocalVar> kv in temp) { localByStoreSite[kv.Key] = kv.Value == l2 ? l1 : kv.Value; } l1.isArg |= l2.isArg; l1.type = InstructionState.FindCommonBaseType(l1.type, l2.type); Debug.Assert(l1.type != VerifierTypeWrapper.Invalid); return(l1); }
internal LocalVarInfo(CodeInfo ma, ClassFile classFile, ClassFile.Method method, UntangledExceptionTable exceptions, MethodWrapper mw, ClassLoaderWrapper classLoader) { Dictionary <int, string>[] localStoreReaders = FindLocalVariables(ma, mw, classFile, method); // now that we've done the code flow analysis, we can do a liveness analysis on the local variables ClassFile.Method.Instruction[] instructions = method.Instructions; Dictionary <long, LocalVar> localByStoreSite = new Dictionary <long, LocalVar>(); List <LocalVar> locals = new List <LocalVar>(); for (int i = 0; i < localStoreReaders.Length; i++) { if (localStoreReaders[i] != null) { VisitLocalLoads(ma, method, locals, localByStoreSite, localStoreReaders[i], i, classLoader.EmitDebugInfo); } } Dictionary <LocalVar, LocalVar> forwarders = new Dictionary <LocalVar, LocalVar>(); if (classLoader.EmitDebugInfo) { InstructionFlags[] flags = MethodAnalyzer.ComputePartialReachability(ma, method.Instructions, exceptions, 0, false); // if we're emitting debug info, we need to keep dead stores as well... for (int i = 0; i < instructions.Length; i++) { if ((flags[i] & InstructionFlags.Reachable) != 0 && IsStoreLocal(instructions[i].NormalizedOpCode)) { if (!localByStoreSite.ContainsKey(MakeKey(i, instructions[i].NormalizedArg1))) { LocalVar v = new LocalVar(); v.local = instructions[i].NormalizedArg1; v.type = ma.GetStackTypeWrapper(i, 0); FindLvtEntry(v, method, i); locals.Add(v); localByStoreSite.Add(MakeKey(i, v.local), v); } } } // to make the debugging experience better, we have to trust the // LocalVariableTable (unless it's clearly bogus) and merge locals // together that are the same according to the LVT for (int i = 0; i < locals.Count - 1; i++) { for (int j = i + 1; j < locals.Count; j++) { LocalVar v1 = (LocalVar)locals[i]; LocalVar v2 = (LocalVar)locals[j]; if (v1.name != null && v1.name == v2.name && v1.start_pc == v2.start_pc && v1.end_pc == v2.end_pc) { // we can only merge if the resulting type is valid (this protects against incorrect // LVT data, but is also needed for constructors, where the uninitialized this is a different // type from the initialized this) TypeWrapper tw = InstructionState.FindCommonBaseType(v1.type, v2.type); if (tw != VerifierTypeWrapper.Invalid) { v1.isArg |= v2.isArg; v1.type = tw; forwarders.Add(v2, v1); locals.RemoveAt(j); j--; } } } } } else { for (int i = 0; i < locals.Count - 1; i++) { for (int j = i + 1; j < locals.Count; j++) { LocalVar v1 = (LocalVar)locals[i]; LocalVar v2 = (LocalVar)locals[j]; // if the two locals are the same, we merge them, this is a small // optimization, it should *not* be required for correctness. if (v1.local == v2.local && v1.type == v2.type) { v1.isArg |= v2.isArg; forwarders.Add(v2, v1); locals.RemoveAt(j); j--; } } } } invokespecialLocalVars = new LocalVar[instructions.Length][]; localVars = new LocalVar[instructions.Length]; for (int i = 0; i < localVars.Length; i++) { LocalVar v = null; if (localStoreReaders[i] != null) { Debug.Assert(IsLoadLocal(instructions[i].NormalizedOpCode)); // lame way to look up the local variable for a load // (by indirecting through a corresponding store) foreach (int store in localStoreReaders[i].Keys) { v = localByStoreSite[MakeKey(store, instructions[i].NormalizedArg1)]; break; } } else { if (instructions[i].NormalizedOpCode == NormalizedByteCode.__invokespecial) { invokespecialLocalVars[i] = new LocalVar[method.MaxLocals]; for (int j = 0; j < invokespecialLocalVars[i].Length; j++) { localByStoreSite.TryGetValue(MakeKey(i, j), out invokespecialLocalVars[i][j]); } } else { localByStoreSite.TryGetValue(MakeKey(i, instructions[i].NormalizedArg1), out v); } } if (v != null) { LocalVar fwd; if (forwarders.TryGetValue(v, out fwd)) { v = fwd; } localVars[i] = v; } } this.allLocalVars = locals.ToArray(); }
private static void VisitLocalLoads(CodeInfo codeInfo, ClassFile.Method method, List <LocalVar> locals, Dictionary <long, LocalVar> localByStoreSite, Dictionary <int, string> storeSites, int instructionIndex, bool debug) { Debug.Assert(IsLoadLocal(method.Instructions[instructionIndex].NormalizedOpCode)); LocalVar local = null; TypeWrapper type = VerifierTypeWrapper.Null; int localIndex = method.Instructions[instructionIndex].NormalizedArg1; bool isArg = false; foreach (int store in storeSites.Keys) { if (store == -1) { // it's a method argument, it has no initial store, but the type is simply the parameter type type = InstructionState.FindCommonBaseType(type, codeInfo.GetLocalTypeWrapper(0, localIndex)); isArg = true; } else { if (method.Instructions[store].NormalizedOpCode == NormalizedByteCode.__invokespecial) { type = InstructionState.FindCommonBaseType(type, codeInfo.GetLocalTypeWrapper(store + 1, localIndex)); } else if (method.Instructions[store].NormalizedOpCode == NormalizedByteCode.__static_error) { // it's an __invokespecial that turned into a __static_error // (since a __static_error doesn't continue, we don't need to set type) } else { Debug.Assert(IsStoreLocal(method.Instructions[store].NormalizedOpCode)); type = InstructionState.FindCommonBaseType(type, codeInfo.GetStackTypeWrapper(store, 0)); } } // we can't have an invalid type, because that would have failed verification earlier Debug.Assert(type != VerifierTypeWrapper.Invalid); LocalVar l; if (localByStoreSite.TryGetValue(MakeKey(store, localIndex), out l)) { if (local == null) { local = l; } else if (local != l) { // If we've already defined a LocalVar and we find another one, then we merge them // together. // This happens for the following code fragment: // // int i = -1; // try { i = 0; for(; ; ) System.out.println(i); } catch(Exception x) {} // try { i = 0; for(; ; ) System.out.println(i); } catch(Exception x) {} // System.out.println(i); // local = MergeLocals(locals, localByStoreSite, local, l); } } } if (local == null) { local = new LocalVar(); local.local = localIndex; if (VerifierTypeWrapper.IsThis(type)) { local.type = ((VerifierTypeWrapper)type).UnderlyingType; } else { local.type = type; } local.isArg = isArg; if (debug) { FindLvtEntry(local, method, instructionIndex); } locals.Add(local); } else { local.isArg |= isArg; local.type = InstructionState.FindCommonBaseType(local.type, type); Debug.Assert(local.type != VerifierTypeWrapper.Invalid); } foreach (int store in storeSites.Keys) { LocalVar v; if (!localByStoreSite.TryGetValue(MakeKey(store, localIndex), out v)) { localByStoreSite[MakeKey(store, localIndex)] = local; } else if (v != local) { local = MergeLocals(locals, localByStoreSite, local, v); } } }
private LocalVar[] ReadLocalVars(Stream s) { int size = ReadInt(s); LocalVar[] vars = new LocalVar[size]; for (int i = 0; i < size; ++i) { vars[i] = new LocalVar { Name = ReadString(s), StartPc = ReadUInt(s), EndPc = ReadUInt(s), }; } return vars; }
private static LocalVar MergeLocals(List<LocalVar> locals, Dictionary<long, LocalVar> localByStoreSite, LocalVar l1, LocalVar l2) { Debug.Assert(l1 != l2); Debug.Assert(l1.local == l2.local); for (int i = 0; i < locals.Count; i++) { if (locals[i] == l2) { locals.RemoveAt(i); i--; } } Dictionary<long, LocalVar> temp = new Dictionary<long, LocalVar>(localByStoreSite); localByStoreSite.Clear(); foreach (KeyValuePair<long, LocalVar> kv in temp) { localByStoreSite[kv.Key] = kv.Value == l2 ? l1 : kv.Value; } l1.isArg |= l2.isArg; l1.type = InstructionState.FindCommonBaseType(l1.type, l2.type); Debug.Assert(l1.type != VerifierTypeWrapper.Invalid); return l1; }
internal LocalVarInfo(CodeInfo ma, ClassFile classFile, ClassFile.Method method, UntangledExceptionTable exceptions, MethodWrapper mw, ClassLoaderWrapper classLoader) { Dictionary<int, string>[] localStoreReaders = FindLocalVariables(ma, mw, classFile, method); // now that we've done the code flow analysis, we can do a liveness analysis on the local variables ClassFile.Method.Instruction[] instructions = method.Instructions; Dictionary<long, LocalVar> localByStoreSite = new Dictionary<long, LocalVar>(); List<LocalVar> locals = new List<LocalVar>(); for (int i = 0; i < localStoreReaders.Length; i++) { if (localStoreReaders[i] != null) { VisitLocalLoads(ma, method, locals, localByStoreSite, localStoreReaders[i], i, classLoader.EmitDebugInfo); } } Dictionary<LocalVar, LocalVar> forwarders = new Dictionary<LocalVar, LocalVar>(); if (classLoader.EmitDebugInfo) { InstructionFlags[] flags = MethodAnalyzer.ComputePartialReachability(ma, method.Instructions, exceptions, 0, false); // if we're emitting debug info, we need to keep dead stores as well... for (int i = 0; i < instructions.Length; i++) { if ((flags[i] & InstructionFlags.Reachable) != 0 && IsStoreLocal(instructions[i].NormalizedOpCode)) { if (!localByStoreSite.ContainsKey(MakeKey(i, instructions[i].NormalizedArg1))) { LocalVar v = new LocalVar(); v.local = instructions[i].NormalizedArg1; v.type = ma.GetStackTypeWrapper(i, 0); FindLvtEntry(v, method, i); locals.Add(v); localByStoreSite.Add(MakeKey(i, v.local), v); } } } // to make the debugging experience better, we have to trust the // LocalVariableTable (unless it's clearly bogus) and merge locals // together that are the same according to the LVT for (int i = 0; i < locals.Count - 1; i++) { for (int j = i + 1; j < locals.Count; j++) { LocalVar v1 = (LocalVar)locals[i]; LocalVar v2 = (LocalVar)locals[j]; if (v1.name != null && v1.name == v2.name && v1.start_pc == v2.start_pc && v1.end_pc == v2.end_pc) { // we can only merge if the resulting type is valid (this protects against incorrect // LVT data, but is also needed for constructors, where the uninitialized this is a different // type from the initialized this) TypeWrapper tw = InstructionState.FindCommonBaseType(v1.type, v2.type); if (tw != VerifierTypeWrapper.Invalid) { v1.isArg |= v2.isArg; v1.type = tw; forwarders.Add(v2, v1); locals.RemoveAt(j); j--; } } } } } else { for (int i = 0; i < locals.Count - 1; i++) { for (int j = i + 1; j < locals.Count; j++) { LocalVar v1 = (LocalVar)locals[i]; LocalVar v2 = (LocalVar)locals[j]; // if the two locals are the same, we merge them, this is a small // optimization, it should *not* be required for correctness. if (v1.local == v2.local && v1.type == v2.type) { v1.isArg |= v2.isArg; forwarders.Add(v2, v1); locals.RemoveAt(j); j--; } } } } invokespecialLocalVars = new LocalVar[instructions.Length][]; localVars = new LocalVar[instructions.Length]; for (int i = 0; i < localVars.Length; i++) { LocalVar v = null; if (localStoreReaders[i] != null) { Debug.Assert(IsLoadLocal(instructions[i].NormalizedOpCode)); // lame way to look up the local variable for a load // (by indirecting through a corresponding store) foreach (int store in localStoreReaders[i].Keys) { v = localByStoreSite[MakeKey(store, instructions[i].NormalizedArg1)]; break; } } else { if (instructions[i].NormalizedOpCode == NormalizedByteCode.__invokespecial) { invokespecialLocalVars[i] = new LocalVar[method.MaxLocals]; for (int j = 0; j < invokespecialLocalVars[i].Length; j++) { localByStoreSite.TryGetValue(MakeKey(i, j), out invokespecialLocalVars[i][j]); } } else { localByStoreSite.TryGetValue(MakeKey(i, instructions[i].NormalizedArg1), out v); } } if (v != null) { LocalVar fwd; if (forwarders.TryGetValue(v, out fwd)) { v = fwd; } localVars[i] = v; } } this.allLocalVars = locals.ToArray(); }
private static void VisitLocalLoads(CodeInfo codeInfo, ClassFile.Method method, List<LocalVar> locals, Dictionary<long, LocalVar> localByStoreSite, Dictionary<int, string> storeSites, int instructionIndex, bool debug) { Debug.Assert(IsLoadLocal(method.Instructions[instructionIndex].NormalizedOpCode)); LocalVar local = null; TypeWrapper type = VerifierTypeWrapper.Null; int localIndex = method.Instructions[instructionIndex].NormalizedArg1; bool isArg = false; foreach (int store in storeSites.Keys) { if (store == -1) { // it's a method argument, it has no initial store, but the type is simply the parameter type type = InstructionState.FindCommonBaseType(type, codeInfo.GetLocalTypeWrapper(0, localIndex)); isArg = true; } else { if (method.Instructions[store].NormalizedOpCode == NormalizedByteCode.__invokespecial) { type = InstructionState.FindCommonBaseType(type, codeInfo.GetLocalTypeWrapper(store + 1, localIndex)); } else if (method.Instructions[store].NormalizedOpCode == NormalizedByteCode.__static_error) { // it's an __invokespecial that turned into a __static_error // (since a __static_error doesn't continue, we don't need to set type) } else { Debug.Assert(IsStoreLocal(method.Instructions[store].NormalizedOpCode)); type = InstructionState.FindCommonBaseType(type, codeInfo.GetStackTypeWrapper(store, 0)); } } // we can't have an invalid type, because that would have failed verification earlier Debug.Assert(type != VerifierTypeWrapper.Invalid); LocalVar l; if (localByStoreSite.TryGetValue(MakeKey(store, localIndex), out l)) { if (local == null) { local = l; } else if (local != l) { // If we've already defined a LocalVar and we find another one, then we merge them // together. // This happens for the following code fragment: // // int i = -1; // try { i = 0; for(; ; ) System.out.println(i); } catch(Exception x) {} // try { i = 0; for(; ; ) System.out.println(i); } catch(Exception x) {} // System.out.println(i); // local = MergeLocals(locals, localByStoreSite, local, l); } } } if (local == null) { local = new LocalVar(); local.local = localIndex; if (VerifierTypeWrapper.IsThis(type)) { local.type = ((VerifierTypeWrapper)type).UnderlyingType; } else { local.type = type; } local.isArg = isArg; if (debug) { FindLvtEntry(local, method, instructionIndex); } locals.Add(local); } else { local.isArg |= isArg; local.type = InstructionState.FindCommonBaseType(local.type, type); Debug.Assert(local.type != VerifierTypeWrapper.Invalid); } foreach (int store in storeSites.Keys) { LocalVar v; if (!localByStoreSite.TryGetValue(MakeKey(store, localIndex), out v)) { localByStoreSite[MakeKey(store, localIndex)] = local; } else if (v != local) { local = MergeLocals(locals, localByStoreSite, local, v); } } }
private static void HandleVariables() { Token[] smt=ts.PeekNextStatement(); SubRecord slsd; SubRecord scvr; while(smt.Length>0&&smt[0].IsType()) { ts.PopNextStatement(); if(smt.Length!=2||smt[1].type!=TokenType.Unknown) { AddError("Expected <type> <variable name>"); smt=ts.PeekNextStatement(); continue; } slsd=new SubRecord(); slsd.Name="SLSD"; byte[] data=new byte[24]; TypeConverter.si2h(locals.Count+1, data, 0); if(smt[0].IsKeyword(Keywords.Int)) data[16]=1; slsd.SetData(data); r.AddRecord(slsd); scvr=new SubRecord(); scvr.Name="SCVR"; scvr.SetStrData(smt[1].utoken, true); r.AddRecord(scvr); LocalVar lv=new LocalVar(locals.Count+1, smt[0]); locals.Add(smt[1].token, lv); localList.Add(lv); ts.AddLocal(smt[1].token); smt=ts.PeekNextStatement(); } }
public EnumInfo(FieldInfo cur, FieldInfo sta, LocalVar localSta = null) { Current = cur; State = sta; localState = localSta; }