OpenSimMirror/OpenSim/Region/ScriptEngine/YEngine/XMRObjectTokens.cs

6297 lines
230 KiB
C#

/*
* 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.Yengine
{
/*
* Encapsulate object code for a method.
*/
public abstract class ObjectTokens
{
public ScriptObjCode scriptObjCode;
public ObjectTokens(ScriptObjCode scriptObjCode)
{
this.scriptObjCode = scriptObjCode;
}
public abstract void Close();
public abstract void BegMethod(DynamicMethod method);
public abstract void EndMethod();
public abstract void DefineLabel(int number, string name);
public abstract void DefineLocal(int number, string name, string type, Type syType);
public abstract void DefineMethod(string methName, Type retType, Type[] argTypes, string[] argNames);
public abstract void MarkLabel(int offset, int number);
public abstract void BegExcBlk(int offset);
public abstract void BegCatBlk(int offset, Type excType);
public abstract void BegFinBlk(int offset);
public abstract void EndExcBlk(int offset);
public abstract void EmitNull(int offset, OpCode opCode);
public abstract void EmitField(int offset, OpCode opCode, FieldInfo field);
public abstract void EmitLocal(int offset, OpCode opCode, int number);
public abstract void EmitType(int offset, OpCode opCode, Type type);
public abstract void EmitLabel(int offset, OpCode opCode, int number);
public abstract void EmitLabels(int offset, OpCode opCode, int[] numbers);
public abstract void EmitMethod(int offset, OpCode opCode, MethodInfo method);
public abstract void EmitCtor(int offset, OpCode opCode, ConstructorInfo ctor);
public abstract void EmitDouble(int offset, OpCode opCode, double value);
public abstract void EmitFloat(int offset, OpCode opCode, float value);
public abstract void EmitInteger(int offset, OpCode opCode, int value);
public abstract void EmitString(int offset, OpCode opCode, string value);
}
/******************\
* DISASSEMBLER *
\******************/
public class OTDisassemble: ObjectTokens
{
private static readonly int OPCSTRWIDTH = 12;
private Dictionary<int, string> labelNames;
private Dictionary<int, string> 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<int, string>();
localNames = new Dictionary<int, string>();
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<string, string> typeTranslator = InitTypeTranslator();
private static Dictionary<string, string> InitTypeTranslator()
{
Dictionary<string, string> d = new Dictionary<string, string>();
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<int, OTLocal> eharglist;
private Dictionary<int, OTLabel> labels;
private Dictionary<int, OTLocal> locals;
private Dictionary<string, string[]> methargnames;
private LinkedList<OTCilInstr> cilinstrs;
private OTStmtBlock topBlock;
private Stack<OTOpnd> opstack;
private Stack<OTStmtBegExcBlk> trystack;
private Stack<OTStmtBlock> 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<string, string[]>();
}
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<int, OTLocal>();
labels = new Dictionary<int, OTLabel>();
locals = new Dictionary<int, OTLocal>();
cilinstrs = new LinkedList<OTCilInstr>();
opstack = new Stack<OTOpnd>();
trystack = new Stack<OTStmtBegExcBlk>();
blockstack = new Stack<OTStmtBlock>();
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<OTCilInstr> 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_<suffix>; 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<int> 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<string, int> namecounts = new Dictionary<string, int>();
foreach(Dictionary<int, string> 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<string, string> inittypes = new Dictionary<string, string>();
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<int, string> varnames = scriptObjCode.globalVarNames[iartypename];
string typename = iartypename.ToLowerInvariant();
if(typename.StartsWith("iar"))
typename = typename.Substring(3);
if(typename.EndsWith("s"))
typename = typename.Substring(0, typename.Length - 1);
foreach(string varname in varnames.Values)
{
if(!badinit && inittypes.ContainsKey(varname))
{
inittypes[varname] = typename;
}
else
{
if(first)
twout.Write('\n');
twout.Write('\n' + typename + ' ' + varname + ';');
first = false;
}
}
}
/*
* If $globalvarinit() has anything bad in it, output it as a function.
* Otherwise, output it as a series of global declarations with init values.
*/
if(badinit)
{
MethodDump();
}
else
{
foreach(OTStmt stmt in topBlock.blkstmts)
{
OTStmtStore store = (OTStmtStore)stmt;
OTOpndGlobal globalop = (OTOpndGlobal)store.varwr;
string name = globalop.PrintableString;
if(first)
twout.Write('\n');
twout.Write('\n' + inittypes[name] + ' ');
store.PrintStmt(twout, "");
first = false;
}
}
}
/**
* Generate output for other functions.
*/
private void MethodDump()
{
string indent;
/*
* Event handlers don't have an argument list as such in the original
* code. Instead they have a series of assignments from ehargs[] to
* local variables. So make those local variables look like they are
* an argument list.
*/
int i = method.Name.IndexOf(' ');
if(i >= 0)
{
/*
* Maybe we have to output the state name.
*/
string statename = method.Name.Substring(0, i);
string eventname = method.Name.Substring(++i);
if(laststate != statename)
{
if(laststate != null)
twout.Write("\n}");
if(statename == "default")
{
twout.Write("\n\ndefault {");
}
else
{
twout.Write("\n\nstate " + statename + " {");
}
laststate = statename;
}
else
{
twout.Write('\n');
}
/*
* Output event name and argument list.
* Remove from locals list so they don't print below.
*/
twout.Write('\n' + INDENT + eventname + " (");
MethodInfo meth = typeof(IEventHandlers).GetMethod(eventname);
i = 0;
foreach(ParameterInfo pi in meth.GetParameters())
{
// skip the first param cuz it's the XMRInstance arg
if(i > 0)
twout.Write(", ");
OTLocal local;
if(eharglist.TryGetValue(i, out local) && locals.ContainsKey(local.number))
{
twout.Write(local.DumpString());
locals.Remove(local.number);
}
else
{
// maybe the assignment was removed
// eg, because the local was write-only (not referenced)
// so substitute in placeholder that won't be referenced
twout.Write(AbbrType(pi.ParameterType) + " arg$" + (i + 1));
}
i++;
}
twout.Write(')');
/*
* Indent method body by 4 spaces.
*/
indent = INDENT;
}
else
{
/*
* Maybe need to close out previous state.
*/
if(laststate != null)
{
twout.Write("\n}");
laststate = null;
}
/*
* Output blank line and return type (if any).
*/
twout.Write("\n\n");
if(method.ReturnType != typeof(void))
{
twout.Write(AbbrType(method.ReturnType) + ' ');
}
/*
* Output method name and argument list.
*/
int j = method.Name.IndexOf('(');
if(j < 0)
{
twout.Write(method.Name);
}
else
{
twout.Write(method.Name.Substring(0, j) + " (");
bool first = true;
j = 0;
foreach(ParameterInfo pi in method.GetParameters())
{
if(j > 0)
{ // skip the XMRInstance arg$0 parameter
if(!first)
twout.Write(", ");
twout.Write(AbbrType(pi.ParameterType) + ' ' + MethArgName(j));
first = false;
}
j++;
}
twout.Write(')');
}
/*
* Don't indent method body at all.
*/
indent = "";
}
/*
* Output local variable declarations.
*/
twout.Write('\n' + indent + '{');
bool didOne = false;
foreach(OTLocal local in locals.Values)
{
twout.Write('\n' + indent + INDENT + local.DumpString() + "; // r:" + local.nlclreads + " w:" + local.nlclwrites);
didOne = true;
}
if(didOne)
twout.Write('\n');
/*
* Output statements.
*/
if(topBlock.blkstmts.Count == 0)
{
twout.Write(" }");
}
else
{
topBlock.PrintBodyAndEnd(twout, indent);
}
}
/**
* Get abbreviated type string.
*/
public static string AbbrType(Type type)
{
if(type == null)
return "null";
return AbbrType(type.Name);
}
public static string AbbrType(string type)
{
if(type.StartsWith("OpenSim.Region.ScriptEngine.YEngine."))
{
type = type.Substring(38);
int i = type.IndexOf(',');
if(i > 0)
type = type.Substring(0, i);
}
if(typeTranslator.ContainsKey(type))
{
type = typeTranslator[type];
}
return type;
}
/**
* Get current method's argument name.
*/
public string MethArgName(int index)
{
string[] argnames;
if(methargnames.TryGetValue(method.Name, out argnames) && (index < argnames.Length))
{
return argnames[index];
}
return "arg$" + index;
}
/**
* Strip svperflvovs (float) cast from rotation/vector values.
*/
public static OTOpnd StripFloatCast(OTOpnd op)
{
if(op is OTOpndCast)
{
OTOpndCast opcast = (OTOpndCast)op;
if((opcast.type == typeof(double)) && (opcast.value is OTOpndInt))
{
return opcast.value;
}
}
return op;
}
/**
* Strip svperflvovs Brtrues so we don't end up with stuff like 'if (!! someint) ...'.
*/
public static OTOpnd StripBrtrue(OTOpnd op)
{
if(op is OTOpndUnOp)
{
OTOpndUnOp opunop = (OTOpndUnOp)op;
if(opunop.opCode == MyOp.Brtrue)
return opunop.value;
}
return op;
}
/*
* Local variable declaration.
*/
private class OTLocal
{
public int number;
public string name;
public string type;
public int nlclreads;
public int nlclwrites;
public OTLocal(int number, string name, string type)
{
this.number = number;
this.name = name.StartsWith("tmp$") ? name : name + "$" + number;
this.type = type;
}
public string DumpString()
{
return AbbrType(type) + ' ' + name;
}
}
/***********************************************\
* Tokens that are one-for-one with CIL code *
\***********************************************/
/*
* Part of instruction stream.
*/
public abstract class OTCilInstr
{
public int offset; // cil offset
public OTCilInstr(int offset)
{
this.offset = offset;
}
public abstract string DumpString();
public abstract void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> 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<OTCilInstr> link)
{
OTStmtLabel.AddLast(decompile, this);
}
}
/*
* 'try {'
*/
private class OTCilBegExcBlk: OTCilInstr
{
public LinkedList<OTCilBegCatBlk> catches = new LinkedList<OTCilBegCatBlk>();
public OTCilBegExcBlk(int offset) : base(offset)
{
}
public override string DumpString()
{
return "try {";
}
public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<OTCilInstr> 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<type>[<intconst>] is a reference to a global variable
* likewise so is __xmrinst.glblVars.iar<type>[<intconst>]
*/
if((array is OTOpndField) && (index is OTOpndInt))
{
/*
* arrayfield = (arg$0.glblVars).iar<type>
* arrayfieldobj = arg$0.glblVars
* iartypename = iar<type>
*/
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<string, string> xor1ops = InitXor1Ops();
private static Dictionary<string, string> InitXor1Ops()
{
Dictionary<string, string> d = new Dictionary<string, string>();
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<string, int> precedence = InitPrecedence();
private static Dictionary<string, int> InitPrecedence()
{
Dictionary<string, int> d = new Dictionary<string, int>();
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<string, MethodInfo> mathmeths = InitMathMeths();
private static Dictionary<string, MethodInfo> InitMathMeths()
{
Dictionary<string, MethodInfo> d = new Dictionary<string, MethodInfo>();
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<List,Object,String>
* 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<whatever> 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$<n> = newarr object[<m>] << link points here
* dup$<n>[0] = bla
* dup$<n>[1] = bla
* ...
* ... newobj list (dup$<n>) ...
*/
public static bool Detect(LinkedListNode<OTStmt> link)
{
if(link == null)
return false;
/*
* Check for 'dup$<n> = newarr object[<m>]' and get listsize from <m>.
*/
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$<n>[<i>] = bla
* If so, save the bla values in the values[] array.
*/
LinkedListNode<OTStmt> 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$<n>)' 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<OTStmt> 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) => <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) => <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<string, string> binops = InitBinops();
private static Dictionary<string, string> InitBinops()
{
Dictionary<string, string> d = new Dictionary<string, string>();
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<string, string> brfops = InitBrfOps();
private static Dictionary<string, string> InitBrfOps()
{
Dictionary<string, string> d = new Dictionary<string, string>();
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<OTStmt> 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<OTStmt> 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<OTStmt> link)
{
// strip all statements following jump until seeing some label
bool rc = false;
if(link != null)
{
LinkedListNode<OTStmt> 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<OTStmt> link)
{
return catchblock.StripStuff(null);
}
public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd)
{
return catchblock.ReplaceOperand(oldopnd, newopnd);
}
public override bool DetectDoForIfWhile(LinkedListNode<OTStmt> 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<OTStmtBegCatBlk> catches = new LinkedList<OTStmtBegCatBlk>();
// 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<OTStmt> 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<OTStmt> 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<OTStmtBegCatBlk> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> ScanForLabel(LinkedListNode<OTStmt> 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<OTStmt> link, LinkedListNode<OTStmt> donelink)
{
LinkedListNode<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> blkstmts = new LinkedList<OTStmt>();
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<OTStmt> link)
{
// loop through all sub-statements to strip out behind-the-scenes references
bool rc = false;
loop:
for(LinkedListNode<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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_<suffix>; << link points here
* ... <dobody> ...
* [ if (dotest) ] jump doloop_<suffix>;
*/
public static bool Detect(LinkedListNode<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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...
* <forinit>
* @forloop_<suffix>; << link points here
* [ if (<fortest>) jump forbreak_<suffix>; ]
* ... <forbody> ...
* jump forloop_<suffix>;
* [ @forbreak_<suffix>; ]
*/
public static bool Detect(LinkedListNode<OTStmt> 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_<suffix>;' statement
// also gather up the statements for the for body block
it.forbody = new OTStmtBlock();
LinkedListNode<OTStmt> 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<OTStmt> 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<OTStmt> 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<OTStmt> 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_<suffix>; << link points here
* ... then body ...
* @ifdone_<suffix>;
*
* if (condition) jump ifelse_<suffix>;
* ... then body ...
* jump ifdone_<suffix>; << optional if true body doesn't fall through
* @ifelse_<suffix>;
* ... else body ...
* @ifdone_<suffix>;
*/
public static bool Detect(LinkedListNode<OTStmt> 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_<suffix> label
// also save the intervening statements for the then body
OTStmtBlock thenbody;
LinkedListNode<OTStmt> donelink = ScanForLabel(link, jumpstmt.label, out thenbody);
// make sure we found matching label
if(donelink == null)
return false;
// replace the jump ifdone_<suffix> with the <then body>
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_<suffix> 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_<suffix> label
// also save the intervening statements for the true body
OTStmtBlock thenbody;
LinkedListNode<OTStmt> 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_<suffix>
LinkedListNode<OTStmt> 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_<suffix> label
// also save the intervening statements for the else body
OTStmtBlock elsebody;
LinkedListNode<OTStmt> donelink = ScanForLabel(elselink, jumpifdone.label, out elsebody);
if(donelink != null)
{
// replace the jump ifdone_<suffix> with the <true body>
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_<suffix> 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_<suffix>, so make it a simple if/then
// if (condition) jump ifelse_<suffix>; << link
// ... then body ... << encapsulated in block thenbody
// @ifelse_<suffix>; << elselink
// ... else body ... << still inline and leave it there
// @ifdone_<suffix>; << strip this out
// replace the jump ifelse_<suffix> with the <true body>
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_<suffix> label
StripInterveningStatements(link, elselink);
// there's a dangling unused ifdone_<suffix> label ahead that has to be stripped
for(LinkedListNode<OTStmt> 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<OTStmt> 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<OTStmt> ScanForLabel(LinkedListNode<OTStmt> 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<OTStmt> link, LinkedListNode<OTStmt> donelink)
{
LinkedListNode<OTStmt> 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<string, MyOp> myopsbyname = new Dictionary<string, MyOp>();
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", "!=");
}
}
}