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

5477 lines
219 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.XMREngine {
/*
* Encapsulate object code for a method.
*/
public abstract class ObjectTokens {
public ScriptObjCode scriptObjCode;
public ObjectTokens (ScriptObjCode scriptObjCode)
{
this.scriptObjCode = scriptObjCode;
}
public abstract void Close ();
public abstract void BegMethod (DynamicMethod method);
public abstract void EndMethod ();
public abstract void DefineLabel (int number, string name);
public abstract void DefineLocal (int number, string name, string type, Type syType);
public abstract void DefineMethod (string methName, Type retType, Type[] argTypes, string[] argNames);
public abstract void MarkLabel (int offset, int number);
public abstract void BegExcBlk (int offset);
public abstract void BegCatBlk (int offset, Type excType);
public abstract void BegFinBlk (int offset);
public abstract void EndExcBlk (int offset);
public abstract void EmitNull (int offset, OpCode opCode);
public abstract void EmitField (int offset, OpCode opCode, FieldInfo field);
public abstract void EmitLocal (int offset, OpCode opCode, int number);
public abstract void EmitType (int offset, OpCode opCode, Type type);
public abstract void EmitLabel (int offset, OpCode opCode, int number);
public abstract void EmitLabels (int offset, OpCode opCode, int[] numbers);
public abstract void EmitMethod (int offset, OpCode opCode, MethodInfo method);
public abstract void EmitCtor (int offset, OpCode opCode, ConstructorInfo ctor);
public abstract void EmitDouble (int offset, OpCode opCode, double value);
public abstract void EmitFloat (int offset, OpCode opCode, float value);
public abstract void EmitInteger (int offset, OpCode opCode, int value);
public abstract void EmitString (int offset, OpCode opCode, string value);
}
/******************\
* DISASSEMBLER *
\******************/
public class OTDisassemble : ObjectTokens {
private static readonly int OPCSTRWIDTH = 12;
private Dictionary<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.XMREngine.")) {
type = type.Substring (38);
int i = type.IndexOf (',');
if (i > 0) type = type.Substring (0, i);
}
if (typeTranslator.ContainsKey (type)) {
type = typeTranslator[type];
}
return type;
}
/**
* Get current method's argument name.
*/
public string MethArgName (int index)
{
string[] argnames;
if (methargnames.TryGetValue (method.Name, out argnames) && (index < argnames.Length)) {
return argnames[index];
}
return "arg$" + index;
}
/**
* Strip svperflvovs (float) cast from rotation/vector values.
*/
public static OTOpnd StripFloatCast (OTOpnd op)
{
if (op is OTOpndCast) {
OTOpndCast opcast = (OTOpndCast) op;
if ((opcast.type == typeof (double)) && (opcast.value is OTOpndInt)) {
return opcast.value;
}
}
return op;
}
/**
* Strip svperflvovs Brtrues so we don't end up with stuff like 'if (!! someint) ...'.
*/
public static OTOpnd StripBrtrue (OTOpnd op)
{
if (op is OTOpndUnOp) {
OTOpndUnOp opunop = (OTOpndUnOp) op;
if (opunop.opCode == MyOp.Brtrue) return opunop.value;
}
return op;
}
/*
* Local variable declaration.
*/
private class OTLocal {
public int number;
public string name;
public string type;
public int nlclreads;
public int nlclwrites;
public OTLocal (int number, string name, string type)
{
this.number = number;
this.name = name.StartsWith ("tmp$") ? name : name + "$" + number;
this.type = type;
}
public string DumpString ()
{
return AbbrType (type) + ' ' + name;
}
}
/***********************************************\
* Tokens that are one-for-one with CIL code *
\***********************************************/
/*
* Part of instruction stream.
*/
public abstract class OTCilInstr {
public int offset; // cil offset
public OTCilInstr (int offset)
{
this.offset = offset;
}
public abstract string DumpString ();
public abstract void BuildStatements (OTDecompile decompile, LinkedListNode<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", "!=");
}
}
}