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