/* * 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 xmrobj file. * See xmrengcomp.cx utility program. */ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * 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.XMREngine.")) { 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", "!="); } } }