/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using OpenSim.Region.ScriptEngine.Shared.ScriptBase; using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Text; using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; /** * Contains classes that disassemble or decompile an yobj file. * See xmrengcomp.cx utility program. */ namespace OpenSim.Region.ScriptEngine.Yengine { /* * Encapsulate object code for a method. */ public abstract class ObjectTokens { public ScriptObjCode scriptObjCode; public ObjectTokens(ScriptObjCode scriptObjCode) { this.scriptObjCode = scriptObjCode; } public abstract void Close(); public abstract void BegMethod(DynamicMethod method); public abstract void EndMethod(); public abstract void DefineLabel(int number, string name); public abstract void DefineLocal(int number, string name, string type, Type syType); public abstract void DefineMethod(string methName, Type retType, Type[] argTypes, string[] argNames); public abstract void MarkLabel(int offset, int number); public abstract void BegExcBlk(int offset); public abstract void BegCatBlk(int offset, Type excType); public abstract void BegFinBlk(int offset); public abstract void EndExcBlk(int offset); public abstract void EmitNull(int offset, OpCode opCode); public abstract void EmitField(int offset, OpCode opCode, FieldInfo field); public abstract void EmitLocal(int offset, OpCode opCode, int number); public abstract void EmitType(int offset, OpCode opCode, Type type); public abstract void EmitLabel(int offset, OpCode opCode, int number); public abstract void EmitLabels(int offset, OpCode opCode, int[] numbers); public abstract void EmitMethod(int offset, OpCode opCode, MethodInfo method); public abstract void EmitCtor(int offset, OpCode opCode, ConstructorInfo ctor); public abstract void EmitDouble(int offset, OpCode opCode, double value); public abstract void EmitFloat(int offset, OpCode opCode, float value); public abstract void EmitInteger(int offset, OpCode opCode, int value); public abstract void EmitString(int offset, OpCode opCode, string value); } /******************\ * DISASSEMBLER * \******************/ public class OTDisassemble: ObjectTokens { private static readonly int OPCSTRWIDTH = 12; private Dictionary labelNames; private Dictionary localNames; private StringBuilder lbuf = new StringBuilder(); private TextWriter twout; public OTDisassemble(ScriptObjCode scriptObjCode, TextWriter twout) : base(scriptObjCode) { this.twout = twout; } public override void Close() { twout.WriteLine("TheEnd."); } /** * About to generate object code for this method. */ public override void BegMethod(DynamicMethod method) { labelNames = new Dictionary(); localNames = new Dictionary(); twout.WriteLine(""); lbuf.Append(method.ReturnType.Name); lbuf.Append(' '); lbuf.Append(method.Name); ParameterInfo[] parms = method.GetParameters(); int nArgs = parms.Length; lbuf.Append(" ("); for(int i = 0; i < nArgs; i++) { if(i > 0) lbuf.Append(", "); lbuf.Append(parms[i].ParameterType.Name); } lbuf.Append(')'); FlushLine(); lbuf.Append('{'); FlushLine(); } /** * Dump out reconstructed source for this method. */ public override void EndMethod() { lbuf.Append('}'); FlushLine(); } /** * Add instructions to stream. */ public override void DefineLabel(int number, string name) { labelNames[number] = name + "$" + number; } public override void DefineLocal(int number, string name, string type, Type syType) { localNames[number] = name + "$" + number; lbuf.Append(" "); lbuf.Append(type.PadRight(OPCSTRWIDTH - 1)); lbuf.Append(' '); lbuf.Append(localNames[number]); FlushLine(); } public override void DefineMethod(string methName, Type retType, Type[] argTypes, string[] argNames) { } public override void MarkLabel(int offset, int number) { LinePrefix(offset); lbuf.Append(labelNames[number]); lbuf.Append(":"); FlushLine(); } public override void BegExcBlk(int offset) { LinePrefix(offset); lbuf.Append(" BeginExceptionBlock"); FlushLine(); } public override void BegCatBlk(int offset, Type excType) { LinePrefix(offset); lbuf.Append(" BeginCatchBlock "); lbuf.Append(excType.Name); FlushLine(); } public override void BegFinBlk(int offset) { LinePrefix(offset); lbuf.Append(" BeginFinallyBlock"); FlushLine(); } public override void EndExcBlk(int offset) { LinePrefix(offset); lbuf.Append(" EndExceptionBlock"); FlushLine(); } public override void EmitNull(int offset, OpCode opCode) { LinePrefix(offset, opCode); FlushLine(); } public override void EmitField(int offset, OpCode opCode, FieldInfo field) { LinePrefix(offset, opCode); lbuf.Append(field.DeclaringType.Name); lbuf.Append(':'); lbuf.Append(field.Name); lbuf.Append(" -> "); lbuf.Append(field.FieldType.Name); lbuf.Append(" (field)"); FlushLine(); } public override void EmitLocal(int offset, OpCode opCode, int number) { LinePrefix(offset, opCode); lbuf.Append(localNames[number]); lbuf.Append(" (local)"); FlushLine(); } public override void EmitType(int offset, OpCode opCode, Type type) { LinePrefix(offset, opCode); lbuf.Append(type.Name); lbuf.Append(" (type)"); FlushLine(); } public override void EmitLabel(int offset, OpCode opCode, int number) { LinePrefix(offset, opCode); lbuf.Append(labelNames[number]); lbuf.Append(" (label)"); FlushLine(); } public override void EmitLabels(int offset, OpCode opCode, int[] numbers) { LinePrefix(offset, opCode); int lineLen = lbuf.Length; int nLabels = numbers.Length; for(int i = 0; i < nLabels; i++) { if(i > 0) { lbuf.AppendLine(); lbuf.Append(",".PadLeft(lineLen)); } lbuf.Append(labelNames[numbers[i]]); } FlushLine(); } public override void EmitMethod(int offset, OpCode opCode, MethodInfo method) { LinePrefix(offset, opCode); ParameterInfo[] parms = method.GetParameters(); int nArgs = parms.Length; if(method.DeclaringType != null) { lbuf.Append(method.DeclaringType.Name); lbuf.Append(':'); } lbuf.Append(method.Name); lbuf.Append('('); for(int i = 0; i < nArgs; i++) { if(i > 0) lbuf.Append(","); lbuf.Append(parms[i].ParameterType.Name); } lbuf.Append(") -> "); lbuf.Append(method.ReturnType.Name); FlushLine(); } public override void EmitCtor(int offset, OpCode opCode, ConstructorInfo ctor) { LinePrefix(offset, opCode); ParameterInfo[] parms = ctor.GetParameters(); int nArgs = parms.Length; lbuf.Append(ctor.DeclaringType.Name); lbuf.Append(":("); for(int i = 0; i < nArgs; i++) { if(i > 0) lbuf.Append(","); lbuf.Append(parms[i].ParameterType.Name); } lbuf.Append(")"); FlushLine(); } public override void EmitDouble(int offset, OpCode opCode, double value) { LinePrefix(offset, opCode); lbuf.Append(value.ToString()); lbuf.Append(" (double)"); FlushLine(); } public override void EmitFloat(int offset, OpCode opCode, float value) { LinePrefix(offset, opCode); lbuf.Append(value.ToString()); lbuf.Append(" (float)"); FlushLine(); } public override void EmitInteger(int offset, OpCode opCode, int value) { LinePrefix(offset, opCode); lbuf.Append(value.ToString()); lbuf.Append(" (int)"); FlushLine(); } public override void EmitString(int offset, OpCode opCode, string value) { LinePrefix(offset, opCode); lbuf.Append("\""); lbuf.Append(value); lbuf.Append("\" (string)"); FlushLine(); } /** * Put offset and opcode at beginning of line. */ private void LinePrefix(int offset, OpCode opCode) { LinePrefix(offset); lbuf.Append(" "); lbuf.Append(opCode.ToString().PadRight(OPCSTRWIDTH - 1)); lbuf.Append(' '); } private void LinePrefix(int offset) { lbuf.Append(" "); lbuf.Append(offset.ToString("X4")); lbuf.Append(" "); } /** * Flush line buffer to output file. */ private void FlushLine() { if(lbuf.Length > 0) { twout.WriteLine(lbuf.ToString()); lbuf.Remove(0, lbuf.Length); } } } /****************\ * DECOMPILER * \****************/ /** * Note: The decompiler does not handle any xmroption extensions * such as &&&, |||, ? operators and switch statements, as * they do branches with a non-empty stack, which is way * beyond this code's ability to analyze. */ public class OTDecompile: ObjectTokens { public const string _mainCallNo = "__mainCallNo$"; public const string _callLabel = "__call_"; public const string _callMode = "callMode"; public const string _checkRunQuick = "CheckRunQuick"; public const string _checkRunStack = "CheckRunStack"; public const string _cmRestore = "__cmRestore"; public const string _doBreak = "dobreak_"; public const string _doCont = "docont_"; public const string _doGblInit = "doGblInit"; public const string _doLoop = "doloop_"; public const string _ehArgs = "ehArgs"; public const string _forBreak = "forbreak_"; public const string _forCont = "forcont_"; public const string _forLoop = "forloop_"; public const string _globalvarinit = "$globalvarinit()"; public const string _heapTrackerPop = "Pop"; public const string _heapTrackerPush = "Push"; public const string _ifDone = "ifdone_"; public const string _ifElse = "ifelse_"; public const string _llAbstemp = "llAbstemp"; public const string _retlbl = "__retlbl"; public const string _retval = "__retval$"; public const string _whileBreak = "whilebreak_"; public const string _whileCont = "whilecont_"; public const string _whileLoop = "whileloop_"; public const string _xmrinst = "__xmrinst"; public const string _xmrinstlocal = "__xmrinst$"; private const string INDENT = " "; private const string LABELINDENT = " "; private static Dictionary typeTranslator = InitTypeTranslator(); private static Dictionary InitTypeTranslator() { Dictionary d = new Dictionary(); d["Boolean"] = "integer"; d["bool"] = "integer"; d["Double"] = "float"; d["double"] = "float"; d["Int32"] = "integer"; d["int"] = "integer"; d["htlist"] = "list"; d["htobject"] = "object"; d["htstring"] = "string"; d["lslfloat"] = "float"; d["lslint"] = "integer"; d["lsllist"] = "list"; d["lslrot"] = "rotation"; d["lslstr"] = "string"; d["lslvec"] = "vector"; d["Quaternion"] = "rotation"; d["String"] = "string"; d["Vector3"] = "vector"; return d; } private Dictionary eharglist; private Dictionary labels; private Dictionary locals; private Dictionary methargnames; private LinkedList cilinstrs; private OTStmtBlock topBlock; private Stack opstack; private Stack trystack; private Stack blockstack; private int dupNo; private DynamicMethod method; private string laststate; private TextWriter twout; public OTDecompile(ScriptObjCode scriptObjCode, TextWriter twout) : base(scriptObjCode) { this.twout = twout; twout.Write("xmroption dollarsigns;"); methargnames = new Dictionary(); } public override void Close() { if(laststate != null) { twout.Write("\n}"); laststate = null; } twout.Write('\n'); } /** * About to generate object code for this method. */ public override void BegMethod(DynamicMethod method) { this.method = method; eharglist = new Dictionary(); labels = new Dictionary(); locals = new Dictionary(); cilinstrs = new LinkedList(); opstack = new Stack(); trystack = new Stack(); blockstack = new Stack(); dupNo = 0; } /** * Dump out reconstructed source for this method. */ public override void EndMethod() { // Convert CIL code to primitive statements. // There are a bunch of labels and internal code such as call stack save restore. topBlock = new OTStmtBlock(); blockstack.Push(topBlock); for(LinkedListNode link = cilinstrs.First; link != null; link = link.Next) { link.Value.BuildStatements(this, link); } // Strip out stuff we don't want, such as references to callMode. // This strips out stack frame capture and restore code. topBlock.StripStuff(null); // including a possible final return statement // - delete if void return value // - delete if returning __retval cuz we converted all __retval assignments to return statements if((topBlock.blkstmts.Last != null) && (topBlock.blkstmts.Last.Value is OTStmtRet)) { OTStmtRet finalret = (OTStmtRet)topBlock.blkstmts.Last.Value; if((finalret.value == null) || ((finalret.value is OTOpndLocal) && ((OTOpndLocal)finalret.value).local.name.StartsWith(_retval))) { topBlock.blkstmts.RemoveLast(); } } // At this point, all behind-the-scenes references are removed except // that the do/for/if/while blocks are represented by OTStmtCont-style // if/jumps. So try to convert them to the higher-level structures. topBlock.DetectDoForIfWhile(null); // Final strip to get rid of unneeded @forbreak_; labels and the like. topBlock.StripStuff(null); // Build reference counts so we don't output unneeded declarations, // especially temps and internal variables. foreach(OTLocal local in locals.Values) { local.nlclreads = 0; local.nlclwrites = 0; } topBlock.CountRefs(); for(IEnumerator localenum = locals.Keys.GetEnumerator(); localenum.MoveNext();) { OTLocal local = locals[localenum.Current]; if(((local.nlclreads | local.nlclwrites) == 0) || local.name.StartsWith(_xmrinstlocal)) { locals.Remove(localenum.Current); localenum = locals.Keys.GetEnumerator(); } } // Strip the $n off of local vars that are not ambiguous. // Make sure they don't mask globals and arguments as well. Dictionary namecounts = new Dictionary(); foreach(Dictionary varnames in scriptObjCode.globalVarNames.Values) { foreach(string varname in varnames.Values) { int count; if(!namecounts.TryGetValue(varname, out count)) count = 0; namecounts[varname] = count + 1; } } if(methargnames.ContainsKey(method.Name)) { foreach(string argname in methargnames[method.Name]) { int count; if(!namecounts.TryGetValue(argname, out count)) count = 0; namecounts[argname] = count + 1; } } foreach(OTLocal local in locals.Values) { int i = local.name.LastIndexOf('$'); string name = local.name.Substring(0, i); int count; if(!namecounts.TryGetValue(name, out count)) count = 0; namecounts[name] = count + 1; } foreach(OTLocal local in locals.Values) { int i = local.name.LastIndexOf('$'); string name = local.name.Substring(0, i); int count = namecounts[name]; if(count == 1) local.name = name; } // Print out result. if(method.Name == _globalvarinit) { GlobalsDump(); } else { MethodDump(); } } /** * Add instructions to stream. */ public override void DefineLabel(int number, string name) { labels.Add(number, new OTLabel(number, name)); } public override void DefineLocal(int number, string name, string type, Type syType) { locals.Add(number, new OTLocal(number, name, type)); } public override void DefineMethod(string methName, Type retType, Type[] argTypes, string[] argNames) { methargnames[methName] = argNames; } public override void MarkLabel(int offset, int number) { OTCilInstr label = labels[number]; label.offset = offset; cilinstrs.AddLast(label); } public override void BegExcBlk(int offset) { cilinstrs.AddLast(new OTCilBegExcBlk(offset)); } public override void BegCatBlk(int offset, Type excType) { cilinstrs.AddLast(new OTCilBegCatBlk(offset, excType)); } public override void BegFinBlk(int offset) { cilinstrs.AddLast(new OTCilBegFinBlk(offset)); } public override void EndExcBlk(int offset) { cilinstrs.AddLast(new OTCilEndExcBlk(offset)); } public override void EmitNull(int offset, OpCode opCode) { cilinstrs.AddLast(new OTCilNull(offset, opCode)); } public override void EmitField(int offset, OpCode opCode, FieldInfo field) { cilinstrs.AddLast(new OTCilField(offset, opCode, field)); } public override void EmitLocal(int offset, OpCode opCode, int number) { cilinstrs.AddLast(new OTCilLocal(offset, opCode, locals[number])); } public override void EmitType(int offset, OpCode opCode, Type type) { cilinstrs.AddLast(new OTCilType(offset, opCode, type)); } public override void EmitLabel(int offset, OpCode opCode, int number) { cilinstrs.AddLast(new OTCilLabel(offset, opCode, labels[number])); } public override void EmitLabels(int offset, OpCode opCode, int[] numbers) { OTLabel[] labelarray = new OTLabel[numbers.Length]; for(int i = 0; i < numbers.Length; i++) { labelarray[i] = labels[numbers[i]]; } cilinstrs.AddLast(new OTCilLabels(offset, opCode, labelarray)); } public override void EmitMethod(int offset, OpCode opCode, MethodInfo method) { cilinstrs.AddLast(new OTCilMethod(offset, opCode, method)); } public override void EmitCtor(int offset, OpCode opCode, ConstructorInfo ctor) { cilinstrs.AddLast(new OTCilCtor(offset, opCode, ctor)); } public override void EmitDouble(int offset, OpCode opCode, double value) { cilinstrs.AddLast(new OTCilDouble(offset, opCode, value)); } public override void EmitFloat(int offset, OpCode opCode, float value) { cilinstrs.AddLast(new OTCilFloat(offset, opCode, value)); } public override void EmitInteger(int offset, OpCode opCode, int value) { cilinstrs.AddLast(new OTCilInteger(offset, opCode, value)); } public override void EmitString(int offset, OpCode opCode, string value) { cilinstrs.AddLast(new OTCilString(offset, opCode, value)); } /** * Add the given statement to the end of the currently open block. */ public void AddLastStmt(OTStmt stmt) { blockstack.Peek().blkstmts.AddLast(stmt); } /** * Generate output for $globalvarinit() function. * Also outputs declarations for global variables. */ private void GlobalsDump() { // Scan $globalvarinit(). It should only have global var assignments in it. // Also gather up list of variables it initializes. bool badinit = false; Dictionary inittypes = new Dictionary(); foreach(OTStmt stmt in topBlock.blkstmts) { if(!(stmt is OTStmtStore)) { badinit = true; break; } OTStmtStore store = (OTStmtStore)stmt; if(!(store.varwr is OTOpndGlobal)) { badinit = true; break; } OTOpndGlobal globalop = (OTOpndGlobal)store.varwr; inittypes[globalop.PrintableString] = ""; } // Scan through list of all global variables in the script. // Output declarations for those what don't have any init statement for them. // Save the type for those that do have init statements. bool first = true; foreach(string iartypename in scriptObjCode.globalVarNames.Keys) { Dictionary varnames = scriptObjCode.globalVarNames[iartypename]; string typename = iartypename.ToLowerInvariant(); if(typename.StartsWith("iar")) typename = typename.Substring(3); if(typename.EndsWith("s")) typename = typename.Substring(0, typename.Length - 1); foreach(string varname in varnames.Values) { if(!badinit && inittypes.ContainsKey(varname)) { inittypes[varname] = typename; } else { if(first) twout.Write('\n'); twout.Write('\n' + typename + ' ' + varname + ';'); first = false; } } } // If $globalvarinit() has anything bad in it, output it as a function. // Otherwise, output it as a series of global declarations with init values. if(badinit) { MethodDump(); } else { foreach(OTStmt stmt in topBlock.blkstmts) { OTStmtStore store = (OTStmtStore)stmt; OTOpndGlobal globalop = (OTOpndGlobal)store.varwr; string name = globalop.PrintableString; if(first) twout.Write('\n'); twout.Write('\n' + inittypes[name] + ' '); store.PrintStmt(twout, ""); first = false; } } } /** * Generate output for other functions. */ private void MethodDump() { string indent; // Event handlers don't have an argument list as such in the original // code. Instead they have a series of assignments from ehargs[] to // local variables. So make those local variables look like they are // an argument list. int i = method.Name.IndexOf(' '); if(i >= 0) { // Maybe we have to output the state name. string statename = method.Name.Substring(0, i); string eventname = method.Name.Substring(++i); if(laststate != statename) { if(laststate != null) twout.Write("\n}"); if(statename == "default") { twout.Write("\n\ndefault {"); } else { twout.Write("\n\nstate " + statename + " {"); } laststate = statename; } else { twout.Write('\n'); } // Output event name and argument list. // Remove from locals list so they don't print below. twout.Write('\n' + INDENT + eventname + " ("); MethodInfo meth = typeof(IEventHandlers).GetMethod(eventname); i = 0; foreach(ParameterInfo pi in meth.GetParameters()) { // skip the first param cuz it's the XMRInstance arg if(i > 0) twout.Write(", "); OTLocal local; if(eharglist.TryGetValue(i, out local) && locals.ContainsKey(local.number)) { twout.Write(local.DumpString()); locals.Remove(local.number); } else { // maybe the assignment was removed // eg, because the local was write-only (not referenced) // so substitute in placeholder that won't be referenced twout.Write(AbbrType(pi.ParameterType) + " arg$" + (i + 1)); } i++; } twout.Write(')'); // Indent method body by 4 spaces. indent = INDENT; } else { // Maybe need to close out previous state. if(laststate != null) { twout.Write("\n}"); laststate = null; } // Output blank line and return type (if any). twout.Write("\n\n"); if(method.ReturnType != typeof(void)) { twout.Write(AbbrType(method.ReturnType) + ' '); } // Output method name and argument list. int j = method.Name.IndexOf('('); if(j < 0) { twout.Write(method.Name); } else { twout.Write(method.Name.Substring(0, j) + " ("); bool first = true; j = 0; foreach(ParameterInfo pi in method.GetParameters()) { if(j > 0) { // skip the XMRInstance arg$0 parameter if(!first) twout.Write(", "); twout.Write(AbbrType(pi.ParameterType) + ' ' + MethArgName(j)); first = false; } j++; } twout.Write(')'); } // Don't indent method body at all. indent = ""; } // Output local variable declarations. twout.Write('\n' + indent + '{'); bool didOne = false; foreach(OTLocal local in locals.Values) { twout.Write('\n' + indent + INDENT + local.DumpString() + "; // r:" + local.nlclreads + " w:" + local.nlclwrites); didOne = true; } if(didOne) twout.Write('\n'); // Output statements. if(topBlock.blkstmts.Count == 0) { twout.Write(" }"); } else { topBlock.PrintBodyAndEnd(twout, indent); } } /** * Get abbreviated type string. */ public static string AbbrType(Type type) { if(type == null) return "null"; return AbbrType(type.Name); } public static string AbbrType(string type) { if(type.StartsWith("OpenSim.Region.ScriptEngine.YEngine.")) { type = type.Substring(38); int i = type.IndexOf(','); if(i > 0) type = type.Substring(0, i); } if(typeTranslator.ContainsKey(type)) { type = typeTranslator[type]; } return type; } /** * Get current method's argument name. */ public string MethArgName(int index) { string[] argnames; if(methargnames.TryGetValue(method.Name, out argnames) && (index < argnames.Length)) { return argnames[index]; } return "arg$" + index; } /** * Strip svperflvovs (float) cast from rotation/vector values. */ public static OTOpnd StripFloatCast(OTOpnd op) { if(op is OTOpndCast) { OTOpndCast opcast = (OTOpndCast)op; if((opcast.type == typeof(double)) && (opcast.value is OTOpndInt)) { return opcast.value; } } return op; } /** * Strip svperflvovs Brtrues so we don't end up with stuff like 'if (!! someint) ...'. */ public static OTOpnd StripBrtrue(OTOpnd op) { if(op is OTOpndUnOp) { OTOpndUnOp opunop = (OTOpndUnOp)op; if(opunop.opCode == MyOp.Brtrue) return opunop.value; } return op; } /* * Local variable declaration. */ private class OTLocal { public int number; public string name; public string type; public int nlclreads; public int nlclwrites; public OTLocal(int number, string name, string type) { this.number = number; this.name = name.StartsWith("tmp$") ? name : name + "$" + number; this.type = type; } public string DumpString() { return AbbrType(type) + ' ' + name; } } /***********************************************\ * Tokens that are one-for-one with CIL code * \***********************************************/ /* * Part of instruction stream. */ public abstract class OTCilInstr { public int offset; // cil offset public OTCilInstr(int offset) { this.offset = offset; } public abstract string DumpString(); public abstract void BuildStatements(OTDecompile decompile, LinkedListNode link); protected void CheckEmptyStack(OTDecompile decompile, string opMnemonic) { if(decompile.opstack.Count > 0) { Console.Error.WriteLine("CheckEmptyStack: " + decompile.method.Name + " 0x" + offset.ToString("X") + ": " + opMnemonic + " stack depth " + decompile.opstack.Count); } } } /* * Label mark point. */ private class OTLabel: OTCilInstr { public int number; public string name; public int lbljumps; public OTLabel(int number, string name) : base(-1) { this.number = number; this.name = name; } public string PrintableName { get { if(name.StartsWith(_doBreak)) return _doBreak + "$" + number; if(name.StartsWith(_doCont)) return _doCont + "$" + number; if(name.StartsWith(_forBreak)) return _forBreak + "$" + number; if(name.StartsWith(_forCont)) return _forCont + "$" + number; if(name.StartsWith(_whileBreak)) return _whileBreak + "$" + number; if(name.StartsWith(_whileCont)) return _whileCont + "$" + number; return name; } } public override string DumpString() { return name + ":"; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { OTStmtLabel.AddLast(decompile, this); } } /* * 'try {' */ private class OTCilBegExcBlk: OTCilInstr { public LinkedList catches = new LinkedList(); public OTCilBegExcBlk(int offset) : base(offset) { } public override string DumpString() { return "try {"; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { CheckEmptyStack(decompile, "try"); // link the try itself onto outer block OTStmtBegExcBlk trystmt = new OTStmtBegExcBlk(); decompile.AddLastStmt(trystmt); // subsequent statements go to the try block trystmt.tryblock = new OTStmtBlock(); decompile.trystack.Push(trystmt); decompile.blockstack.Push(trystmt.tryblock); } } /* * '} catch (...) {' */ private class OTCilBegCatBlk: OTCilInstr { public Type excType; public OTCilBegCatBlk(int offset, Type excType) : base(offset) { this.excType = excType; } public override string DumpString() { return "} catch (" + AbbrType(excType) + ") {"; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { CheckEmptyStack(decompile, "catch"); // link the catch itself onto the try statement OTStmtBegExcBlk trystmt = decompile.trystack.Peek(); OTStmtBegCatBlk catstmt = new OTStmtBegCatBlk(excType); trystmt.catches.AddLast(catstmt); // start capturing statements into the catch block catstmt.tryblock = trystmt; catstmt.catchblock = new OTStmtBlock(); decompile.blockstack.Pop(); decompile.blockstack.Push(catstmt.catchblock); // fill the stack slot with something for the exception argument OTOpndDup dup = new OTOpndDup(++decompile.dupNo); decompile.opstack.Push(dup); } } /* * '} finally {' */ private class OTCilBegFinBlk: OTCilInstr { public OTCilBegFinBlk(int offset) : base(offset) { } public override string DumpString() { return "} finally {"; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { CheckEmptyStack(decompile, "finally"); // link the finally itself to the try statement OTStmtBegExcBlk trystmt = decompile.trystack.Peek(); OTStmtBegFinBlk finstmt = new OTStmtBegFinBlk(); trystmt.finblock = finstmt; // start capturing statements into the finally block finstmt.tryblock = trystmt; finstmt.finblock = new OTStmtBlock(); decompile.blockstack.Pop(); decompile.blockstack.Push(finstmt.finblock); } } /* * '}' end of try */ private class OTCilEndExcBlk: OTCilInstr { public OTCilEndExcBlk(int offset) : base(offset) { } public override string DumpString() { return "} // end try"; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { CheckEmptyStack(decompile, "endtry"); // pop the try/catch/finally blocks from stacks decompile.blockstack.Pop(); decompile.trystack.Pop(); // subsequent statements collect following the try } } /* * Actual opcodes (instructions). */ private class OTCilNull: OTCilInstr { public MyOp opCode; public OTCilNull(int offset, OpCode opCode) : base(offset) { this.opCode = MyOp.GetByName(opCode.Name); } public override string DumpString() { return opCode.ToString(); } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "conv.i1": case "conv.i2": case "conv.i4": case "conv.i8": { OTOpnd value = decompile.opstack.Pop(); decompile.opstack.Push(new OTOpndCast(typeof(int), value)); break; } case "conv.r4": case "conv.r8": { OTOpnd value = decompile.opstack.Pop(); decompile.opstack.Push(new OTOpndCast(typeof(double), value)); break; } case "dup": { OTOpnd value = decompile.opstack.Pop(); if(!(value is OTOpndDup)) { OTOpndDup dup = new OTOpndDup(++decompile.dupNo); OTStmtStore.AddLast(decompile, dup, value); value = dup; } decompile.opstack.Push(value); decompile.opstack.Push(value); break; } case "endfinally": break; case "ldarg.0": { decompile.opstack.Push(new OTOpndArg(0, false, decompile)); break; } case "ldarg.1": { decompile.opstack.Push(new OTOpndArg(1, false, decompile)); break; } case "ldarg.2": { decompile.opstack.Push(new OTOpndArg(2, false, decompile)); break; } case "ldarg.3": { decompile.opstack.Push(new OTOpndArg(3, false, decompile)); break; } case "ldc.i4.0": { decompile.opstack.Push(new OTOpndInt(0)); break; } case "ldc.i4.1": { decompile.opstack.Push(new OTOpndInt(1)); break; } case "ldc.i4.2": { decompile.opstack.Push(new OTOpndInt(2)); break; } case "ldc.i4.3": { decompile.opstack.Push(new OTOpndInt(3)); break; } case "ldc.i4.4": { decompile.opstack.Push(new OTOpndInt(4)); break; } case "ldc.i4.5": { decompile.opstack.Push(new OTOpndInt(5)); break; } case "ldc.i4.6": { decompile.opstack.Push(new OTOpndInt(6)); break; } case "ldc.i4.7": { decompile.opstack.Push(new OTOpndInt(7)); break; } case "ldc.i4.8": { decompile.opstack.Push(new OTOpndInt(8)); break; } case "ldc.i4.m1": { decompile.opstack.Push(new OTOpndInt(-1)); break; } case "ldelem.i4": case "ldelem.r4": case "ldelem.r8": case "ldelem.ref": { OTOpnd index = decompile.opstack.Pop(); OTOpnd array = decompile.opstack.Pop(); decompile.opstack.Push(OTOpndArrayElem.Make(array, index, false, decompile)); break; } case "ldnull": { decompile.opstack.Push(new OTOpndNull()); break; } case "neg": case "not": { OTOpnd value = decompile.opstack.Pop(); decompile.opstack.Push(OTOpndUnOp.Make(opCode, value)); break; } case "pop": { OTStmtVoid.AddLast(decompile, decompile.opstack.Pop()); break; } case "ret": { OTOpnd value = null; if(decompile.method.ReturnType != typeof(void)) { value = decompile.opstack.Pop(); } CheckEmptyStack(decompile); decompile.AddLastStmt(new OTStmtRet(value)); break; } case "stelem.i4": case "stelem.r8": case "stelem.ref": { OTOpnd value = decompile.opstack.Pop(); OTOpnd index = decompile.opstack.Pop(); OTOpnd array = decompile.opstack.Pop(); OTStmtStore.AddLast(decompile, OTOpndArrayElem.Make(array, index, false, decompile), value); break; } case "throw": { OTOpnd value = decompile.opstack.Pop(); CheckEmptyStack(decompile); decompile.AddLastStmt(new OTStmtThrow(value, decompile)); break; } case "add": case "and": case "ceq": case "cgt": case "cgt.un": case "clt": case "clt.un": case "div": case "div.un": case "mul": case "or": case "rem": case "rem.un": case "shl": case "shr": case "shr.un": case "sub": case "xor": { OTOpnd rite = decompile.opstack.Pop(); OTOpnd left = decompile.opstack.Pop(); decompile.opstack.Push(OTOpndBinOp.Make(left, opCode, rite)); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } protected void CheckEmptyStack(OTDecompile decompile) { CheckEmptyStack(decompile, opCode.ToString()); } } private class OTCilField: OTCilNull { public FieldInfo field; public OTCilField(int offset, OpCode opCode, FieldInfo field) : base(offset, opCode) { this.field = field; } public override string DumpString() { return opCode.ToString() + ' ' + field.Name; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "ldfld": { OTOpnd obj = decompile.opstack.Pop(); decompile.opstack.Push(OTOpndField.Make(obj, field)); break; } case "ldsfld": { decompile.opstack.Push(new OTOpndSField(field)); break; } case "stfld": { OTOpnd val = decompile.opstack.Pop(); OTOpnd obj = decompile.opstack.Pop(); OTStmtStore.AddLast(decompile, OTOpndField.Make(obj, field), val); break; } case "stsfld": { OTOpnd val = decompile.opstack.Pop(); OTStmtStore.AddLast(decompile, new OTOpndSField(field), val); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } private class OTCilLocal: OTCilNull { public OTLocal local; public OTCilLocal(int offset, OpCode opCode, OTLocal local) : base(offset, opCode) { this.local = local; } public override string DumpString() { return opCode.ToString() + ' ' + local.name; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "ldloc": { decompile.opstack.Push(new OTOpndLocal(local)); break; } case "ldloca": { decompile.opstack.Push(new OTOpndLocalRef(local)); break; } case "stloc": { OTOpnd val = decompile.opstack.Pop(); OTStmtStore.AddLast(decompile, new OTOpndLocal(local), val); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } private class OTCilType: OTCilNull { public Type type; public OTCilType(int offset, OpCode opCode, Type type) : base(offset, opCode) { this.type = type; } public override string DumpString() { return opCode.ToString() + ' ' + AbbrType(type); } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "box": { break; } case "castclass": case "unbox.any": { OTOpnd value = decompile.opstack.Pop(); decompile.opstack.Push(new OTOpndCast(type, value)); break; } case "ldelem": { OTOpnd index = decompile.opstack.Pop(); OTOpnd array = decompile.opstack.Pop(); decompile.opstack.Push(OTOpndArrayElem.Make(array, index, false, decompile)); break; } case "ldelema": { OTOpnd index = decompile.opstack.Pop(); OTOpnd array = decompile.opstack.Pop(); decompile.opstack.Push(OTOpndArrayElem.Make(array, index, true, decompile)); break; } case "newarr": { OTOpnd index = decompile.opstack.Pop(); decompile.opstack.Push(new OTOpndNewarr(type, index)); break; } case "stelem": { OTOpnd value = decompile.opstack.Pop(); OTOpnd index = decompile.opstack.Pop(); OTOpnd array = decompile.opstack.Pop(); OTStmtStore.AddLast(decompile, OTOpndArrayElem.Make(array, index, false, decompile), value); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } private class OTCilLabel: OTCilNull { public OTLabel label; public OTCilLabel(int offset, OpCode opCode, OTLabel label) : base(offset, opCode) { this.label = label; } public override string DumpString() { return opCode.ToString() + ' ' + label.name; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { // We don't handle non-empty stack at branch points. // // So handle this case specially: // // dup // ldc.i4.0 // bge.s llAbstemp << we are here // neg // llAbstemp: // // becomes: // // call llAbs case "bge.s": { OTOpnd rite = decompile.opstack.Pop(); // alleged zero OTOpnd left = decompile.opstack.Pop(); // alleged dup if((label.name == _llAbstemp) && (decompile.opstack.Count > 0)) { LinkedListNode linkneg = link.Next; if((left is OTOpndDup) && (rite is OTOpndInt) && (linkneg != null) && (linkneg.Value is OTCilNull) && (((OTCilNull)linkneg.Value).opCode == MyOp.Neg)) { OTOpndInt riteint = (OTOpndInt)rite; LinkedListNode linklbl = linkneg.Next; if((riteint.value == 0) && (linklbl != null) && (linklbl.Value is OTLabel) && (((OTLabel)linklbl.Value) == label)) { linkneg.List.Remove(linkneg); linklbl.List.Remove(linklbl); MethodInfo method = typeof(ScriptBaseClass).GetMethod("llAbs"); OTOpnd[] args = new OTOpnd[] { new OTOpndNull(), decompile.opstack.Pop() }; OTOpndCall.AddLast(decompile, method, args); break; } } } CheckEmptyStack(decompile); OTOpnd valu = OTOpndBinOp.Make(left, opCode, rite); OTStmt jump = OTStmtJump.Make(label); decompile.AddLastStmt(new OTStmtCond(valu, jump)); break; } case "beq": case "bge": case "bgt": case "ble": case "blt": case "bne.un": case "beq.s": case "bgt.s": case "ble.s": case "blt.s": case "bne.un.s": { OTOpnd rite = decompile.opstack.Pop(); OTOpnd left = decompile.opstack.Pop(); CheckEmptyStack(decompile); OTOpnd valu = OTOpndBinOp.Make(left, opCode, rite); OTStmt jump = OTStmtJump.Make(label); decompile.AddLastStmt(new OTStmtCond(valu, jump)); break; } case "brfalse": case "brfalse.s": case "brtrue": case "brtrue.s": { OTOpnd value = decompile.opstack.Pop(); CheckEmptyStack(decompile); OTOpnd valu = OTOpndUnOp.Make(opCode, value); OTStmt jump = OTStmtJump.Make(label); decompile.AddLastStmt(new OTStmtCond(valu, jump)); break; } case "br": case "br.s": case "leave": { CheckEmptyStack(decompile); OTStmt jump = OTStmtJump.Make(label); decompile.AddLastStmt(jump); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } private class OTCilLabels: OTCilNull { public OTLabel[] labels; public OTCilLabels(int offset, OpCode opCode, OTLabel[] labels) : base(offset, opCode) { this.labels = labels; } public override string DumpString() { StringBuilder sb = new StringBuilder(); sb.Append(opCode.ToString()); foreach(OTLabel label in labels) { sb.Append(' '); sb.Append(label.name); } return sb.ToString(); } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "switch": { OTOpnd value = decompile.opstack.Pop(); CheckEmptyStack(decompile); decompile.AddLastStmt(new OTStmtSwitch(value, labels)); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } private class OTCilMethod: OTCilNull { public MethodInfo method; public OTCilMethod(int offset, OpCode opCode, MethodInfo method) : base(offset, opCode) { this.method = method; } public override string DumpString() { return opCode.ToString() + ' ' + method.Name; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "call": case "callvirt": { int nargs = method.GetParameters().Length; if(!method.IsStatic) nargs++; OTOpnd[] args = new OTOpnd[nargs]; for(int i = nargs; --i >= 0;) { args[i] = decompile.opstack.Pop(); } OTOpndCall.AddLast(decompile, method, args); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } private class OTCilCtor: OTCilNull { public ConstructorInfo ctor; public OTCilCtor(int offset, OpCode opCode, ConstructorInfo ctor) : base(offset, opCode) { this.ctor = ctor; } public override string DumpString() { return opCode.ToString() + ' ' + AbbrType(ctor.DeclaringType); } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "newobj": { int nargs = ctor.GetParameters().Length; OTOpnd[] args = new OTOpnd[nargs]; for(int i = nargs; --i >= 0;) { args[i] = decompile.opstack.Pop(); } decompile.opstack.Push(OTOpndNewobj.Make(ctor, args)); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } private class OTCilDouble: OTCilNull { public double value; public OTCilDouble(int offset, OpCode opCode, double value) : base(offset, opCode) { this.value = value; } public override string DumpString() { return opCode.ToString() + ' ' + value; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "ldc.r8": { decompile.opstack.Push(new OTOpndDouble(value)); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } private class OTCilFloat: OTCilNull { public float value; public OTCilFloat(int offset, OpCode opCode, float value) : base(offset, opCode) { this.value = value; } public override string DumpString() { return opCode.ToString() + ' ' + value; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "ldc.r4": { decompile.opstack.Push(new OTOpndFloat(value)); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } private class OTCilInteger: OTCilNull { public int value; public OTCilInteger(int offset, OpCode opCode, int value) : base(offset, opCode) { this.value = value; } public override string DumpString() { return opCode.ToString() + ' ' + value; } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "ldarg": case "ldarg.s": { decompile.opstack.Push(new OTOpndArg(value, false, decompile)); break; } case "ldarga": case "ldarga.s": { decompile.opstack.Push(new OTOpndArg(value, true, decompile)); break; } case "ldc.i4": case "ldc.i4.s": { decompile.opstack.Push(new OTOpndInt(value)); break; } case "starg": { OTOpnd val = decompile.opstack.Pop(); OTStmtStore.AddLast(decompile, new OTOpndArg(value, false, decompile), val); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } private class OTCilString: OTCilNull { public string value; public OTCilString(int offset, OpCode opCode, string value) : base(offset, opCode) { this.value = value; } public override string DumpString() { StringBuilder sb = new StringBuilder(); sb.Append(opCode.ToString()); sb.Append(' '); TokenDeclInline.PrintParamString(sb, value); return sb.ToString(); } public override void BuildStatements(OTDecompile decompile, LinkedListNode link) { switch(opCode.ToString()) { case "ldstr": { decompile.opstack.Push(new OTOpndString(value)); break; } default: throw new Exception("unknown opcode " + opCode.ToString()); } } } /***************************************\ * Tokens what are on operand stack. * \***************************************/ public abstract class OTOpnd { /** * See if it possibly has any side effects. */ public abstract bool HasSideEffects { get; } /** * Increment reference counts. */ public virtual void CountRefs(bool writing) { } /** * If this operand is a 'by reference' operand, * return the corresponding 'by value' operand. */ public virtual OTOpnd GetNonByRefOpnd() { return this; } /** * If this operand is same as oldopnd, replace it with newopnd. * * This default just does a shallow search which is ok if this operand does not have any sub-operands. * But it must be overridden for a deep search if this operand has any sub-operands. */ public virtual OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } return this; } /** * See if the two operands are the same value. * Note that calls might have side-effects so are never the same. */ public abstract bool SameAs(OTOpnd other); /** * Get a printable string representation of the operand. */ public abstract string PrintableString { get; } } /** * Argument variable. */ private class OTOpndArg: OTOpnd { public int index; public bool byref; private OTDecompile decompile; public OTOpndArg(int index, bool byref, OTDecompile decompile) { this.index = index; this.byref = byref; this.decompile = decompile; } public override bool HasSideEffects { get { return false; } } public override OTOpnd GetNonByRefOpnd() { if(!byref) return this; return new OTOpndArg(index, false, decompile); } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndArg)) return false; return (((OTOpndArg)other).byref == byref) && (((OTOpndArg)other).index == index); } public override string PrintableString { get { string argname = decompile.MethArgName(index); return byref ? ("ref " + argname) : argname; } } } /** * Element of an array. */ private class OTOpndArrayElem: OTOpnd { public bool byref; public OTOpnd array; public OTOpnd index; public static OTOpnd Make(OTOpnd array, OTOpnd index, bool byref, OTDecompile decompile) { // arg$0.glblVars.iar[] is a reference to a global variable // likewise so is __xmrinst.glblVars.iar[] if((array is OTOpndField) && (index is OTOpndInt)) { // arrayfield = (arg$0.glblVars).iar // arrayfieldobj = arg$0.glblVars // iartypename = iar OTOpndField arrayfield = (OTOpndField)array; OTOpnd arrayfieldobj = arrayfield.obj; string iartypename = arrayfield.field.Name; // See if they are what they are supposed to be. if((arrayfieldobj is OTOpndField) && iartypename.StartsWith("iar")) { // arrayfieldobjfield = arg$0.glblVars OTOpndField arrayfieldobjfield = (OTOpndField)arrayfieldobj; // See if the parts are what they are supposed to be. if(IsArg0OrXMRInst(arrayfieldobjfield.obj) && (arrayfieldobjfield.field.Name == "glblVars")) { // Everything matches up, make a global variable instead of an array reference. return new OTOpndGlobal(iartypename, ((OTOpndInt)index).value, byref, decompile.scriptObjCode); } } } // Other array reference. OTOpndArrayElem it = new OTOpndArrayElem(); it.array = array; it.index = index; it.byref = byref; return it; } private OTOpndArrayElem() { } public override bool HasSideEffects { get { return array.HasSideEffects || index.HasSideEffects; } } public override void CountRefs(bool writing) { array.CountRefs(false); index.CountRefs(false); } public override OTOpnd GetNonByRefOpnd() { if(!byref) return this; OTOpndArrayElem it = new OTOpndArrayElem(); it.array = array; it.index = index; return it; } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } array = array.ReplaceOperand(oldopnd, newopnd, ref rc); index = index.ReplaceOperand(oldopnd, newopnd, ref rc); return this; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndArrayElem)) return false; OTOpndArrayElem otherae = (OTOpndArrayElem)other; return array.SameAs(otherae.array) && index.SameAs(otherae.index); } public override string PrintableString { get { return (byref ? "ref " : "") + array.PrintableString + "[" + index.PrintableString + "]"; } } /** * See if the argument is a reference to arg$0 or __xmrinst */ public static bool IsArg0OrXMRInst(OTOpnd obj) { if(obj is OTOpndArg) { OTOpndArg objarg = (OTOpndArg)obj; return objarg.index == 0; } if(obj is OTOpndLocal) { OTOpndLocal objlcl = (OTOpndLocal)obj; return objlcl.local.name.StartsWith(_xmrinstlocal); } return false; } } /** * Binary operator. */ private class OTOpndBinOp: OTOpnd { public OTOpnd left; public MyOp opCode; public OTOpnd rite; private static Dictionary xor1ops = InitXor1Ops(); private static Dictionary InitXor1Ops() { Dictionary d = new Dictionary(); d["ceq"] = "cne"; d["cge"] = "clt"; d["cgt"] = "cle"; d["cle"] = "cgt"; d["clt"] = "cge"; d["cne"] = "ceq"; return d; } public static OTOpnd Make(OTOpnd left, MyOp opCode, OTOpnd rite) { // ((x clt y) xor 1) => (x cge y) etc string xor1op; if((left is OTOpndBinOp) && xor1ops.TryGetValue(((OTOpndBinOp)left).opCode.name, out xor1op) && (opCode == MyOp.Xor) && (rite is OTOpndInt) && (((OTOpndInt)rite).value == 1)) { opCode = MyOp.GetByName(xor1op); } // handle strcmp() cases (see OTOpndStrCmp) if(left is OTOpndStrCmp) { OTOpnd strcmp = ((OTOpndStrCmp)left).MakeBinOp(opCode, rite); if(strcmp != null) return strcmp; } // nothing special, make as is OTOpndBinOp it = new OTOpndBinOp(); it.left = left; it.opCode = opCode; it.rite = rite; return it; } private OTOpndBinOp() { } public override bool HasSideEffects { get { return left.HasSideEffects || rite.HasSideEffects; } } public override void CountRefs(bool writing) { left.CountRefs(false); rite.CountRefs(false); } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } left = left.ReplaceOperand(oldopnd, newopnd, ref rc); rite = rite.ReplaceOperand(oldopnd, newopnd, ref rc); return this; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndBinOp)) return false; OTOpndBinOp otherbo = (OTOpndBinOp)other; return left.SameAs(otherbo.left) && (opCode.ToString() == otherbo.opCode.ToString()) && rite.SameAs(otherbo.rite); } public override string PrintableString { get { StringBuilder sb = new StringBuilder(); bool leftneedsparen = ItNeedsParentheses(left, true); if(leftneedsparen) sb.Append('('); sb.Append(left.PrintableString); if(leftneedsparen) sb.Append(')'); sb.Append(' '); sb.Append(opCode.source); sb.Append(' '); bool riteneedsparen = ItNeedsParentheses(rite, false); if(riteneedsparen) sb.Append('('); sb.Append(rite.PrintableString); if(riteneedsparen) sb.Append(')'); return sb.ToString(); } } /** * See if source code representation requires parentheses around the given operand. * @param it = the other operand to decide about * @param itleft = true: 'it' is on the left of this operand (A $ B) # C * false: 'it' is on the right of this operand A $ (B # C) */ private bool ItNeedsParentheses(OTOpnd it, bool itleft) { if(!(it is OTOpndBinOp)) return false; string itop = ((OTOpndBinOp)it).opCode.source; string myop = opCode.source; // find them in table. higher number is for *, lower is for +. int itpi, mypi; if(!precedence.TryGetValue(itop, out itpi)) return true; if(!precedence.TryGetValue(myop, out mypi)) return true; int itpiabs = Math.Abs(itpi); int mypiabs = Math.Abs(mypi); // if its precedence is lower (eg +) than my precedence (eg *), it needs parentheses if(itpiabs < mypiabs) return true; // if its precedence is higher (eg *) than my precedence (eg +), it doesn't needs parentheses if(itpiabs > mypiabs) return false; // if (A $ B) # C, we can safely go without the parentheses if(itleft) return false; // my it // A $ (B # C) only works without parentheses for commutative $ // A - (B + C) and A - (B - C) require parentheses // A + (B - C) does not return mypi < 0; // neg: things like -, /, etc require parentheses // pos: things like +, *, etc do not need parens } // see MMRScriptReduce.PrecedenceInit() private static Dictionary precedence = InitPrecedence(); private static Dictionary InitPrecedence() { Dictionary d = new Dictionary(); d["|"] = 140; d["^"] = 160; d["&"] = 180; d["<<"] = -260; d[">>"] = -260; d["+"] = 280; d["-"] = -280; d["*"] = 320; d["/"] = -320; d["%"] = -320; return d; } } /** * Call with or without return value. */ private class OTOpndCall: OTOpnd { private static Dictionary mathmeths = InitMathMeths(); private static Dictionary InitMathMeths() { Dictionary d = new Dictionary(); d["Acos"] = typeof(ScriptBaseClass).GetMethod("llAcos"); d["Asin"] = typeof(ScriptBaseClass).GetMethod("llAsin"); d["Atan"] = typeof(ScriptBaseClass).GetMethod("llAtan"); d["Cos"] = typeof(ScriptBaseClass).GetMethod("llCos"); d["Abs"] = typeof(ScriptBaseClass).GetMethod("llFabs"); d["Log"] = typeof(ScriptBaseClass).GetMethod("llLog"); d["Log10"] = typeof(ScriptBaseClass).GetMethod("llLog10"); d["Round"] = typeof(ScriptBaseClass).GetMethod("llRound"); d["Sin"] = typeof(ScriptBaseClass).GetMethod("llSin"); d["Sqrt"] = typeof(ScriptBaseClass).GetMethod("llSqrt"); d["Tan"] = typeof(ScriptBaseClass).GetMethod("llTan"); return d; } public MethodInfo method; public OTOpnd[] args; // pushes on stack for return-value functions // pushes to end of instruction stream for return-void functions public static void AddLast(OTDecompile decompile, MethodInfo method, OTOpnd[] args) { int nargs = args.Length; // heap tracker push is just the single arg value as far as we're concerned if((nargs == 1) && (method.Name == _heapTrackerPush) && method.DeclaringType.Name.StartsWith("HeapTracker")) { decompile.opstack.Push(args[0]); return; } // heap tracker pop is just a store as far as we're concerned if((nargs == 2) && (method.Name == _heapTrackerPop) && method.DeclaringType.Name.StartsWith("HeapTracker")) { OTStmtStore.AddLast(decompile, args[0], args[1]); return; } // string.Compare() is its own thing cuz it has to decompile many ways if((nargs == 2) && (method.DeclaringType == typeof(string)) && (method.Name == "Compare")) { decompile.opstack.Push(new OTOpndStrCmp(args[0], args[1])); return; } // ObjectToString, etc, should appear as casts if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToBool")) { MethodInfo meth = typeof(XMRInstAbstract).GetMethod("xmr" + method.Name); AddLast(decompile, meth, new OTOpnd[] { new OTOpndNull(), args[0] }); return; } if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToFloat")) { decompile.opstack.Push(new OTOpndCast(typeof(double), args[0])); return; } if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToInteger")) { decompile.opstack.Push(new OTOpndCast(typeof(int), args[0])); return; } if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToList")) { decompile.opstack.Push(new OTOpndCast(typeof(LSL_List), args[0])); return; } if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToRotation")) { decompile.opstack.Push(new OTOpndCast(typeof(LSL_Rotation), args[0])); return; } if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToString")) { decompile.opstack.Push(new OTOpndCast(typeof(string), args[0])); return; } if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToVector")) { decompile.opstack.Push(new OTOpndCast(typeof(LSL_Vector), args[0])); return; } if((method.DeclaringType == typeof(XMRInstAbstract)) && (method.Name == "xmrHeapLeft")) { AddLast(decompile, typeof(ScriptBaseClass).GetMethod("llGetFreeMemory"), new OTOpnd[] { new OTOpndNull() }); return; } // pop to entry in the list/object/string array if(PopToGlobalArray(decompile, method, args)) return; // strip off event handler argument unwrapper calls if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.StartsWith("EHArgUnwrap")) { decompile.opstack.Push(args[0]); return; } // translate Math method to ll method MethodInfo mathmeth; if((method.DeclaringType == typeof(Math)) && mathmeths.TryGetValue(method.Name, out mathmeth)) { AddLast(decompile, mathmeth, new OTOpnd[] { new OTOpndNull(), args[0] }); return; } if((method.DeclaringType == typeof(Math)) && (method.Name == "Atan2")) { AddLast(decompile, typeof(ScriptBaseClass).GetMethod("llAtan2"), new OTOpnd[] { new OTOpndNull(), args[0], args[1] }); return; } if((method.DeclaringType == typeof(Math)) && (method.Name == "Pow")) { AddLast(decompile, typeof(ScriptBaseClass).GetMethod("llPow"), new OTOpnd[] { new OTOpndNull(), args[0], args[1] }); return; } // string concat should be a bunch of adds if((method.Name == "Concat") && (method.DeclaringType == typeof(string))) { int k = args.Length; while(k > 1) { int j = 0; int i; for(i = 0; i + 2 <= k; i += 2) { args[j++] = OTOpndBinOp.Make(args[i + 0], MyOp.Add, args[i + 1]); } while(i < k) args[j++] = args[i++]; k = j; } if(k > 0) decompile.opstack.Push(args[0]); return; } // bunch of calls for rotation and vector arithmetic if((method.DeclaringType == typeof(BinOpStr)) && BinOpStrCall(decompile, method, args)) return; if((method.DeclaringType == typeof(ScriptCodeGen)) && (method.Name == "LSLRotationNegate")) { decompile.opstack.Push(OTOpndUnOp.Make(MyOp.Neg, args[0])); return; } if((method.DeclaringType == typeof(ScriptCodeGen)) && (method.Name == "LSLVectorNegate")) { decompile.opstack.Push(OTOpndUnOp.Make(MyOp.Neg, args[0])); return; } // otherwise process it as a call OTOpndCall call = new OTOpndCall(); call.method = method; call.args = args; if(method.ReturnType == typeof(void)) { OTStmtVoid.AddLast(decompile, call); } else { decompile.opstack.Push(call); } } public override bool HasSideEffects { get { return true; } } /** * Handle a call to XMRInstArrays.Pop * by converting it to a store directly into the array. */ private static bool PopToGlobalArray(OTDecompile decompile, MethodInfo method, OTOpnd[] args) { if(method.DeclaringType != typeof(XMRInstArrays)) return false; if(args.Length != 3) return false; string array = null; if(method.Name == "PopList") array = "iarLists"; if(method.Name == "PopObject") array = "iarObjects"; if(method.Name == "PopString") array = "iarStrings"; if(array == null) return false; // make token that points to the iar array FieldInfo field = typeof(XMRInstArrays).GetField(array); OTOpnd arrayfield = OTOpndField.Make(args[0], field); // make token that points to the element to be popped to OTOpnd element = OTOpndArrayElem.Make(arrayfield, args[1], false, decompile); // make a statement to store value in that element OTStmtStore.AddLast(decompile, element, args[2]); return true; } /** * BinOpStr has a bunch of calls to do funky arithmetic. * Instead of generating a call, put back the original source. */ private static bool BinOpStrCall(OTDecompile decompile, MethodInfo method, OTOpnd[] args) { switch(method.Name) { case "MethFloatAddList": case "MethIntAddList": case "MethKeyAddList": case "MethListAddFloat": case "MethListAddInt": case "MethListAddKey": case "MethListAddList": case "MethListAddObj": case "MethListAddRot": case "MethListAddStr": case "MethListAddVec": case "MethObjAddList": case "MethRotAddList": case "MethRotAddRot": case "MethStrAddList": case "MethVecAddList": case "MethVecAddVec": { decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Add, args[1])); return true; } case "MethListEqList": case "MethRotEqRot": case "MethVecEqVec": { decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Ceq, args[1])); return true; } case "MethListNeList": case "MethRotNeRot": case "MethVecNeVec": { decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Cne, args[1])); return true; } case "MethRotSubRot": case "MethVecSubVec": { decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Sub, args[1])); return true; } case "MethFloatMulVec": case "MethIntMulVec": case "MethRotMulRot": case "MethVecMulFloat": case "MethVecMulInt": case "MethVecMulRot": case "MethVecMulVec": { decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Mul, args[1])); return true; } case "MethRotDivRot": case "MethVecDivFloat": case "MethVecDivInt": case "MethVecDivRot": { decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Div, args[1])); return true; } default: return false; } } private OTOpndCall() { } public override void CountRefs(bool writing) { foreach(OTOpnd arg in args) { arg.CountRefs(false); } } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { for(int i = 0; i < args.Length; i++) { args[i] = args[i].ReplaceOperand(oldopnd, newopnd, ref rc); } return this; } public override bool SameAs(OTOpnd other) { return false; } public override string PrintableString { get { StringBuilder sb = new StringBuilder(); // GetByKey(a,i) => a[i] if((method.DeclaringType == typeof(XMR_Array)) && (method.Name == "GetByKey") && (args.Length == 2)) { sb.Append(args[0].PrintableString); sb.Append('['); sb.Append(args[1].PrintableString); sb.Append(']'); return sb.ToString(); } // SetByKey(a,i,v) => a[i] = v if((method.DeclaringType == typeof(XMR_Array)) && (method.Name == "SetByKey") && (args.Length == 3)) { sb.Append(args[0].PrintableString); sb.Append('['); sb.Append(args[1].PrintableString); sb.Append("] = "); sb.Append(args[2].PrintableString); return sb.ToString(); } // CompValuListEl.GetElementFromList accesses list elements like an array. if((method.DeclaringType == typeof(CompValuListEl)) && (method.Name == "GetElementFromList")) { sb.Append(args[0].PrintableString); sb.Append('['); sb.Append(args[1].PrintableString); sb.Append(']'); return sb.ToString(); } // methods that are part of ScriptBaseClass are LSL functions such as llSay() // so we want to skip outputting "arg$0," as it is the hidden "this" argument. // and there are also XMRInstAbstract functions such as xmrEventDequeue(). int starti = 0; if((method.DeclaringType == typeof(ScriptBaseClass)) && !method.IsStatic) starti = 1; if((method.DeclaringType == typeof(XMRInstAbstract)) && !method.IsStatic) starti = 1; // likewise, method that have null as the declaring type are script-defined // dynamic methods which have a hidden "this" argument passed as "arg$0". if(method.DeclaringType == null) starti = 1; // all others we want to show the type name (such as Math.Abs, String.Compare, etc) if(starti == 0) { sb.Append(AbbrType(method.DeclaringType)); sb.Append('.'); } // script-defined functions have the param types as part of their name // so strip them off here so they don't clutter things up int i = method.Name.IndexOf('('); if(i < 0) sb.Append(method.Name); else sb.Append(method.Name.Substring(0, i)); // now add the call arguments sb.Append(" ("); bool first = true; foreach(OTOpnd arg in args) { if(--starti < 0) { if(!first) sb.Append(", "); sb.Append(arg.PrintableString); first = false; } } sb.Append(')'); return sb.ToString(); } } } /** * Cast value to the given type. */ private class OTOpndCast: OTOpnd { public Type type; public OTOpnd value; public OTOpndCast(Type type, OTOpnd value) { this.type = type; this.value = value; } public override bool HasSideEffects { get { return value.HasSideEffects; } } public override void CountRefs(bool writing) { value.CountRefs(false); } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } value = value.ReplaceOperand(oldopnd, newopnd, ref rc); return this; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndCast)) return false; OTOpndCast othercast = (OTOpndCast)other; return (type == othercast.type) && value.SameAs(othercast.value); } public override string PrintableString { get { StringBuilder sb = new StringBuilder(); sb.Append('('); sb.Append(AbbrType(type)); sb.Append(") "); if(value is OTOpndBinOp) sb.Append('('); sb.Append(value.PrintableString); if(value is OTOpndBinOp) sb.Append(')'); return sb.ToString(); } } } /** * Duplicate stack value without re-performing computation. * Semantics just like local var except it doesn't have a declaration. */ private class OTOpndDup: OTOpnd { public int index; public int ndupreads; public OTOpndDup(int index) { this.index = index; } public override bool HasSideEffects { get { return false; } } public override void CountRefs(bool writing) { if(!writing) ndupreads++; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndDup)) return false; return ((OTOpndDup)other).index == index; } public override string PrintableString { get { return "dup$" + index; } } } /** * Field of an object. */ private class OTOpndField: OTOpnd { public OTOpnd obj; public FieldInfo field; public static OTOpnd Make(OTOpnd obj, FieldInfo field) { // LSL_Float.value => the object itself if((field.DeclaringType == typeof(LSL_Float)) && (field.Name == "value")) { return obj; } // LSL_Integer.value => the object itself if((field.DeclaringType == typeof(LSL_Integer)) && (field.Name == "value")) { return obj; } // LSL_String.m_string => the object itself if((field.DeclaringType == typeof(LSL_String)) && (field.Name == "m_string")) { return obj; } // some other field, output code to access it // sometimes the object comes as by reference (value types), so we might need to deref it first OTOpndField it = new OTOpndField(); it.obj = obj.GetNonByRefOpnd(); it.field = field; return it; } private OTOpndField() { } public override bool HasSideEffects { get { return obj.HasSideEffects; } } public override void CountRefs(bool writing) { // the field may be getting written to, but the object is being read obj.CountRefs(false); } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } obj = obj.ReplaceOperand(oldopnd, newopnd, ref rc); return this; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndField)) return false; OTOpndField otherfield = (OTOpndField)other; return (field.Name == otherfield.field.Name) && obj.SameAs(otherfield.obj); } public override string PrintableString { get { StringBuilder sb = new StringBuilder(); if(obj is OTOpndBinOp) sb.Append('('); sb.Append(obj.PrintableString); if(obj is OTOpndBinOp) sb.Append(')'); sb.Append('.'); sb.Append(field.Name); return sb.ToString(); } } } /** * Script-level global variable. */ private class OTOpndGlobal: OTOpnd { public string iartypename; public int iararrayidx; public bool byref; public ScriptObjCode scriptObjCode; public OTOpndGlobal(string iartypename, int iararrayidx, bool byref, ScriptObjCode scriptObjCode) { this.iartypename = iartypename; this.iararrayidx = iararrayidx; this.byref = byref; this.scriptObjCode = scriptObjCode; } public override bool HasSideEffects { get { return false; } } public override OTOpnd GetNonByRefOpnd() { if(!byref) return this; return new OTOpndGlobal(iartypename, iararrayidx, false, scriptObjCode); } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndGlobal)) return false; OTOpndGlobal otherglobal = (OTOpndGlobal)other; return (iartypename == otherglobal.iartypename) && (iararrayidx == otherglobal.iararrayidx); } public override string PrintableString { get { return (byref ? "ref " : "") + scriptObjCode.globalVarNames[iartypename][iararrayidx]; } } } /** * List initialization. */ private class OTOpndListIni: OTOpnd { public OTOpnd[] values; /** * Try to detect list initialization building idiom: * dup$ = newarr object[] << link points here * dup$[0] = bla * dup$[1] = bla * ... * ... newobj list (dup$) ... */ public static bool Detect(LinkedListNode link) { if(link == null) return false; /* * Check for 'dup$ = newarr object[]' and get listsize from . */ OTStmtStore store = (OTStmtStore)link.Value; if(!(store.varwr is OTOpndDup)) return false; if(!(store.value is OTOpndNewarr)) return false; OTOpndDup storevar = (OTOpndDup)store.varwr; OTOpndNewarr storeval = (OTOpndNewarr)store.value; if(storeval.type != typeof(object)) return false; if(!(storeval.index is OTOpndInt)) return false; int listsize = ((OTOpndInt)storeval.index).value; // Good chance of having list initializer, malloc an object to hold it. OTOpndListIni it = new OTOpndListIni(); it.values = new OTOpnd[listsize]; // There should be exactly listsize statements following that of the form: // dup$[] = bla // If so, save the bla values in the values[] array. LinkedListNode vallink = link; for(int i = 0; i < listsize; i++) { vallink = vallink.Next; if(vallink == null) return false; if(!(vallink.Value is OTStmtStore)) return false; OTStmtStore valstore = (OTStmtStore)vallink.Value; if(!(valstore.varwr is OTOpndArrayElem)) return false; OTOpndArrayElem varelem = (OTOpndArrayElem)valstore.varwr; if(varelem.array != storevar) return false; if(!(varelem.index is OTOpndInt)) return false; if(((OTOpndInt)varelem.index).value != i) return false; it.values[i] = valstore.value; } // The next statement should have a 'newobj list (dup$)' in it somewhere // that we want to replace with 'it'. ConstructorInfo protoctor = typeof(LSL_List).GetConstructor(new Type[] { typeof(object[]) }); OTOpnd[] protoargs = new OTOpnd[] { storevar }; OTOpnd proto = OTOpndNewobj.Make(protoctor, protoargs); vallink = vallink.Next; bool rc = vallink.Value.ReplaceOperand(proto, it); // If successful, delete 'dup$n =' and all 'dup$n[i] =' statements. if(rc) { do { LinkedListNode nextlink = link.Next; link.List.Remove(link); link = nextlink; } while(link != vallink); } return rc; } public override bool HasSideEffects { get { foreach(OTOpnd value in values) { if(value.HasSideEffects) return true; } return false; } } public override void CountRefs(bool writing) { foreach(OTOpnd value in values) { value.CountRefs(false); } } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } for(int i = 0; i < values.Length; i++) { values[i] = values[i].ReplaceOperand(oldopnd, newopnd, ref rc); } return this; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndListIni)) return false; OTOpndListIni otherli = (OTOpndListIni)other; if(otherli.values.Length != values.Length) return false; for(int i = 0; i < values.Length; i++) { if(!values[i].SameAs(otherli.values[i])) return false; } return true; } public override string PrintableString { get { StringBuilder sb = new StringBuilder(); sb.Append('['); for(int i = 0; i < values.Length; i++) { if(i > 0) sb.Append(','); sb.Append(' '); sb.Append(values[i].PrintableString); } sb.Append(" ]"); return sb.ToString(); } } } /** * Local variable. */ private class OTOpndLocal: OTOpnd { public OTLocal local; public OTOpndLocal(OTLocal local) { this.local = local; } public override bool HasSideEffects { get { return false; } } public override void CountRefs(bool writing) { if(writing) local.nlclwrites++; else local.nlclreads++; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndLocal)) return false; OTOpndLocal otherlocal = (OTOpndLocal)other; return local == otherlocal.local; } public override string PrintableString { get { return local.name; } } } private class OTOpndLocalRef: OTOpnd { public OTLocal local; public OTOpndLocalRef(OTLocal local) { this.local = local; } public override bool HasSideEffects { get { return true; } } public override void CountRefs(bool writing) { local.nlclreads++; local.nlclwrites++; } public override OTOpnd GetNonByRefOpnd() { return new OTOpndLocal(local); } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndLocal)) return false; OTOpndLocal otherlocal = (OTOpndLocal)other; return local == otherlocal.local; } public override string PrintableString { get { return "ref " + local.name; } } } /** * New C#-level array. */ private class OTOpndNewarr: OTOpnd { public Type type; public OTOpnd index; public OTOpndNewarr(Type type, OTOpnd index) { this.type = type; this.index = index; } public override bool HasSideEffects { get { return index.HasSideEffects; } } public override void CountRefs(bool writing) { index.CountRefs(false); } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } index = index.ReplaceOperand(oldopnd, newopnd, ref rc); return this; } public override bool SameAs(OTOpnd other) { return false; } public override string PrintableString { get { return "newarr " + type.Name + "[" + index.PrintableString + "]"; } } } /** * New C#-level object. */ private class OTOpndNewobj: OTOpnd { public ConstructorInfo ctor; public OTOpnd[] args; public static OTOpnd Make(ConstructorInfo ctor, OTOpnd[] args) { // newobj LSL_Float (x) => x if((ctor.DeclaringType == typeof(LSL_Float)) && (args.Length == 1)) { Type ptype = ctor.GetParameters()[0].ParameterType; if(ptype == typeof(string)) { return new OTOpndCast(typeof(double), args[0]); } return args[0]; } // newobj LSL_Integer (x) => x if((ctor.DeclaringType == typeof(LSL_Integer)) && (args.Length == 1)) { Type ptype = ctor.GetParameters()[0].ParameterType; if(ptype == typeof(string)) { return new OTOpndCast(typeof(int), args[0]); } return args[0]; } // newobj LSL_String (x) => x if((ctor.DeclaringType == typeof(LSL_String)) && (args.Length == 1)) { return args[0]; } // newobj LSL_Rotation (x, y, z, w) => if((ctor.DeclaringType == typeof(LSL_Rotation)) && (args.Length == 4)) { return new OTOpndRot(args[0], args[1], args[2], args[3]); } // newobj LSL_Vector (x, y, z) => if((ctor.DeclaringType == typeof(LSL_Vector)) && (args.Length == 3)) { return new OTOpndVec(args[0], args[1], args[2]); } // newobj LSL_Rotation (string) => (rotation) string if((ctor.DeclaringType == typeof(LSL_Rotation)) && (args.Length == 1)) { return new OTOpndCast(typeof(LSL_Rotation), args[0]); } // newobj LSL_Vector (string) => (rotation) string if((ctor.DeclaringType == typeof(LSL_Vector)) && (args.Length == 1)) { return new OTOpndCast(typeof(LSL_Vector), args[0]); } // newobj LSL_List (newarr object[0]) => [ ] if((ctor.DeclaringType == typeof(LSL_List)) && (args.Length == 1) && (args[0] is OTOpndNewarr)) { OTOpndNewarr arg0 = (OTOpndNewarr)args[0]; if((arg0.type == typeof(object)) && (arg0.index is OTOpndInt) && (((OTOpndInt)arg0.index).value == 0)) { OTOpndListIni listini = new OTOpndListIni(); listini.values = new OTOpnd[0]; return listini; } } // something else, output as is OTOpndNewobj it = new OTOpndNewobj(); it.ctor = ctor; it.args = args; return it; } private OTOpndNewobj() { } public override bool HasSideEffects { get { foreach(OTOpnd arg in args) { if(arg.HasSideEffects) return true; } return false; } } public override void CountRefs(bool writing) { foreach(OTOpnd arg in args) { arg.CountRefs(false); } } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } for(int i = 0; i < args.Length; i++) { args[i] = args[i].ReplaceOperand(oldopnd, newopnd, ref rc); } return this; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndNewobj)) return false; OTOpndNewobj otherno = (OTOpndNewobj)other; if(otherno.ctor.DeclaringType != ctor.DeclaringType) return false; if(otherno.args.Length != args.Length) return false; for(int i = 0; i < args.Length; i++) { if(!args[i].SameAs(otherno.args[i])) return false; } return true; } public override string PrintableString { get { StringBuilder sb = new StringBuilder(); sb.Append("newobj "); sb.Append(ctor.DeclaringType.Name); sb.Append(" ("); bool first = true; foreach(OTOpnd arg in args) { if(!first) sb.Append(", "); sb.Append(arg.PrintableString); first = false; } sb.Append(')'); return sb.ToString(); } } } /** * Rotation value. */ private class OTOpndRot: OTOpnd { private OTOpnd x, y, z, w; public OTOpndRot(OTOpnd x, OTOpnd y, OTOpnd z, OTOpnd w) { this.x = StripFloatCast(x); this.y = StripFloatCast(y); this.z = StripFloatCast(z); this.w = StripFloatCast(w); } public override bool HasSideEffects { get { return x.HasSideEffects || y.HasSideEffects || z.HasSideEffects || w.HasSideEffects; } } public override void CountRefs(bool writing) { x.CountRefs(false); y.CountRefs(false); z.CountRefs(false); w.CountRefs(false); } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } x = x.ReplaceOperand(oldopnd, newopnd, ref rc); y = y.ReplaceOperand(oldopnd, newopnd, ref rc); z = z.ReplaceOperand(oldopnd, newopnd, ref rc); w = w.ReplaceOperand(oldopnd, newopnd, ref rc); return this; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndRot)) return false; OTOpndRot otherv = (OTOpndRot)other; return otherv.x.SameAs(x) && otherv.y.SameAs(y) && otherv.z.SameAs(z) && otherv.w.SameAs(w); } public override string PrintableString { get { return "<" + x.PrintableString + ", " + y.PrintableString + ", " + z.PrintableString + ", " + w.PrintableString + ">"; } } } /** * Static field. */ private class OTOpndSField: OTOpnd { private FieldInfo field; public OTOpndSField(FieldInfo field) { this.field = field; } public override bool HasSideEffects { get { return false; } } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndSField)) return false; OTOpndSField othersfield = (OTOpndSField)other; return (field.Name == othersfield.field.Name) && (field.DeclaringType == othersfield.field.DeclaringType); } public override string PrintableString { get { if(field.DeclaringType == typeof(ScriptBaseClass)) return field.Name; return field.DeclaringType.Name + "." + field.Name; } } } /** * Call to string.Compare(). * See use cases in BinOpStr: * strcmp (a, b) ceq 0 * (strcmp (a, b) ceq 0) xor 1 => we translate to: strcmp (a, b) cne 0 * strcmp (a, b) clt 0 * strcmp (a, b) clt 1 // <= * strcmp (a, b) cgt 0 * strcmp (a, b) cgt -1 // >= * ...but then optimized by ScriptCollector if followed by br{false,true}: * ceq + xor 1 + brtrue => bne.un * ceq + xor 1 + brfalse => beq * ceq + brtrue => beq * ceq + brfalse => bne.un * cgt + brtrue => bgt * cgt + brfalse => ble * clt + brtrue => blt * clt + brfalse => bge * So we end up with these cases: * strcmp (a, b) ceq 0 * strcmp (a, b) cne 0 * strcmp (a, b) clt 0 * strcmp (a, b) clt 1 * strcmp (a, b) cgt 0 * strcmp (a, b) cgt -1 * strcmp (a, b) beq 0 * strcmp (a, b) bne.un 0 * strcmp (a, b) bgt 0 * strcmp (a, b) ble 0 * strcmp (a, b) bgt -1 * strcmp (a, b) ble -1 * strcmp (a, b) blt 0 * strcmp (a, b) bge 0 * strcmp (a, b) blt 1 * strcmp (a, b) bge 1 * ... so we pretty them up in OTOpndBinOp */ private class OTOpndStrCmp: OTOpnd { private static Dictionary binops = InitBinops(); private static Dictionary InitBinops() { Dictionary d = new Dictionary(); d["ceq 0"] = "ceq"; d["cne 0"] = "cne"; d["clt 0"] = "clt"; d["clt 1"] = "cle"; d["cgt 0"] = "cgt"; d["cgt -1"] = "cge"; d["beq 0"] = "ceq"; d["bne.un 0"] = "cne"; d["bgt 0"] = "cgt"; d["ble 0"] = "cle"; d["bgt -1"] = "cge"; d["ble -1"] = "clt"; d["blt 0"] = "clt"; d["bge 0"] = "cge"; d["blt 1"] = "cle"; d["bge 1"] = "cgt"; return d; } private OTOpnd arg0; private OTOpnd arg1; public OTOpndStrCmp(OTOpnd arg0, OTOpnd arg1) { this.arg0 = arg0; this.arg1 = arg1; } /** * Try to make something a script writer would recognize. * If we can't, then we leave it as a call to xmrStringCompare(). * this = some strcmp(a,b) * opCode = hopefully some cxx or bxx from above table * rite = hopefully some constant from above table */ public OTOpnd MakeBinOp(MyOp opCode, OTOpnd rite) { if(!(rite is OTOpndInt)) return null; int riteint = ((OTOpndInt)rite).value; string key = opCode.name + ' ' + riteint; string cxxopname; if(!binops.TryGetValue(key, out cxxopname)) return null; return OTOpndBinOp.Make(arg0, MyOp.GetByName(cxxopname), arg1); } public OTOpnd MakeUnOp(MyOp opCode) { if(opCode == MyOp.Brfalse) return OTOpndBinOp.Make(arg0, MyOp.Ceq, arg1); if(opCode == MyOp.Brtrue) return OTOpndBinOp.Make(arg0, MyOp.Cne, arg1); return null; } public override bool HasSideEffects { get { return false; } } public override void CountRefs(bool writing) { arg0.CountRefs(writing); arg1.CountRefs(writing); } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } arg0 = arg0.ReplaceOperand(oldopnd, newopnd, ref rc); arg1 = arg1.ReplaceOperand(oldopnd, newopnd, ref rc); return this; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndStrCmp)) return false; return arg0.SameAs(((OTOpndStrCmp)other).arg0) && arg1.SameAs(((OTOpndStrCmp)other).arg1); } public override string PrintableString { get { return "xmrStringCompare (" + arg0.PrintableString + ", " + arg1.PrintableString + ")"; } } } /** * Unary operator. */ private class OTOpndUnOp: OTOpnd { public MyOp opCode; public OTOpnd value; private static Dictionary brfops = InitBrfOps(); private static Dictionary InitBrfOps() { Dictionary d = new Dictionary(); d["beq"] = "cne"; d["bge"] = "clt"; d["bgt"] = "cle"; d["ble"] = "cgt"; d["blt"] = "cge"; d["bne.un"] = "ceq"; d["ceq"] = "cne"; d["cge"] = "clt"; d["cgt"] = "cle"; d["cle"] = "cgt"; d["clt"] = "cge"; d["cne"] = "ceq"; return d; } public static OTOpnd Make(MyOp opCode, OTOpnd value) { // (brfalse (brfalse (x))) => (brtrue (x)) if((opCode == MyOp.Brfalse) && (value is OTOpndUnOp) && (((OTOpndUnOp)value).opCode == MyOp.Brfalse)) { ((OTOpndUnOp)value).opCode = MyOp.Brtrue; return value; } // (brfalse (brtrue (x))) => (brfalse (x)) if((opCode == MyOp.Brfalse) && (value is OTOpndUnOp) && (((OTOpndUnOp)value).opCode == MyOp.Brtrue)) { ((OTOpndUnOp)value).opCode = MyOp.Brfalse; return value; } // (brtrue (brfalse (x))) => (brfalse (x)) if((opCode == MyOp.Brtrue) && (value is OTOpndUnOp) && (((OTOpndUnOp)value).opCode == MyOp.Brfalse)) { return value; } // (brtrue (brtrue (x))) => (brtrue (x)) if((opCode == MyOp.Brtrue) && (value is OTOpndUnOp) && (((OTOpndUnOp)value).opCode == MyOp.Brtrue)) { return value; } // (brfalse (x beq y)) => (x bne y) etc string brfop; if((opCode == MyOp.Brfalse) && (value is OTOpndBinOp) && brfops.TryGetValue(((OTOpndBinOp)value).opCode.name, out brfop)) { ((OTOpndBinOp)value).opCode = MyOp.GetByName(brfop); return value; } // (brtrue (x beq y)) => (x beq y) etc if((opCode == MyOp.Brtrue) && (value is OTOpndBinOp) && brfops.ContainsKey(((OTOpndBinOp)value).opCode.name)) { return value; } // strcmp() can be a special case if(value is OTOpndStrCmp) { OTOpnd strcmp = ((OTOpndStrCmp)value).MakeUnOp(opCode); if(strcmp != null) return strcmp; } // nothing special, save opcode and value OTOpndUnOp it = new OTOpndUnOp(); it.opCode = opCode; it.value = value; return it; } private OTOpndUnOp() { } public override bool HasSideEffects { get { return value.HasSideEffects; } } public override void CountRefs(bool writing) { value.CountRefs(false); } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } value = value.ReplaceOperand(oldopnd, newopnd, ref rc); return this; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndUnOp)) return false; OTOpndUnOp otherop = (OTOpndUnOp)other; return (opCode.ToString() == otherop.opCode.ToString()) && value.SameAs(otherop.value); } public override string PrintableString { get { StringBuilder sb = new StringBuilder(); sb.Append(opCode.source); sb.Append(' '); if(value is OTOpndBinOp) sb.Append('('); sb.Append(value.PrintableString); if(value is OTOpndBinOp) sb.Append(')'); return sb.ToString(); } } } /** * Vector value. */ private class OTOpndVec: OTOpnd { private OTOpnd x, y, z; public OTOpndVec(OTOpnd x, OTOpnd y, OTOpnd z) { this.x = StripFloatCast(x); this.y = StripFloatCast(y); this.z = StripFloatCast(z); } public override bool HasSideEffects { get { return x.HasSideEffects || y.HasSideEffects || z.HasSideEffects; } } public override void CountRefs(bool writing) { x.CountRefs(false); y.CountRefs(false); z.CountRefs(false); } public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) { if(SameAs(oldopnd)) { rc = true; return newopnd; } x = x.ReplaceOperand(oldopnd, newopnd, ref rc); y = y.ReplaceOperand(oldopnd, newopnd, ref rc); z = z.ReplaceOperand(oldopnd, newopnd, ref rc); return this; } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndVec)) return false; OTOpndVec otherv = (OTOpndVec)other; return otherv.x.SameAs(x) && otherv.y.SameAs(y) && otherv.z.SameAs(z); } public override string PrintableString { get { return "<" + x.PrintableString + ", " + y.PrintableString + ", " + z.PrintableString + ">"; } } } /** * Constants. */ private class OTOpndDouble: OTOpnd { public double value; public OTOpndDouble(double value) { this.value = value; } public override bool HasSideEffects { get { return false; } } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndDouble)) return false; return ((OTOpndDouble)other).value == value; } public override string PrintableString { get { string s = value.ToString(); long i; if(long.TryParse(s, out i)) { s += ".0"; } return s; } } } private class OTOpndFloat: OTOpnd { public float value; public OTOpndFloat(float value) { this.value = value; } public override bool HasSideEffects { get { return false; } } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndFloat)) return false; return ((OTOpndFloat)other).value == value; } public override string PrintableString { get { string s = value.ToString(); long i; if(long.TryParse(s, out i)) { s += ".0"; } return s; } } } private class OTOpndInt: OTOpnd { public int value; public OTOpndInt(int value) { this.value = value; } public override bool HasSideEffects { get { return false; } } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndInt)) return false; return ((OTOpndInt)other).value == value; } public override string PrintableString { get { return value.ToString(); } } } private class OTOpndNull: OTOpnd { public override bool HasSideEffects { get { return false; } } public override bool SameAs(OTOpnd other) { return other is OTOpndNull; } public override string PrintableString { get { return "undef"; } } } private class OTOpndString: OTOpnd { public string value; public OTOpndString(string value) { this.value = value; } public override bool HasSideEffects { get { return false; } } public override bool SameAs(OTOpnd other) { if(!(other is OTOpndString)) return false; return ((OTOpndString)other).value == value; } public override string PrintableString { get { StringBuilder sb = new StringBuilder(); TokenDeclInline.PrintParamString(sb, value); return sb.ToString(); } } } /****************************************\ * Tokens what are in statement list. * \****************************************/ public abstract class OTStmt { /** * Increment reference counts. */ public abstract void CountRefs(); /** * Strip out any of the behind-the-scenes code such as stack capture/restore. * By default, there is no change. */ public virtual bool StripStuff(LinkedListNode link) { return false; } /** * Replace the oldopnd operand with the newopnd operand if it is present. * Return whether or not it was found and replaced. */ public abstract bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd); /** * Detect and modify for do/for/if/while structures. */ public virtual bool DetectDoForIfWhile(LinkedListNode link) { return false; } /** * If this statement is the old statement, replace it with the given new statement. * Also search any sub-ordinate statements. * **NOTE**: minimally implemented to replace a Jump with a Break or Continue */ public abstract OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt); /** * Print the statement out on the given printer with the given indenting. * The first line is already indented, subsequent lines must be indented as given. * This method should leave the printer at the end of the line. */ public abstract void PrintStmt(TextWriter twout, string indent); /** * Strip all statements following this statement * because this statement jumps somewhere. */ protected bool StripStuffForTerminal(LinkedListNode link) { // strip all statements following jump until seeing some label bool rc = false; if(link != null) { LinkedListNode nextlink; while((nextlink = link.Next) != null) { if(nextlink.Value is OTStmtLabel) break; nextlink.List.Remove(nextlink); rc = true; } } return rc; } } /**************************\ * Primitive statements * \**************************/ /** * Begin catch block (catch). */ private class OTStmtBegCatBlk: OTStmt { public OTStmtBegExcBlk tryblock; public OTStmtBlock catchblock; private Type excType; public OTStmtBegCatBlk(Type excType) { this.excType = excType; } public override void CountRefs() { catchblock.CountRefs(); } public override bool StripStuff(LinkedListNode link) { return catchblock.StripStuff(null); } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { return catchblock.ReplaceOperand(oldopnd, newopnd); } public override bool DetectDoForIfWhile(LinkedListNode link) { return catchblock.DetectDoForIfWhile(link); } public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { catchblock = (OTStmtBlock)catchblock.ReplaceStatement(oldstmt, newstmt); return this; } /** * Print out the catch block including its enclosed statements. */ public override void PrintStmt(TextWriter twout, string indent) { twout.Write("catch (" + excType.Name + ") "); catchblock.PrintStmt(twout, indent); } } /** * Begin exception block (try). */ private class OTStmtBegExcBlk: OTStmt { // statements within the try { } not including any catch or finally public OTStmtBlock tryblock; // list of all catch { } blocks associated with this try { } public LinkedList catches = new LinkedList(); // possible single finally { } associated with this try public OTStmtBegFinBlk finblock; // might be null public override void CountRefs() { tryblock.CountRefs(); foreach(OTStmtBegCatBlk catblock in catches) { catblock.CountRefs(); } if(finblock != null) finblock.CountRefs(); } /** * Strip behind-the-scenes info from all the sub-blocks. */ public override bool StripStuff(LinkedListNode link) { // strip behind-the-scenes info from all the sub-blocks. bool rc = tryblock.StripStuff(null); foreach(OTStmtBegCatBlk catblk in catches) { rc |= catblk.StripStuff(null); } if(finblock != null) rc |= finblock.StripStuff(null); if(rc) return true; // change: // try { // ... // } // to: // { // ... // } // note that an empty catch () { } has meaning so can't be stripped // empty finally { } blocks strips itself from the try if((catches.Count == 0) && (finblock == null) && (link != null)) { link.List.AddAfter(link, tryblock); tryblock = null; link.List.Remove(link); return true; } return false; } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { bool rc = tryblock.ReplaceOperand(oldopnd, newopnd); foreach(OTStmtBegCatBlk catblk in catches) { rc |= catblk.ReplaceOperand(oldopnd, newopnd); } if(finblock != null) rc |= finblock.ReplaceOperand(oldopnd, newopnd); return rc; } public override bool DetectDoForIfWhile(LinkedListNode link) { bool rc = tryblock.DetectDoForIfWhile(link); foreach(OTStmtBegCatBlk catblk in catches) { rc |= catblk.DetectDoForIfWhile(link); } if(finblock != null) rc |= finblock.DetectDoForIfWhile(link); return rc; } /** * Assume we will never try to replace the try block itself. * But go through all our sub-ordinates statements. */ public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { tryblock = (OTStmtBlock)tryblock.ReplaceStatement(oldstmt, newstmt); for(LinkedListNode catlink = catches.First; catlink != null; catlink = catlink.Next) { catlink.Value = (OTStmtBegCatBlk)catlink.Value.ReplaceStatement(oldstmt, newstmt); } if(finblock != null) finblock = (OTStmtBegFinBlk)finblock.ReplaceStatement(oldstmt, newstmt); return this; } /** * Print out the try block including its enclosed statements. * And since the try is the only thing pushed to the outer block, * we also print out all the catch and finally blocks. */ public override void PrintStmt(TextWriter twout, string indent) { twout.Write("try "); tryblock.PrintStmt(twout, indent); foreach(OTStmtBegCatBlk catblk in catches) { twout.Write(' '); catblk.PrintStmt(twout, indent); } if(finblock != null) { twout.Write(' '); finblock.PrintStmt(twout, indent); } } } /** * Begin finally block (finally). */ private class OTStmtBegFinBlk: OTStmt { public OTStmtBegExcBlk tryblock; public OTStmtBlock finblock; public override void CountRefs() { finblock.CountRefs(); } /** * Strip behind-the-scene parts from the finally block. */ public override bool StripStuff(LinkedListNode link) { // strip behind-the-scenes parts from finally block itself if(finblock.StripStuff(null)) return true; // if finblock is empty, delete the finally from the try if(finblock.blkstmts.Count == 0) { tryblock.finblock = null; return true; } return false; } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { return finblock.ReplaceOperand(oldopnd, newopnd); } public override bool DetectDoForIfWhile(LinkedListNode link) { return finblock.DetectDoForIfWhile(link); } /** * Assume we will never try to replace the finally block itself. * But go through all our sub-ordinates statements. */ public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { finblock = (OTStmtBlock)finblock.ReplaceStatement(oldstmt, newstmt); return this; } /** * Print out the finally block including its enclosed statements. */ public override void PrintStmt(TextWriter twout, string indent) { twout.Write("finally "); finblock.PrintStmt(twout, indent); } } /** * Simple if jump/break/continue statement. */ private class OTStmtCond: OTStmt { public OTOpnd valu; public OTStmt stmt; // jump, break, continue only public OTStmtCond(OTOpnd valu, OTStmt stmt) { this.valu = valu; this.stmt = stmt; } public override void CountRefs() { valu.CountRefs(false); stmt.CountRefs(); } public override bool StripStuff(LinkedListNode link) { // we assume that callMode is always CallMode_NORMAL, ie, not doing a stack capture or restore // so the 'if (arg$0.callMode bne.un 0) ...' is deleted // and the 'if (arg$0.callMode bne.un 1) ...' becomes unconditional // it can also be __xmrinst.callMode instead of arg$0 if(valu is OTOpndBinOp) { OTOpndBinOp binop = (OTOpndBinOp)valu; if((binop.left is OTOpndField) && (binop.opCode.ToString() == "bne.un") && (binop.rite is OTOpndInt)) { OTOpndField leftfield = (OTOpndField)binop.left; if(leftfield.field.Name == _callMode) { bool ok = false; if(leftfield.obj is OTOpndArg) { ok = ((OTOpndArg)leftfield.obj).index == 0; } if(leftfield.obj is OTOpndLocal) { ok = ((OTOpndLocal)leftfield.obj).local.name.StartsWith(_xmrinstlocal); } if(ok) { OTOpndInt riteint = (OTOpndInt)binop.rite; // delete 'if ((arg$0).callMode bne.un 0) ...' if(riteint.value == XMRInstAbstract.CallMode_NORMAL) { link.List.Remove(link); return true; } // make 'if ((arg$0).callMode bne.un 1) ...' unconditional if(riteint.value == XMRInstAbstract.CallMode_SAVE) { link.Value = stmt; return true; } } } } } // similarly we assume that doGblInit is always 0 to eliminate the code at beginning of default state_entry() // so the 'if (brfalse __xmrinst.doGblInit) ...' is made unconditional if(valu is OTOpndUnOp) { OTOpndUnOp unop = (OTOpndUnOp)valu; if((unop.opCode == MyOp.Brfalse) && (unop.value is OTOpndField)) { OTOpndField valuefield = (OTOpndField)unop.value; if(valuefield.field.Name == _doGblInit) { bool ok = false; if(valuefield.obj is OTOpndLocal) { ok = ((OTOpndLocal)valuefield.obj).local.name.StartsWith(_xmrinstlocal); } if(ok) { // make 'if (brfalse __xmrinst.doGblInit) ...' unconditional link.Value = stmt; return true; } } } } return false; } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { bool rc = stmt.ReplaceOperand(oldopnd, newopnd); valu = valu.ReplaceOperand(oldopnd, newopnd, ref rc); return rc; } /** * Maybe this simple if statement is part of a script-level if/then/else statement. */ public override bool DetectDoForIfWhile(LinkedListNode link) { return OTStmtIf.Detect(link); } /** * Assume we won't replace the if statement itself. * But search all our sub-ordinate statements. */ public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { stmt = stmt.ReplaceStatement(oldstmt, newstmt); return this; } public override void PrintStmt(TextWriter twout, string indent) { twout.Write("if (" + StripBrtrue(valu).PrintableString + ") "); stmt.PrintStmt(twout, indent); } /** * Scan forward for a given label definition. * Put intervening statements in a statement block. * @param link = start scanning after this statement * @param label = look for this label definition * @param block = where to return intervening statement block * @returns null: label definition not found * else: label definition statement */ private static LinkedListNode ScanForLabel(LinkedListNode link, OTLabel label, out OTStmtBlock block) { block = new OTStmtBlock(); while((link = link.Next) != null) { if(link.Value is OTStmtLabel) { if(((OTStmtLabel)link.Value).label == label) break; } block.blkstmts.AddLast(link.Value); } return link; } /** * Strip statements after link up to and including donelink. */ private static void StripInterveningStatements(LinkedListNode link, LinkedListNode donelink) { LinkedListNode striplink; do { striplink = link.Next; striplink.List.Remove(striplink); } while(striplink != donelink); } } /** * Jump to a label. */ private class OTStmtJump: OTStmt { public OTLabel label; public static OTStmt Make(OTLabel label) { // jumps to __retlbl are return statements // note that is is safe to say it is a valueless return because // valued returns are done with this construct: // __retval = ....; // jump __retlbl; // and those __retval = statements have been changed to return statements already if(label.name.StartsWith(_retlbl)) return new OTStmtRet(null); // other jumps are really jumps OTStmtJump it = new OTStmtJump(); it.label = label; return it; } private OTStmtJump() { } public override void CountRefs() { label.lbljumps++; } public override bool StripStuff(LinkedListNode link) { if(link == null) return false; // strip statements following unconditional jump until next label bool rc = StripStuffForTerminal(link); // if we (now) have: // jump label; // @label; // ... delete this jump if(link.Next != null) { OTStmtLabel nextlabel = (OTStmtLabel)link.Next.Value; if(nextlabel.label == label) { link.List.Remove(link); rc = true; } } return rc; } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { return false; } /** * This is actually what ReplaceStatement() is currently used for. * It replaces a jump with a break or a continue. */ public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { if((oldstmt is OTStmtJump) && (((OTStmtJump)oldstmt).label == label)) return newstmt; return this; } public override void PrintStmt(TextWriter twout, string indent) { twout.Write("jump " + label.PrintableName + ';'); } } /** * Label definition point. */ private class OTStmtLabel: OTStmt { public OTLabel label; private OTDecompile decompile; public static void AddLast(OTDecompile decompile, OTLabel label) { OTStmtLabel it = new OTStmtLabel(); it.label = label; it.decompile = decompile; decompile.AddLastStmt(it); } private OTStmtLabel() { } public override void CountRefs() { // don't increment label.lbljumps // cuz we don't want the positioning // to count as a reference, only jumps // to the label should count } public override bool StripStuff(LinkedListNode link) { // if label has nothing jumping to it, remove the label if(link != null) { label.lbljumps = 0; decompile.topBlock.CountRefs(); if(label.lbljumps == 0) { link.List.Remove(link); return true; } } return false; } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { return false; } public override bool DetectDoForIfWhile(LinkedListNode link) { if(OTStmtDo.Detect(link)) return true; if(OTStmtFor.Detect(link, true)) return true; if(OTStmtFor.Detect(link, false)) return true; return false; } public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { return this; } public override void PrintStmt(TextWriter twout, string indent) { twout.Write("@" + label.PrintableName + ';'); } } /** * Return with or without value. */ private class OTStmtRet: OTStmt { public OTOpnd value; // might be null public OTStmtRet(OTOpnd value) { this.value = value; } public override void CountRefs() { if(value != null) value.CountRefs(false); } public override bool StripStuff(LinkedListNode link) { return StripStuffForTerminal(link); } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { bool rc = false; if(value != null) value = value.ReplaceOperand(oldopnd, newopnd, ref rc); return rc; } public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { return this; } public override void PrintStmt(TextWriter twout, string indent) { if(value == null) { twout.Write("return;"); } else { twout.Write("return " + value.PrintableString + ';'); } } } /** * Store value in variable. */ private class OTStmtStore: OTStmt { public OTOpnd varwr; public OTOpnd value; private OTDecompile decompile; public static void AddLast(OTDecompile decompile, OTOpnd varwr, OTOpnd value) { OTStmtStore it = new OTStmtStore(varwr, value, decompile); decompile.AddLastStmt(it); } public OTStmtStore(OTOpnd varwr, OTOpnd value, OTDecompile decompile) { this.varwr = varwr; this.value = value; this.decompile = decompile; } public override void CountRefs() { varwr.CountRefs(true); value.CountRefs(false); } public override bool StripStuff(LinkedListNode link) { // strip out stores to __mainCallNo if(varwr is OTOpndLocal) { OTOpndLocal local = (OTOpndLocal)varwr; if(local.local.name.StartsWith(_mainCallNo)) { link.List.Remove(link); return true; } } // strip out stores to local vars where the var is not read // but convert the value to an OTStmtVoid in case it is a call if(varwr is OTOpndLocal) { OTOpndLocal local = (OTOpndLocal)varwr; local.local.nlclreads = 0; decompile.topBlock.CountRefs(); if(local.local.nlclreads == 0) { OTStmt voidstmt = OTStmtVoid.Make(value); if(voidstmt == null) link.List.Remove(link); else link.Value = voidstmt; return true; } } // strip out bla = newobj HeapTrackerList (...); if(value is OTOpndNewobj) { OTOpndNewobj valueno = (OTOpndNewobj)value; if(valueno.ctor.DeclaringType == typeof(HeapTrackerList)) { link.List.Remove(link); return true; } } // strip out bla = newobj HeapTrackerObject (...); if(value is OTOpndNewobj) { OTOpndNewobj valueno = (OTOpndNewobj)value; if(valueno.ctor.DeclaringType == typeof(HeapTrackerObject)) { link.List.Remove(link); return true; } } // strip out bla = newobj HeapTrackerString (...); if(value is OTOpndNewobj) { OTOpndNewobj valueno = (OTOpndNewobj)value; if(valueno.ctor.DeclaringType == typeof(HeapTrackerString)) { link.List.Remove(link); return true; } } // convert tmp$n = bla bla; // .... tmp$n ....; // to // .... bla bla ....; // gets rid of vast majority of temps if(varwr is OTOpndLocal) { OTOpndLocal temp = (OTOpndLocal)varwr; if(temp.local.name.StartsWith("tmp$")) { temp.local.nlclreads = 0; temp.local.nlclwrites = 0; decompile.topBlock.CountRefs(); if((temp.local.nlclreads == 1) && (temp.local.nlclwrites == 1) && (link.Next != null)) { OTStmt nextstmt = link.Next.Value; if(!(nextstmt is OTStmtBlock)) { if(nextstmt.ReplaceOperand(varwr, value)) { link.List.Remove(link); return true; } } } // also try to convert: // tmp$n = ... asdf ... << we are here (link) // lcl = tmp$n; << nextstore // ... qwer tmp$n ... // ... no further references to tmp$n // to: // lcl = ... asdf ... // ... qwer lcl ... if((temp.local.nlclreads == 2) && (temp.local.nlclwrites == 1) && (link.Next != null) && (link.Next.Value is OTStmtStore)) { OTStmtStore nextstore = (OTStmtStore)link.Next.Value; if((nextstore.varwr is OTOpndLocal) && (nextstore.value is OTOpndLocal) && (link.Next.Next != null)) { OTOpndLocal localopnd = (OTOpndLocal)nextstore.varwr; OTOpndLocal tempopnd = (OTOpndLocal)nextstore.value; if(tempopnd.local == temp.local) { OTStmt finalstmt = link.Next.Next.Value; if(finalstmt.ReplaceOperand(tempopnd, localopnd)) { nextstore.value = value; link.List.Remove(link); return true; } } } } } } // convert: // dup$n = ... asdf ... << we are here // lcl = dup$n; // ... qwer dup$n ... // ... no further references to dup$n // to: // lcl = ... asdf ... // ... qwer lcl ... if((varwr is OTOpndDup) && (link != null)) { OTOpndDup vardup = (OTOpndDup)varwr; LinkedListNode nextlink = link.Next; vardup.ndupreads = 0; decompile.topBlock.CountRefs(); if((vardup.ndupreads == 2) && (nextlink != null) && (nextlink.Value is OTStmtStore)) { // point to the supposed lcl = dup$n statement OTStmtStore nextstore = (OTStmtStore)nextlink.Value; LinkedListNode nextlink2 = nextlink.Next; if((nextstore.varwr is OTOpndLocal) && (nextstore.value == vardup) && (nextlink2 != null)) { // get the local var being written and point to the ... qwer dup$n ... statement OTOpndLocal varlcl = (OTOpndLocal)nextstore.varwr; OTStmt nextstmt2 = nextlink2.Value; // try to replace dup$n in qwer with lcl if(nextstmt2.ReplaceOperand(vardup, varlcl)) { // successful, replace dup$n in asdf with lcl // and delete the lcl = dup$n statement varwr = varlcl; nextlink.List.Remove(nextlink); return true; } } } } // convert: // dup$n = ... asdf ... << we are here // ... qwer dup$n ... // ... no further references to dup$n // to: // ... qwer ... asdf ... ... if((varwr is OTOpndDup) && (link != null)) { OTOpndDup vardup = (OTOpndDup)varwr; LinkedListNode nextlink = link.Next; vardup.ndupreads = 0; decompile.topBlock.CountRefs(); if((vardup.ndupreads == 1) && (nextlink != null)) { // point to the ... qwer dup$n ... statement OTStmt nextstmt = nextlink.Value; // try to replace dup$n in qwer with ... asdf ... if(nextstmt.ReplaceOperand(vardup, value)) { // successful, delete the dup$n = ... asdf ... statement link.List.Remove(link); return true; } } } // look for list initialization [ ... ] if(OTOpndListIni.Detect(link)) return true; // __xmrinst = (XMRInstAbstract) arg$0 indicates this is an event handler // so strip it out and set the flag if((varwr is OTOpndLocal) && (value is OTOpndCast)) { OTOpndLocal lcl = (OTOpndLocal)varwr; OTOpndCast cast = (OTOpndCast)value; if(lcl.local.name.StartsWith(_xmrinstlocal) && (cast.value is OTOpndArg)) { link.List.Remove(link); return true; } } // local = [ (optional cast) ] __xmrinst.ehArgs[n] is a definition of event handler arg #n // if found, make it event handler arg list definition OTOpnd valuenocast = value; if(valuenocast is OTOpndCast) valuenocast = ((OTOpndCast)value).value; if((varwr is OTOpndLocal) && (valuenocast is OTOpndArrayElem)) { OTOpndArrayElem array = (OTOpndArrayElem)valuenocast; if((array.array is OTOpndField) && (array.index is OTOpndInt)) { OTOpndField arrayfield = (OTOpndField)array.array; if((arrayfield.obj is OTOpndLocal) && ((OTOpndLocal)arrayfield.obj).local.name.StartsWith(_xmrinstlocal) && (arrayfield.field.Name == _ehArgs)) { int index = ((OTOpndInt)array.index).value; decompile.eharglist[index] = ((OTOpndLocal)varwr).local; link.List.Remove(link); return true; } } } // __retval$n = ...; => return ...; if(varwr is OTOpndLocal) { OTOpndLocal lcl = (OTOpndLocal)varwr; if(lcl.local.name.StartsWith(_retval)) { link.Value = new OTStmtRet(value); return true; } } return false; } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { bool rc = false; if(value != null) value = value.ReplaceOperand(oldopnd, newopnd, ref rc); return rc; } public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { return this; } public override void PrintStmt(TextWriter twout, string indent) { // print x = x + 1 as x += 1, but don't print x = x < 3 as x <= 3 if(value is OTOpndBinOp) { OTOpndBinOp valuebo = (OTOpndBinOp)value; if(varwr.SameAs(valuebo.left) && " add and div mul or rem shl shr sub xor ".Contains(' ' + valuebo.opCode.name + ' ')) { twout.Write(varwr.PrintableString + ' ' + valuebo.opCode.source + "= " + valuebo.rite.PrintableString + ';'); return; } } twout.Write(varwr.PrintableString + " = " + value.PrintableString + ';'); } } /** * Dispatch to a table of labels. */ private class OTStmtSwitch: OTStmt { private OTOpnd index; private OTLabel[] labels; public OTStmtSwitch(OTOpnd index, OTLabel[] labels) { this.index = index; this.labels = labels; } public override void CountRefs() { index.CountRefs(false); foreach(OTLabel label in labels) { label.lbljumps++; } } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { bool rc = false; if(index != null) index = index.ReplaceOperand(oldopnd, newopnd, ref rc); return rc; } public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { return this; } public override void PrintStmt(TextWriter twout, string indent) { twout.Write("switch (" + index.PrintableString + ") {\n"); for(int i = 0; i < labels.Length; i++) { twout.Write(indent + INDENT + "case " + i + ": jump " + labels[i].name + ";\n"); } twout.Write(indent + '}'); } } /** * Throw an exception. */ private class OTStmtThrow: OTStmt { private OTOpnd value; private OTDecompile decompile; public OTStmtThrow(OTOpnd value, OTDecompile decompile) { this.value = value; this.decompile = decompile; } public override void CountRefs() { value.CountRefs(false); } public override bool StripStuff(LinkedListNode link) { return StripStuffForTerminal(link); } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { bool rc = false; if(value != null) value = value.ReplaceOperand(oldopnd, newopnd, ref rc); return rc; } public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { return this; } public override void PrintStmt(TextWriter twout, string indent) { // throw newobj ScriptUndefinedStateException ("x") => state x if(value is OTOpndNewobj) { OTOpndNewobj valueno = (OTOpndNewobj)value; if((valueno.ctor.DeclaringType == typeof(ScriptUndefinedStateException)) && (valueno.args.Length == 1) && (valueno.args[0] is OTOpndString)) { OTOpndString arg0 = (OTOpndString)valueno.args[0]; twout.Write("state " + arg0.value + "; /* throws undefined state exception */"); return; } } // throw newobj ScriptChangeStateException (n) => state n if(value is OTOpndNewobj) { OTOpndNewobj valueno = (OTOpndNewobj)value; if((valueno.ctor.DeclaringType == typeof(ScriptChangeStateException)) && (valueno.args.Length == 1) && (valueno.args[0] is OTOpndInt)) { OTOpndInt arg0 = (OTOpndInt)valueno.args[0]; twout.Write("state " + decompile.scriptObjCode.stateNames[arg0.value] + ';'); return; } } // throwing something else, output as is twout.Write("throw " + value.PrintableString + ';'); } } /** * Call with void return, or really anything that we discard the value of after computing it. */ private class OTStmtVoid: OTStmt { private OTOpnd value; public static void AddLast(OTDecompile decompile, OTOpnd value) { OTStmt it = OTStmtVoid.Make(value); if(it != null) decompile.AddLastStmt(it); } public static OTStmt Make(OTOpnd value) { if(!value.HasSideEffects) return null; OTStmtVoid it = new OTStmtVoid(); it.value = value; return it; } private OTStmtVoid() { } public override void CountRefs() { value.CountRefs(false); } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { bool rc = false; value = value.ReplaceOperand(oldopnd, newopnd, ref rc); return rc; } public override bool StripStuff(LinkedListNode link) { // strip out calls to CheckRunQuick() and CheckRunStack() if(value is OTOpndCall) { OTOpndCall call = (OTOpndCall)value; MethodInfo method = call.method; if((method.Name == _checkRunQuick) || (method.Name == _checkRunStack)) { link.List.Remove(link); return true; } } return false; } public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { return this; } public override void PrintStmt(TextWriter twout, string indent) { twout.Write(value.PrintableString + ';'); } } /***************************\ * Structured statements * \***************************/ /** * Block of statements. */ private class OTStmtBlock: OTStmt { public LinkedList blkstmts = new LinkedList(); public override void CountRefs() { foreach(OTStmt stmt in blkstmts) { stmt.CountRefs(); } } /** * Scrub out all references to behind-the-scenes parts and simplify. */ public override bool StripStuff(LinkedListNode link) { // loop through all sub-statements to strip out behind-the-scenes references bool rc = false; loop: for(LinkedListNode stmtlink = blkstmts.First; stmtlink != null; stmtlink = stmtlink.Next) { if(stmtlink.Value.StripStuff(stmtlink)) { rc = true; goto loop; } } if(rc) return true; // try to merge this block into outer block // change: // { // ... // { << link points here // ... // } // ... // } // to: // { // ... // ... // ... // } if(link != null) { LinkedListNode nextlink; while((nextlink = blkstmts.Last) != null) { nextlink.List.Remove(nextlink); link.List.AddAfter(link, nextlink); } link.List.Remove(link); return true; } return rc; } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { bool rc = false; foreach(OTStmt stmt in blkstmts) { rc |= stmt.ReplaceOperand(oldopnd, newopnd); } return rc; } /** * Check each statement in the block to see if it starts a do/for/if/while statement. */ public override bool DetectDoForIfWhile(LinkedListNode link) { bool rc = false; loop: for(link = blkstmts.First; link != null; link = link.Next) { if(link.Value.DetectDoForIfWhile(link)) { rc = true; goto loop; } } return rc; } /** * Assume we will never try to replace the block itself. * But go through all our sub-ordinates statements. */ public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { for(LinkedListNode childlink = blkstmts.First; childlink != null; childlink = childlink.Next) { childlink.Value = childlink.Value.ReplaceStatement(oldstmt, newstmt); } return this; } /** * Print out the block including its enclosed statements. */ public override void PrintStmt(TextWriter twout, string indent) { switch(blkstmts.Count) { case 0: { twout.Write("{ }"); break; } ////case 1: { //// blkstmts.First.Value.PrintStmt (twout, indent); //// break; ////} default: { twout.Write('{'); PrintBodyAndEnd(twout, indent); break; } } } public void PrintBodyAndEnd(TextWriter twout, string indent) { string newindent = indent + INDENT; foreach(OTStmt stmt in blkstmts) { twout.Write('\n' + indent); if(!(stmt is OTStmtLabel)) twout.Write(INDENT); else twout.Write(LABELINDENT); stmt.PrintStmt(twout, newindent); } twout.Write('\n' + indent + '}'); } } /** * 'do' statement. */ private class OTStmtDo: OTStmt { private OTOpnd dotest; private OTStmtBlock dobody; /** * See if we have a do loop... * @doloop_; << link points here * ... ... * [ if (dotest) ] jump doloop_; */ public static bool Detect(LinkedListNode link) { // see if we have label starting with 'doloop_' OTLabel looplabel = ((OTStmtLabel)link.Value).label; if(!looplabel.name.StartsWith(_doLoop)) return false; // good chance we have a do loop OTStmtDo it = new OTStmtDo(); // scan ahead looking for the terminating cond/jump loop // also gather up the statements for the do body block it.dobody = new OTStmtBlock(); LinkedListNode nextlink; for(nextlink = link.Next; nextlink != null; nextlink = nextlink.Next) { OTStmt nextstmt = nextlink.Value; // add statement to do body it.dobody.blkstmts.AddLast(nextlink.Value); // check for something what jumps to loop label // that gives us the end of the loop OTStmt maybejump = nextstmt; if(nextstmt is OTStmtCond) { maybejump = ((OTStmtCond)nextstmt).stmt; } if((maybejump is OTStmtJump) && (((OTStmtJump)maybejump).label == looplabel)) { break; } } // make sure we found the jump back to the loop label if(nextlink == null) return false; // remove all statements from caller's block including the continue label if any // but leave the break label alone it will be removed later if unreferenced // and leave the initial loop label intact for now for(LinkedListNode remlink = null; (remlink = link.Next) != null;) { link.List.Remove(remlink); if(remlink == nextlink) break; } // take test condition from last statement of body // it should be an cond/jump or just a jump to the loop label LinkedListNode lastlink = it.dobody.blkstmts.Last; OTStmt laststmt = lastlink.Value; if(laststmt is OTStmtCond) { it.dotest = ((OTStmtCond)laststmt).valu; } else { it.dotest = new OTOpndInt(1); } lastlink.List.Remove(lastlink); // finally replace the loop label with the whole do statement link.Value = it; // tell caller we made a change return true; } public override void CountRefs() { if(dotest != null) dotest.CountRefs(false); if(dobody != null) dobody.CountRefs(); } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { return dobody.ReplaceOperand(oldopnd, newopnd); } public override bool DetectDoForIfWhile(LinkedListNode link) { return dobody.DetectDoForIfWhile(link); } /** * Assume we won't replace the do statement itself. * But search all our sub-ordinate statements. */ public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { dobody = (OTStmtBlock)dobody.ReplaceStatement(oldstmt, newstmt); return this; } public override void PrintStmt(TextWriter twout, string indent) { // output do body twout.Write("do "); dobody.PrintStmt(twout, indent); // output while part twout.Write(" while (" + StripBrtrue(dotest).PrintableString + ");"); } } /** * 'for' or 'while' statement. */ private class OTStmtFor: OTStmt { private bool iswhile; private OTOpnd fortest; private OTStmtBlock forbody; private OTStmt forinit; private OTStmt forstep; /** * See if we have a for or while loop... * * @forloop_; << link points here * [ if () jump forbreak_; ] * ... ... * jump forloop_; * [ @forbreak_; ] */ public static bool Detect(LinkedListNode link, bool iswhile) { string loopname = iswhile ? _whileLoop : _forLoop; string breakname = iswhile ? _whileBreak : _forBreak; // see if we have label starting with 'forloop_' OTLabel looplabel = ((OTStmtLabel)link.Value).label; if(!looplabel.name.StartsWith(loopname)) return false; // good chance we have a for loop OTStmtFor it = new OTStmtFor(); it.iswhile = iswhile; // all labels end with this suffix string suffix = looplabel.name.Substring(loopname.Length); // scan ahead looking for the 'jump forloop_;' statement // also gather up the statements for the for body block it.forbody = new OTStmtBlock(); LinkedListNode lastlink; for(lastlink = link; (lastlink = lastlink.Next) != null;) { // check for jump forloop that tells us where loop ends if(lastlink.Value is OTStmtJump) { OTStmtJump lastjump = (OTStmtJump)lastlink.Value; if(lastjump.label == looplabel) break; } // add to body block it.forbody.blkstmts.AddLast(lastlink.Value); } // make sure we found the 'jump forloop' where the for loop ends if(lastlink == null) return false; // remove all statements from caller's block including final jump // but leave the loop label in place for(LinkedListNode nextlink = null; (nextlink = link.Next) != null;) { link.List.Remove(nextlink); if(nextlink == lastlink) break; } // if statement before loop label is an assignment, use it for the init statement if(!iswhile && (link.Previous != null) && (link.Previous.Value is OTStmtStore)) { it.forinit = link.Previous.Value; link.List.Remove(link.Previous); } // if first statement of for body is 'if (...) jump breaklabel' use it for the test value if((it.forbody.blkstmts.First != null) && (it.forbody.blkstmts.First.Value is OTStmtCond)) { OTStmtCond condstmt = (OTStmtCond)it.forbody.blkstmts.First.Value; if((condstmt.stmt is OTStmtJump) && (((OTStmtJump)condstmt.stmt).label.name == breakname + suffix)) { it.fortest = OTOpndUnOp.Make(MyOp.Brfalse, condstmt.valu); it.forbody.blkstmts.RemoveFirst(); } } // if last statement of body is an assigment, // use the assignment as the step statement if(!iswhile && (it.forbody.blkstmts.Last != null) && (it.forbody.blkstmts.Last.Value is OTStmtStore)) { LinkedListNode storelink = it.forbody.blkstmts.Last; storelink.List.Remove(storelink); it.forstep = storelink.Value; } // finally replace the loop label with the whole for statement link.Value = it; // tell caller we made a change return true; } public override void CountRefs() { if(fortest != null) fortest.CountRefs(false); if(forbody != null) forbody.CountRefs(); if(forinit != null) forinit.CountRefs(); if(forstep != null) forstep.CountRefs(); } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { return forbody.ReplaceOperand(oldopnd, newopnd) | ((forinit != null) && forinit.ReplaceOperand(oldopnd, newopnd)) | ((forstep != null) && forstep.ReplaceOperand(oldopnd, newopnd)); } public override bool DetectDoForIfWhile(LinkedListNode link) { return forbody.DetectDoForIfWhile(link) | ((forinit != null) && forinit.DetectDoForIfWhile(link)) | ((forstep != null) && forstep.DetectDoForIfWhile(link)); } /** * Assume we won't replace the for statement itself. * But search all our sub-ordinate statements. */ public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { forbody = (OTStmtBlock)forbody.ReplaceStatement(oldstmt, newstmt); if(forinit != null) forinit = forinit.ReplaceStatement(oldstmt, newstmt); if(forstep != null) forstep = forstep.ReplaceStatement(oldstmt, newstmt); return this; } public override void PrintStmt(TextWriter twout, string indent) { if(iswhile) { twout.Write("while ("); if(fortest == null) { twout.Write("TRUE"); } else { twout.Write(StripBrtrue(fortest).PrintableString); } } else { twout.Write("for ("); if(forinit != null) { forinit.PrintStmt(twout, indent + INDENT); } else { twout.Write(';'); } if(fortest != null) { twout.Write(' ' + StripBrtrue(fortest).PrintableString); } twout.Write(';'); if(forstep != null) { StringWriter sw = new StringWriter(); sw.Write(' '); forstep.PrintStmt(sw, indent + INDENT); StringBuilder sb = sw.GetStringBuilder(); int sl = sb.Length; if((sl > 0) && (sb[sl - 1] == ';')) sb.Remove(--sl, 1); twout.Write(sb.ToString()); } } twout.Write(") "); forbody.PrintStmt(twout, indent); } } /** * if/then/else block. */ private class OTStmtIf: OTStmt { private OTOpnd testvalu; private OTStmt thenstmt; private OTStmt elsestmt; // might be null /** * Try to detect a structured if statement. * * if (condition) jump ifdone_; << link points here * ... then body ... * @ifdone_; * * if (condition) jump ifelse_; * ... then body ... * jump ifdone_; << optional if true body doesn't fall through * @ifelse_; * ... else body ... * @ifdone_; */ public static bool Detect(LinkedListNode link) { OTStmtCond condstmt = (OTStmtCond)link.Value; if(!(condstmt.stmt is OTStmtJump)) return false; OTStmtJump jumpstmt = (OTStmtJump)condstmt.stmt; if(jumpstmt.label.name.StartsWith(_ifDone)) { // then-only if // skip forward to find the ifdone_ label // also save the intervening statements for the then body OTStmtBlock thenbody; LinkedListNode donelink = ScanForLabel(link, jumpstmt.label, out thenbody); // make sure we found matching label if(donelink == null) return false; // replace the jump ifdone_ with the OTStmtIf it = new OTStmtIf(); it.thenstmt = thenbody; // replace the test value with the opposite it.testvalu = OTOpndUnOp.Make(MyOp.Brfalse, condstmt.valu); condstmt.valu = null; // strip out the true body statements from the main code including the ifdone_ label StripInterveningStatements(link, donelink); // replace the simple conditional with the if/then/else block link.Value = it; // tell caller we changed something return true; } if(jumpstmt.label.name.StartsWith(_ifElse)) { string suffix = jumpstmt.label.name.Substring(_ifElse.Length); // if/then/else OTStmtIf it = new OTStmtIf(); // skip forward to find the ifelse_ label // also save the intervening statements for the true body OTStmtBlock thenbody; LinkedListNode elselink = ScanForLabel(link, jumpstmt.label, out thenbody); // make sure we found matching label if(elselink != null) { // the last statement of the then body might be a jump ifdone_ LinkedListNode lastthenlink = thenbody.blkstmts.Last; if((lastthenlink != null) && (lastthenlink.Value is OTStmtJump)) { OTStmtJump jumpifdone = (OTStmtJump)lastthenlink.Value; if(jumpifdone.label.name == _ifDone + suffix) { lastthenlink.List.Remove(lastthenlink); // skip forward to find the ifdone_ label // also save the intervening statements for the else body OTStmtBlock elsebody; LinkedListNode donelink = ScanForLabel(elselink, jumpifdone.label, out elsebody); if(donelink != null) { // replace the jump ifdone_ with the it.thenstmt = thenbody; // save the else body as well it.elsestmt = elsebody; // replace the test value with the opposite it.testvalu = OTOpndUnOp.Make(MyOp.Brfalse, condstmt.valu); condstmt.valu = null; // strip out the true and else body statements from the main code including the ifdone_ label StripInterveningStatements(link, donelink); // replace the simple conditional with the if/then/else block link.Value = it; // tell caller we changed something return true; } } } // missing the jump _ifDone_, so make it a simple if/then // if (condition) jump ifelse_; << link // ... then body ... << encapsulated in block thenbody // @ifelse_; << elselink // ... else body ... << still inline and leave it there // @ifdone_; << strip this out // replace the jump ifelse_ with the it.thenstmt = thenbody; // replace the test value with the opposite it.testvalu = OTOpndUnOp.Make(MyOp.Brfalse, condstmt.valu); condstmt.valu = null; // strip out the then body statements from the main code including the ifelse_ label StripInterveningStatements(link, elselink); // there's a dangling unused ifdone_ label ahead that has to be stripped for(LinkedListNode donelink = link; (donelink = donelink.Next) != null;) { if((donelink.Value is OTStmtLabel) && (((OTStmtLabel)donelink.Value).label.name == _ifDone + suffix)) { donelink.List.Remove(donelink); break; } } // replace the simple conditional with the if/then/else block link.Value = it; // tell caller we changed something return true; } } return false; } private OTStmtIf() { } public override void CountRefs() { if(testvalu != null) testvalu.CountRefs(false); if(thenstmt != null) thenstmt.CountRefs(); if(elsestmt != null) elsestmt.CountRefs(); } public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) { bool rc = thenstmt.ReplaceOperand(oldopnd, newopnd); testvalu = testvalu.ReplaceOperand(oldopnd, newopnd, ref rc); return rc; } public override bool DetectDoForIfWhile(LinkedListNode link) { return ((thenstmt != null) && thenstmt.DetectDoForIfWhile(link)) | ((elsestmt != null) && elsestmt.DetectDoForIfWhile(link)); } /** * Assume we won't replace the if statement itself. * But search all our sub-ordinate statements. */ public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) { thenstmt = thenstmt.ReplaceStatement(oldstmt, newstmt); if(elsestmt != null) elsestmt = elsestmt.ReplaceStatement(oldstmt, newstmt); return this; } public override void PrintStmt(TextWriter twout, string indent) { twout.Write("if (" + StripBrtrue(testvalu).PrintableString + ") "); OTStmt thenst = ReduceStmtBody(thenstmt, false); thenst.PrintStmt(twout, indent); if(elsestmt != null) { twout.Write('\n' + indent + "else "); OTStmt elsest = ReduceStmtBody(elsestmt, true); elsest.PrintStmt(twout, indent); } } // strip block off a single jump so it prints inline instead of with braces around it // also, if this is part of else, strip block for ifs to make else if statement private static OTStmt ReduceStmtBody(OTStmt statement, bool stripif) { OTStmt onestmt = statement; if((onestmt is OTStmtBlock) && (((OTStmtBlock)onestmt).blkstmts.Count == 1)) { onestmt = ((OTStmtBlock)onestmt).blkstmts.First.Value; if((onestmt is OTStmtJump) || (stripif && (onestmt is OTStmtIf))) { return onestmt; } } return statement; } /** * Scan forward for a given label definition. * Put intervening statements in a statement block. * @param link = start scanning after this statement * @param label = look for this label definition * @param block = where to return intervening statement block * @returns null: label definition not found * else: label definition statement */ private static LinkedListNode ScanForLabel(LinkedListNode link, OTLabel label, out OTStmtBlock block) { block = new OTStmtBlock(); while((link = link.Next) != null) { if(link.Value is OTStmtLabel) { if(((OTStmtLabel)link.Value).label == label) break; } block.blkstmts.AddLast(link.Value); } return link; } /** * Strip statements after link up to and including donelink. */ private static void StripInterveningStatements(LinkedListNode link, LinkedListNode donelink) { LinkedListNode striplink; do { striplink = link.Next; striplink.List.Remove(striplink); } while(striplink != donelink); } } private class MyOp { public int index; public OpCode sysop; public string name; public string source; private static Dictionary myopsbyname = new Dictionary(); private static int nextindex = 0; public MyOp(OpCode sysop) { this.index = nextindex++; this.sysop = sysop; this.name = sysop.Name; myopsbyname.Add(name, this); } public MyOp(OpCode sysop, string source) { this.index = nextindex++; this.sysop = sysop; this.name = sysop.Name; this.source = source; myopsbyname.Add(name, this); } public MyOp(string name) { this.index = nextindex++; this.name = name; myopsbyname.Add(name, this); } public MyOp(string name, string source) { this.index = nextindex++; this.name = name; this.source = source; myopsbyname.Add(name, this); } public static MyOp GetByName(string name) { return myopsbyname[name]; } public override string ToString() { return name; } // these copied from OpCodes.cs public static readonly MyOp Nop = new MyOp(OpCodes.Nop); public static readonly MyOp Break = new MyOp(OpCodes.Break); public static readonly MyOp Ldarg_0 = new MyOp(OpCodes.Ldarg_0); public static readonly MyOp Ldarg_1 = new MyOp(OpCodes.Ldarg_1); public static readonly MyOp Ldarg_2 = new MyOp(OpCodes.Ldarg_2); public static readonly MyOp Ldarg_3 = new MyOp(OpCodes.Ldarg_3); public static readonly MyOp Ldloc_0 = new MyOp(OpCodes.Ldloc_0); public static readonly MyOp Ldloc_1 = new MyOp(OpCodes.Ldloc_1); public static readonly MyOp Ldloc_2 = new MyOp(OpCodes.Ldloc_2); public static readonly MyOp Ldloc_3 = new MyOp(OpCodes.Ldloc_3); public static readonly MyOp Stloc_0 = new MyOp(OpCodes.Stloc_0); public static readonly MyOp Stloc_1 = new MyOp(OpCodes.Stloc_1); public static readonly MyOp Stloc_2 = new MyOp(OpCodes.Stloc_2); public static readonly MyOp Stloc_3 = new MyOp(OpCodes.Stloc_3); public static readonly MyOp Ldarg_S = new MyOp(OpCodes.Ldarg_S); public static readonly MyOp Ldarga_S = new MyOp(OpCodes.Ldarga_S); public static readonly MyOp Starg_S = new MyOp(OpCodes.Starg_S); public static readonly MyOp Ldloc_S = new MyOp(OpCodes.Ldloc_S); public static readonly MyOp Ldloca_S = new MyOp(OpCodes.Ldloca_S); public static readonly MyOp Stloc_S = new MyOp(OpCodes.Stloc_S); public static readonly MyOp Ldnull = new MyOp(OpCodes.Ldnull); public static readonly MyOp Ldc_I4_M1 = new MyOp(OpCodes.Ldc_I4_M1); public static readonly MyOp Ldc_I4_0 = new MyOp(OpCodes.Ldc_I4_0); public static readonly MyOp Ldc_I4_1 = new MyOp(OpCodes.Ldc_I4_1); public static readonly MyOp Ldc_I4_2 = new MyOp(OpCodes.Ldc_I4_2); public static readonly MyOp Ldc_I4_3 = new MyOp(OpCodes.Ldc_I4_3); public static readonly MyOp Ldc_I4_4 = new MyOp(OpCodes.Ldc_I4_4); public static readonly MyOp Ldc_I4_5 = new MyOp(OpCodes.Ldc_I4_5); public static readonly MyOp Ldc_I4_6 = new MyOp(OpCodes.Ldc_I4_6); public static readonly MyOp Ldc_I4_7 = new MyOp(OpCodes.Ldc_I4_7); public static readonly MyOp Ldc_I4_8 = new MyOp(OpCodes.Ldc_I4_8); public static readonly MyOp Ldc_I4_S = new MyOp(OpCodes.Ldc_I4_S); public static readonly MyOp Ldc_I4 = new MyOp(OpCodes.Ldc_I4); public static readonly MyOp Ldc_I8 = new MyOp(OpCodes.Ldc_I8); public static readonly MyOp Ldc_R4 = new MyOp(OpCodes.Ldc_R4); public static readonly MyOp Ldc_R8 = new MyOp(OpCodes.Ldc_R8); public static readonly MyOp Dup = new MyOp(OpCodes.Dup); public static readonly MyOp Pop = new MyOp(OpCodes.Pop); public static readonly MyOp Jmp = new MyOp(OpCodes.Jmp); public static readonly MyOp Call = new MyOp(OpCodes.Call); public static readonly MyOp Calli = new MyOp(OpCodes.Calli); public static readonly MyOp Ret = new MyOp(OpCodes.Ret); public static readonly MyOp Br_S = new MyOp(OpCodes.Br_S); public static readonly MyOp Brfalse_S = new MyOp(OpCodes.Brfalse_S); public static readonly MyOp Brtrue_S = new MyOp(OpCodes.Brtrue_S); public static readonly MyOp Beq_S = new MyOp(OpCodes.Beq_S, "=="); public static readonly MyOp Bge_S = new MyOp(OpCodes.Bge_S, ">="); public static readonly MyOp Bgt_S = new MyOp(OpCodes.Bgt_S, ">"); public static readonly MyOp Ble_S = new MyOp(OpCodes.Ble_S, "<="); public static readonly MyOp Blt_S = new MyOp(OpCodes.Blt_S, "<"); public static readonly MyOp Bne_Un_S = new MyOp(OpCodes.Bne_Un_S, "!="); public static readonly MyOp Bge_Un_S = new MyOp(OpCodes.Bge_Un_S); public static readonly MyOp Bgt_Un_S = new MyOp(OpCodes.Bgt_Un_S); public static readonly MyOp Ble_Un_S = new MyOp(OpCodes.Ble_Un_S); public static readonly MyOp Blt_Un_S = new MyOp(OpCodes.Blt_Un_S); public static readonly MyOp Br = new MyOp(OpCodes.Br); public static readonly MyOp Brfalse = new MyOp(OpCodes.Brfalse, "!"); public static readonly MyOp Brtrue = new MyOp(OpCodes.Brtrue, "!!"); public static readonly MyOp Beq = new MyOp(OpCodes.Beq, "=="); public static readonly MyOp Bge = new MyOp(OpCodes.Bge, ">="); public static readonly MyOp Bgt = new MyOp(OpCodes.Bgt, ">"); public static readonly MyOp Ble = new MyOp(OpCodes.Ble, "<="); public static readonly MyOp Blt = new MyOp(OpCodes.Blt, "<"); public static readonly MyOp Bne_Un = new MyOp(OpCodes.Bne_Un, "!="); public static readonly MyOp Bge_Un = new MyOp(OpCodes.Bge_Un); public static readonly MyOp Bgt_Un = new MyOp(OpCodes.Bgt_Un); public static readonly MyOp Ble_Un = new MyOp(OpCodes.Ble_Un); public static readonly MyOp Blt_Un = new MyOp(OpCodes.Blt_Un); public static readonly MyOp Switch = new MyOp(OpCodes.Switch); public static readonly MyOp Ldind_I1 = new MyOp(OpCodes.Ldind_I1); public static readonly MyOp Ldind_U1 = new MyOp(OpCodes.Ldind_U1); public static readonly MyOp Ldind_I2 = new MyOp(OpCodes.Ldind_I2); public static readonly MyOp Ldind_U2 = new MyOp(OpCodes.Ldind_U2); public static readonly MyOp Ldind_I4 = new MyOp(OpCodes.Ldind_I4); public static readonly MyOp Ldind_U4 = new MyOp(OpCodes.Ldind_U4); public static readonly MyOp Ldind_I8 = new MyOp(OpCodes.Ldind_I8); public static readonly MyOp Ldind_I = new MyOp(OpCodes.Ldind_I); public static readonly MyOp Ldind_R4 = new MyOp(OpCodes.Ldind_R4); public static readonly MyOp Ldind_R8 = new MyOp(OpCodes.Ldind_R8); public static readonly MyOp Ldind_Ref = new MyOp(OpCodes.Ldind_Ref); public static readonly MyOp Stind_Ref = new MyOp(OpCodes.Stind_Ref); public static readonly MyOp Stind_I1 = new MyOp(OpCodes.Stind_I1); public static readonly MyOp Stind_I2 = new MyOp(OpCodes.Stind_I2); public static readonly MyOp Stind_I4 = new MyOp(OpCodes.Stind_I4); public static readonly MyOp Stind_I8 = new MyOp(OpCodes.Stind_I8); public static readonly MyOp Stind_R4 = new MyOp(OpCodes.Stind_R4); public static readonly MyOp Stind_R8 = new MyOp(OpCodes.Stind_R8); public static readonly MyOp Add = new MyOp(OpCodes.Add, "+"); public static readonly MyOp Sub = new MyOp(OpCodes.Sub, "-"); public static readonly MyOp Mul = new MyOp(OpCodes.Mul, "*"); public static readonly MyOp Div = new MyOp(OpCodes.Div, "/"); public static readonly MyOp Div_Un = new MyOp(OpCodes.Div_Un); public static readonly MyOp Rem = new MyOp(OpCodes.Rem, "%"); public static readonly MyOp Rem_Un = new MyOp(OpCodes.Rem_Un); public static readonly MyOp And = new MyOp(OpCodes.And, "&"); public static readonly MyOp Or = new MyOp(OpCodes.Or, "|"); public static readonly MyOp Xor = new MyOp(OpCodes.Xor, "^"); public static readonly MyOp Shl = new MyOp(OpCodes.Shl, "<<"); public static readonly MyOp Shr = new MyOp(OpCodes.Shr, ">>"); public static readonly MyOp Shr_Un = new MyOp(OpCodes.Shr_Un); public static readonly MyOp Neg = new MyOp(OpCodes.Neg, "-"); public static readonly MyOp Not = new MyOp(OpCodes.Not, "~"); public static readonly MyOp Conv_I1 = new MyOp(OpCodes.Conv_I1); public static readonly MyOp Conv_I2 = new MyOp(OpCodes.Conv_I2); public static readonly MyOp Conv_I4 = new MyOp(OpCodes.Conv_I4); public static readonly MyOp Conv_I8 = new MyOp(OpCodes.Conv_I8); public static readonly MyOp Conv_R4 = new MyOp(OpCodes.Conv_R4); public static readonly MyOp Conv_R8 = new MyOp(OpCodes.Conv_R8); public static readonly MyOp Conv_U4 = new MyOp(OpCodes.Conv_U4); public static readonly MyOp Conv_U8 = new MyOp(OpCodes.Conv_U8); public static readonly MyOp Callvirt = new MyOp(OpCodes.Callvirt); public static readonly MyOp Cpobj = new MyOp(OpCodes.Cpobj); public static readonly MyOp Ldobj = new MyOp(OpCodes.Ldobj); public static readonly MyOp Ldstr = new MyOp(OpCodes.Ldstr); public static readonly MyOp Newobj = new MyOp(OpCodes.Newobj); public static readonly MyOp Castclass = new MyOp(OpCodes.Castclass); public static readonly MyOp Isinst = new MyOp(OpCodes.Isinst); public static readonly MyOp Conv_R_Un = new MyOp(OpCodes.Conv_R_Un); public static readonly MyOp Unbox = new MyOp(OpCodes.Unbox); public static readonly MyOp Throw = new MyOp(OpCodes.Throw); public static readonly MyOp Ldfld = new MyOp(OpCodes.Ldfld); public static readonly MyOp Ldflda = new MyOp(OpCodes.Ldflda); public static readonly MyOp Stfld = new MyOp(OpCodes.Stfld); public static readonly MyOp Ldsfld = new MyOp(OpCodes.Ldsfld); public static readonly MyOp Ldsflda = new MyOp(OpCodes.Ldsflda); public static readonly MyOp Stsfld = new MyOp(OpCodes.Stsfld); public static readonly MyOp Stobj = new MyOp(OpCodes.Stobj); public static readonly MyOp Conv_Ovf_I1_Un = new MyOp(OpCodes.Conv_Ovf_I1_Un); public static readonly MyOp Conv_Ovf_I2_Un = new MyOp(OpCodes.Conv_Ovf_I2_Un); public static readonly MyOp Conv_Ovf_I4_Un = new MyOp(OpCodes.Conv_Ovf_I4_Un); public static readonly MyOp Conv_Ovf_I8_Un = new MyOp(OpCodes.Conv_Ovf_I8_Un); public static readonly MyOp Conv_Ovf_U1_Un = new MyOp(OpCodes.Conv_Ovf_U1_Un); public static readonly MyOp Conv_Ovf_U2_Un = new MyOp(OpCodes.Conv_Ovf_U2_Un); public static readonly MyOp Conv_Ovf_U4_Un = new MyOp(OpCodes.Conv_Ovf_U4_Un); public static readonly MyOp Conv_Ovf_U8_Un = new MyOp(OpCodes.Conv_Ovf_U8_Un); public static readonly MyOp Conv_Ovf_I_Un = new MyOp(OpCodes.Conv_Ovf_I_Un); public static readonly MyOp Conv_Ovf_U_Un = new MyOp(OpCodes.Conv_Ovf_U_Un); public static readonly MyOp Box = new MyOp(OpCodes.Box); public static readonly MyOp Newarr = new MyOp(OpCodes.Newarr); public static readonly MyOp Ldlen = new MyOp(OpCodes.Ldlen); public static readonly MyOp Ldelema = new MyOp(OpCodes.Ldelema); public static readonly MyOp Ldelem_I1 = new MyOp(OpCodes.Ldelem_I1); public static readonly MyOp Ldelem_U1 = new MyOp(OpCodes.Ldelem_U1); public static readonly MyOp Ldelem_I2 = new MyOp(OpCodes.Ldelem_I2); public static readonly MyOp Ldelem_U2 = new MyOp(OpCodes.Ldelem_U2); public static readonly MyOp Ldelem_I4 = new MyOp(OpCodes.Ldelem_I4); public static readonly MyOp Ldelem_U4 = new MyOp(OpCodes.Ldelem_U4); public static readonly MyOp Ldelem_I8 = new MyOp(OpCodes.Ldelem_I8); public static readonly MyOp Ldelem_I = new MyOp(OpCodes.Ldelem_I); public static readonly MyOp Ldelem_R4 = new MyOp(OpCodes.Ldelem_R4); public static readonly MyOp Ldelem_R8 = new MyOp(OpCodes.Ldelem_R8); public static readonly MyOp Ldelem_Ref = new MyOp(OpCodes.Ldelem_Ref); public static readonly MyOp Stelem_I = new MyOp(OpCodes.Stelem_I); public static readonly MyOp Stelem_I1 = new MyOp(OpCodes.Stelem_I1); public static readonly MyOp Stelem_I2 = new MyOp(OpCodes.Stelem_I2); public static readonly MyOp Stelem_I4 = new MyOp(OpCodes.Stelem_I4); public static readonly MyOp Stelem_I8 = new MyOp(OpCodes.Stelem_I8); public static readonly MyOp Stelem_R4 = new MyOp(OpCodes.Stelem_R4); public static readonly MyOp Stelem_R8 = new MyOp(OpCodes.Stelem_R8); public static readonly MyOp Stelem_Ref = new MyOp(OpCodes.Stelem_Ref); public static readonly MyOp Ldelem = new MyOp(OpCodes.Ldelem); public static readonly MyOp Stelem = new MyOp(OpCodes.Stelem); public static readonly MyOp Unbox_Any = new MyOp(OpCodes.Unbox_Any); public static readonly MyOp Conv_Ovf_I1 = new MyOp(OpCodes.Conv_Ovf_I1); public static readonly MyOp Conv_Ovf_U1 = new MyOp(OpCodes.Conv_Ovf_U1); public static readonly MyOp Conv_Ovf_I2 = new MyOp(OpCodes.Conv_Ovf_I2); public static readonly MyOp Conv_Ovf_U2 = new MyOp(OpCodes.Conv_Ovf_U2); public static readonly MyOp Conv_Ovf_I4 = new MyOp(OpCodes.Conv_Ovf_I4); public static readonly MyOp Conv_Ovf_U4 = new MyOp(OpCodes.Conv_Ovf_U4); public static readonly MyOp Conv_Ovf_I8 = new MyOp(OpCodes.Conv_Ovf_I8); public static readonly MyOp Conv_Ovf_U8 = new MyOp(OpCodes.Conv_Ovf_U8); public static readonly MyOp Refanyval = new MyOp(OpCodes.Refanyval); public static readonly MyOp Ckfinite = new MyOp(OpCodes.Ckfinite); public static readonly MyOp Mkrefany = new MyOp(OpCodes.Mkrefany); public static readonly MyOp Ldtoken = new MyOp(OpCodes.Ldtoken); public static readonly MyOp Conv_U2 = new MyOp(OpCodes.Conv_U2); public static readonly MyOp Conv_U1 = new MyOp(OpCodes.Conv_U1); public static readonly MyOp Conv_I = new MyOp(OpCodes.Conv_I); public static readonly MyOp Conv_Ovf_I = new MyOp(OpCodes.Conv_Ovf_I); public static readonly MyOp Conv_Ovf_U = new MyOp(OpCodes.Conv_Ovf_U); public static readonly MyOp Add_Ovf = new MyOp(OpCodes.Add_Ovf); public static readonly MyOp Add_Ovf_Un = new MyOp(OpCodes.Add_Ovf_Un); public static readonly MyOp Mul_Ovf = new MyOp(OpCodes.Mul_Ovf); public static readonly MyOp Mul_Ovf_Un = new MyOp(OpCodes.Mul_Ovf_Un); public static readonly MyOp Sub_Ovf = new MyOp(OpCodes.Sub_Ovf); public static readonly MyOp Sub_Ovf_Un = new MyOp(OpCodes.Sub_Ovf_Un); public static readonly MyOp Endfinally = new MyOp(OpCodes.Endfinally); public static readonly MyOp Leave = new MyOp(OpCodes.Leave); public static readonly MyOp Leave_S = new MyOp(OpCodes.Leave_S); public static readonly MyOp Stind_I = new MyOp(OpCodes.Stind_I); public static readonly MyOp Conv_U = new MyOp(OpCodes.Conv_U); public static readonly MyOp Prefix7 = new MyOp(OpCodes.Prefix7); public static readonly MyOp Prefix6 = new MyOp(OpCodes.Prefix6); public static readonly MyOp Prefix5 = new MyOp(OpCodes.Prefix5); public static readonly MyOp Prefix4 = new MyOp(OpCodes.Prefix4); public static readonly MyOp Prefix3 = new MyOp(OpCodes.Prefix3); public static readonly MyOp Prefix2 = new MyOp(OpCodes.Prefix2); public static readonly MyOp Prefix1 = new MyOp(OpCodes.Prefix1); public static readonly MyOp Prefixref = new MyOp(OpCodes.Prefixref); public static readonly MyOp Arglist = new MyOp(OpCodes.Arglist); public static readonly MyOp Ceq = new MyOp(OpCodes.Ceq, "=="); public static readonly MyOp Cgt = new MyOp(OpCodes.Cgt, ">"); public static readonly MyOp Cgt_Un = new MyOp(OpCodes.Cgt_Un); public static readonly MyOp Clt = new MyOp(OpCodes.Clt, "<"); public static readonly MyOp Clt_Un = new MyOp(OpCodes.Clt_Un); public static readonly MyOp Ldftn = new MyOp(OpCodes.Ldftn); public static readonly MyOp Ldvirtftn = new MyOp(OpCodes.Ldvirtftn); public static readonly MyOp Ldarg = new MyOp(OpCodes.Ldarg); public static readonly MyOp Ldarga = new MyOp(OpCodes.Ldarga); public static readonly MyOp Starg = new MyOp(OpCodes.Starg); public static readonly MyOp Ldloc = new MyOp(OpCodes.Ldloc); public static readonly MyOp Ldloca = new MyOp(OpCodes.Ldloca); public static readonly MyOp Stloc = new MyOp(OpCodes.Stloc); public static readonly MyOp Localloc = new MyOp(OpCodes.Localloc); public static readonly MyOp Endfilter = new MyOp(OpCodes.Endfilter); public static readonly MyOp Unaligned = new MyOp(OpCodes.Unaligned); public static readonly MyOp Volatile = new MyOp(OpCodes.Volatile); public static readonly MyOp Tailcall = new MyOp(OpCodes.Tailcall); public static readonly MyOp Initobj = new MyOp(OpCodes.Initobj); public static readonly MyOp Constrained = new MyOp(OpCodes.Constrained); public static readonly MyOp Cpblk = new MyOp(OpCodes.Cpblk); public static readonly MyOp Initblk = new MyOp(OpCodes.Initblk); public static readonly MyOp Rethrow = new MyOp(OpCodes.Rethrow); public static readonly MyOp Sizeof = new MyOp(OpCodes.Sizeof); public static readonly MyOp Refanytype = new MyOp(OpCodes.Refanytype); public static readonly MyOp Readonly = new MyOp(OpCodes.Readonly); // used internally public static readonly MyOp Cge = new MyOp("cge", ">="); public static readonly MyOp Cle = new MyOp("cle", "<="); public static readonly MyOp Cne = new MyOp("cne", "!="); } } }