From 2129d941acbc5f83aca4dc4c30a62d3226888136 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 23 Feb 2018 14:52:34 +0000 Subject: rename XMREngine as Yengine (still not all done), big mess source formating changes, move state files to proper folder, fix a source file locking on errors, more changes for cross platform including from Mike,... yes yes i know a messy commit --- .../Region/ScriptEngine/YEngine/XMRInstAbstract.cs | 2348 ++++++++++++++++++++ 1 file changed, 2348 insertions(+) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/XMRInstAbstract.cs (limited to 'OpenSim/Region/ScriptEngine/YEngine/XMRInstAbstract.cs') diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstAbstract.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstAbstract.cs new file mode 100644 index 0000000..a0bb3e0 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstAbstract.cs @@ -0,0 +1,2348 @@ +/* + * 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.Globalization; +using System.IO; +using System.Reflection.Emit; +using System.Runtime.Serialization; +using System.Text; +using System.Threading; + +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; + +namespace OpenSim.Region.ScriptEngine.Yengine +{ + public class XMRInstArrays + { + public XMR_Array[] iarArrays; + public char[] iarChars; + public double[] iarFloats; + public int[] iarIntegers; + public LSL_List[] iarLists; + public object[] iarObjects; + public LSL_Rotation[] iarRotations; + public string[] iarStrings; + public LSL_Vector[] iarVectors; + public XMRSDTypeClObj[] iarSDTClObjs; + public Delegate[][] iarSDTIntfObjs; + + private XMRInstAbstract instance; + private int heapUse; + + private static readonly XMR_Array[] noArrays = new XMR_Array[0]; + private static readonly char[] noChars = new char[0]; + private static readonly double[] noFloats = new double[0]; + private static readonly int[] noIntegers = new int[0]; + private static readonly LSL_List[] noLists = new LSL_List[0]; + private static readonly object[] noObjects = new object[0]; + private static readonly LSL_Rotation[] noRotations = new LSL_Rotation[0]; + private static readonly string[] noStrings = new string[0]; + private static readonly LSL_Vector[] noVectors = new LSL_Vector[0]; + private static readonly XMRSDTypeClObj[] noSDTClObjs = new XMRSDTypeClObj[0]; + private static readonly Delegate[][] noSDTIntfObjs = new Delegate[0][]; + + public XMRInstArrays(XMRInstAbstract inst) + { + instance = inst; + } + + ~XMRInstArrays() + { + heapUse = instance.UpdateHeapUse(heapUse, 0); + } + + public void AllocVarArrays(XMRInstArSizes ars) + { + ClearOldArrays(); + + heapUse = instance.UpdateHeapUse(heapUse, + ars.iasChars * HeapTrackerObject.HT_CHAR + + ars.iasFloats * HeapTrackerObject.HT_SFLT + + ars.iasIntegers * HeapTrackerObject.HT_INT + + ars.iasRotations * HeapTrackerObject.HT_ROT + + ars.iasVectors * HeapTrackerObject.HT_VEC + + ars.iasSDTIntfObjs * HeapTrackerObject.HT_DELE); + + iarArrays = (ars.iasArrays > 0) ? new XMR_Array[ars.iasArrays] : noArrays; + iarChars = (ars.iasChars > 0) ? new char[ars.iasChars] : noChars; + iarFloats = (ars.iasFloats > 0) ? new double[ars.iasFloats] : noFloats; + iarIntegers = (ars.iasIntegers > 0) ? new int[ars.iasIntegers] : noIntegers; + iarLists = (ars.iasLists > 0) ? new LSL_List[ars.iasLists] : noLists; + iarObjects = (ars.iasObjects > 0) ? new object[ars.iasObjects] : noObjects; + iarRotations = (ars.iasRotations > 0) ? new LSL_Rotation[ars.iasRotations] : noRotations; + iarStrings = (ars.iasStrings > 0) ? new string[ars.iasStrings] : noStrings; + iarVectors = (ars.iasVectors > 0) ? new LSL_Vector[ars.iasVectors] : noVectors; + iarSDTClObjs = (ars.iasSDTClObjs > 0) ? new XMRSDTypeClObj[ars.iasSDTClObjs] : noSDTClObjs; + iarSDTIntfObjs = (ars.iasSDTIntfObjs > 0) ? new Delegate[ars.iasSDTIntfObjs][] : noSDTIntfObjs; + } + + /** + * @brief Do not write directly to iarLists[index], rather use this method. + */ + public void PopList(int index, LSL_List lis) + { + LSL_List old = iarLists[index]; + int newheapuse = heapUse + HeapTrackerList.Size(lis) - HeapTrackerList.Size(old); + heapUse = instance.UpdateHeapUse(heapUse, newheapuse); + iarLists[index] = lis; + } + + /** + * @brief Do not write directly to iarObjects[index], rather use this method. + */ + public void PopObject(int index, object obj) + { + object old = iarObjects[index]; + int newheapuse = heapUse + HeapTrackerObject.Size(obj) - HeapTrackerObject.Size(old); + heapUse = instance.UpdateHeapUse(heapUse, newheapuse); + iarObjects[index] = obj; + } + + /** + * @brief Do not write directly to iarStrings[index], rather use this method. + */ + public void PopString(int index, string str) + { + string old = iarStrings[index]; + int newheapuse = heapUse + HeapTrackerString.Size(str) - HeapTrackerString.Size(old); + heapUse = instance.UpdateHeapUse(heapUse, newheapuse); + iarStrings[index] = str; + } + + /** + * @brief Write all arrays out to a file. + */ + public delegate void Sender(object value); + public void SendArrays(Sender sender) + { + sender(iarArrays); + sender(iarChars); + sender(iarFloats); + sender(iarIntegers); + sender(iarLists); + sender(iarObjects); + sender(iarRotations); + sender(iarStrings); + sender(iarVectors); + sender(iarSDTClObjs); + sender(iarSDTIntfObjs); + } + + /** + * @brief Read all arrays in from a file. + */ + public delegate object Recver(); + public void RecvArrays(Recver recver) + { + ClearOldArrays(); + + iarArrays = (XMR_Array[])recver(); + char[] chrs = (char[])recver(); + double[] flts = (double[])recver(); + int[] ints = (int[])recver(); + LSL_List[] liss = (LSL_List[])recver(); + object[] objs = (object[])recver(); + LSL_Rotation[] rots = (LSL_Rotation[])recver(); + string[] strs = (string[])recver(); + LSL_Vector[] vecs = (LSL_Vector[])recver(); + iarSDTClObjs = (XMRSDTypeClObj[])recver(); + Delegate[][] dels = (Delegate[][])recver(); + + int newheapuse = heapUse; + + // value types simply are the size of the value * number of values + newheapuse += chrs.Length * HeapTrackerObject.HT_CHAR; + newheapuse += flts.Length * HeapTrackerObject.HT_SFLT; + newheapuse += ints.Length * HeapTrackerObject.HT_INT; + newheapuse += rots.Length * HeapTrackerObject.HT_ROT; + newheapuse += vecs.Length * HeapTrackerObject.HT_VEC; + newheapuse += dels.Length * HeapTrackerObject.HT_DELE; + + // lists, objects, strings are the sum of the size of each element + foreach(LSL_List lis in liss) + newheapuse += HeapTrackerList.Size(lis); + + foreach(object obj in objs) + newheapuse += HeapTrackerObject.Size(obj); + + foreach(string str in strs) + newheapuse += HeapTrackerString.Size(str); + + // others (XMR_Array, XMRSDTypeClObj) keep track of their own heap usage + + // update script heap usage, throwing an exception before finalizing changes + heapUse = instance.UpdateHeapUse(heapUse, newheapuse); + + iarChars = chrs; + iarFloats = flts; + iarIntegers = ints; + iarLists = liss; + iarObjects = objs; + iarRotations = rots; + iarStrings = strs; + iarVectors = vecs; + iarSDTIntfObjs = dels; + } + + private void ClearOldArrays() + { + int newheapuse = heapUse; + + iarArrays = null; + if(iarChars != null) + { + newheapuse -= iarChars.Length * HeapTrackerObject.HT_CHAR; + iarChars = null; + } + if(iarFloats != null) + { + newheapuse -= iarFloats.Length * HeapTrackerObject.HT_SFLT; + iarFloats = null; + } + if(iarIntegers != null) + { + newheapuse -= iarIntegers.Length * HeapTrackerObject.HT_INT; + iarIntegers = null; + } + if(iarLists != null) + { + foreach(LSL_List lis in iarLists) + newheapuse -= HeapTrackerList.Size(lis); + iarLists = null; + } + if(iarObjects != null) + { + foreach(object obj in iarObjects) + newheapuse -= HeapTrackerObject.Size(obj); + iarObjects = null; + } + if(iarRotations != null) + { + newheapuse -= iarRotations.Length * HeapTrackerObject.HT_ROT; + iarRotations = null; + } + if(iarStrings != null) + { + foreach(string str in iarStrings) + newheapuse -= HeapTrackerString.Size(str); + iarStrings = null; + } + if(iarVectors != null) + { + newheapuse -= iarVectors.Length * HeapTrackerObject.HT_VEC; + iarVectors = null; + } + iarSDTClObjs = null; + if(iarSDTIntfObjs != null) + { + newheapuse -= iarSDTIntfObjs.Length * HeapTrackerObject.HT_DELE; + iarSDTIntfObjs = null; + } + + heapUse = instance.UpdateHeapUse(heapUse, newheapuse); + } + } + + public class XMRInstArSizes + { + public int iasArrays; + public int iasChars; + public int iasFloats; + public int iasIntegers; + public int iasLists; + public int iasObjects; + public int iasRotations; + public int iasStrings; + public int iasVectors; + public int iasSDTClObjs; + public int iasSDTIntfObjs; + + public void WriteAsmFile(TextWriter asmFileWriter, string label) + { + asmFileWriter.WriteLine(" {0}Arrays {1}", label, iasArrays); + asmFileWriter.WriteLine(" {0}Chars {1}", label, iasChars); + asmFileWriter.WriteLine(" {0}Floats {1}", label, iasFloats); + asmFileWriter.WriteLine(" {0}Integers {1}", label, iasIntegers); + asmFileWriter.WriteLine(" {0}Lists {1}", label, iasLists); + asmFileWriter.WriteLine(" {0}Objects {1}", label, iasObjects); + asmFileWriter.WriteLine(" {0}Rotations {1}", label, iasRotations); + asmFileWriter.WriteLine(" {0}Strings {1}", label, iasStrings); + asmFileWriter.WriteLine(" {0}Vectors {1}", label, iasVectors); + asmFileWriter.WriteLine(" {0}SDTClObjs {1}", label, iasSDTClObjs); + asmFileWriter.WriteLine(" {0}SDTIntfObjs {1}", label, iasSDTIntfObjs); + } + + public void WriteToFile(BinaryWriter objFileWriter) + { + objFileWriter.Write(iasArrays); + objFileWriter.Write(iasChars); + objFileWriter.Write(iasFloats); + objFileWriter.Write(iasIntegers); + objFileWriter.Write(iasLists); + objFileWriter.Write(iasObjects); + objFileWriter.Write(iasRotations); + objFileWriter.Write(iasStrings); + objFileWriter.Write(iasVectors); + objFileWriter.Write(iasSDTClObjs); + objFileWriter.Write(iasSDTIntfObjs); + } + + public void ReadFromFile(BinaryReader objFileReader) + { + iasArrays = objFileReader.ReadInt32(); + iasChars = objFileReader.ReadInt32(); + iasFloats = objFileReader.ReadInt32(); + iasIntegers = objFileReader.ReadInt32(); + iasLists = objFileReader.ReadInt32(); + iasObjects = objFileReader.ReadInt32(); + iasRotations = objFileReader.ReadInt32(); + iasStrings = objFileReader.ReadInt32(); + iasVectors = objFileReader.ReadInt32(); + iasSDTClObjs = objFileReader.ReadInt32(); + iasSDTIntfObjs = objFileReader.ReadInt32(); + } + } + + public class XMRStackFrame + { + public XMRStackFrame nextSF; + public string funcName; + public int callNo; + public object[] objArray; + } + + /* + * Contains only items required by the stand-alone compiler + * so the compiler doesn't need to pull in all of OpenSim. + * + * Inherit from ScriptBaseClass so we can be used as 'this' + * parameter for backend-API calls, eg llSay(). + */ + public abstract class XMRInstAbstract: ScriptBaseClass + { + public const int CallMode_NORMAL = 0; // when function is called, it proceeds normally + public const int CallMode_SAVE = 1; // StackSaveException() was thrown, push args/locals to stackFrames + public const int CallMode_RESTORE = 2; // when function is called, it pops state from stackFrames + + public bool suspendOnCheckRunHold; // suspend script execution until explicitly set false + public bool suspendOnCheckRunTemp; // suspend script execution for single step only + public int stackLimit; // stack must have at least this many bytes free on entry to functions + public int m_StackLeft; // total number of stack bytes yet to be used (init to stacksize) + + public ScriptObjCode m_ObjCode; // script object code this instance was created from + + public object[] ehArgs; // event handler argument array + public bool doGblInit = true; // default state_entry() needs to initialize global variables + public int stateCode = 0; // state the script is in (0 = 'default') + public int newStateCode = -1; // if >= 0, in the middle of exiting 'stateCode' and entering 'newStateCode' + public ScriptEventCode eventCode = ScriptEventCode.None; + // what event handler is executing (or None if not) + + public int callMode = CallMode_NORMAL; + // to capture stack frames on stackFrames: + // set to CallMode_SAVE just before throwing StackSaveException() + // from within CheckRun() and cleared to CallMode_NORMAL when + // the exception is caught + // to restore stack frames from stackFrames: + // set to CallMode_RESTORE just before calling CallSEH() and + // cleared to CallMode_NORMAL by CheckRun() + public XMRStackFrame stackFrames; // stack frames being saved/restored + + private static readonly char[] justacomma = { ',' }; + + /* + * These arrays hold the global variable values for the script instance. + * The array lengths are determined by the script compilation, + * and are found in ScriptObjCode.glblSizes. + */ + public XMRInstArrays glblVars; + + public XMRInstAbstract() + { + glblVars = new XMRInstArrays(this); + } + + /****************************************************************\ + * Abstract function prototypes. * + * These functions require access to the OpenSim environment. * + \****************************************************************/ + + public abstract void CheckRunWork(); + public abstract void StateChange(); + + [xmrMethodCallsCheckRunAttribute] // calls CheckRun() + [xmrMethodIsNoisyAttribute] // calls Stub() + public abstract LSL_List xmrEventDequeue(double timeout, int returnMask1, int returnMask2, + int backgroundMask1, int backgroundMask2); + + [xmrMethodIsNoisyAttribute] // calls Stub() + public abstract void xmrEventEnqueue(LSL_List ev); + + [xmrMethodIsNoisyAttribute] // calls Stub() + public abstract LSL_List xmrEventSaveDets(); + + [xmrMethodIsNoisyAttribute] // calls Stub() + public abstract void xmrEventLoadDets(LSL_List dpList); + + + /**************************************************\ + * Functions what don't require runtime support * + * beyond what the compiler provides. * + \**************************************************/ + + protected int heapLimit; + private int heapUsed; + + public virtual int UpdateHeapUse(int olduse, int newuse) + { + if(newuse <= olduse) + Interlocked.Add(ref heapUsed, newuse - olduse); + else + { + int newtotal, oldtotal; + do + { + oldtotal = Interlocked.Add(ref heapUsed, 0); + newtotal = oldtotal + newuse - olduse; + if(newtotal > heapLimit) + { + // System.GC.Collect (); + // System.GC.WaitForPendingFinalizers (); + oldtotal = Interlocked.Add(ref heapUsed, 0); + newtotal = oldtotal + newuse - olduse; + if(newtotal > heapLimit) + throw new OutOfHeapException(oldtotal, newtotal, heapLimit); + } + } while(Interlocked.CompareExchange(ref heapUsed, newtotal, oldtotal) != oldtotal); + } + + return newuse; + } + + public int xmrHeapLeft() + { + return heapLimit - heapUsed; + } + + public int xmrHeapUsed() + { + return heapUsed; + } + + /** + * @brief Call script's event handler function from the very beginning. + * @param instance.stateCode = which state the event is happening in + * @param instance.eventCode = which event is happening in that state + * @returns when event handler has completed or throws an exception + * with instance.eventCode = ScriptEventCode.None + */ + public void CallSEH() + { + ScriptEventHandler seh; + + /* + * CallMode_NORMAL: run event handler from the beginning normally + * CallMode_RESTORE: restore event handler stack from stackFrames + */ + callMode = (stackFrames == null) ? XMRInstAbstract.CallMode_NORMAL : + XMRInstAbstract.CallMode_RESTORE; + + while(true) + { + if(this.newStateCode < 0) + { + // Process event given by 'stateCode' and 'eventCode'. + // The event handler should call CheckRun() as often as convenient. + int newState = this.stateCode; + seh = this.m_ObjCode.scriptEventHandlerTable[newState, (int)this.eventCode]; + if(seh != null) + { + try + { + seh(this); + } + catch(ScriptChangeStateException scse) + { + newState = scse.newState; + } + } + this.ehArgs = null; // we are done with them and no args for + // exit_state()/enter_state() anyway + + // The usual case is no state change. + // Even a 'state ;' statement has no effect except to exit out. + // It does not execute the state_exit() or state_entry() handlers. + // See http://wiki.secondlife.com/wiki/State + if(newState == this.stateCode) + break; + + // Save new state in a more permanent location in case we + // get serialized out while in the state_exit() handler. + this.newStateCode = newState; + } + + // Call old state's state_exit() handler. + this.eventCode = ScriptEventCode.state_exit; + seh = this.m_ObjCode.scriptEventHandlerTable[this.stateCode, (int)ScriptEventCode.state_exit]; + if(seh != null) + { + try + { + seh(this); + } + catch(ScriptChangeStateException scse) + { + this.newStateCode = scse.newState; + } + } + + // Switch over to the new state's state_entry() handler. + this.stateCode = this.newStateCode; + this.eventCode = ScriptEventCode.state_entry; + this.newStateCode = -1; + + // Now that the old state can't possibly start any more activity, + // cancel any listening handlers, etc, of the old state. + this.StateChange(); + + // Loop back to execute new state's state_entry() handler. + } + + // Event no longer being processed. + this.eventCode = ScriptEventCode.None; + } + + /** + * @brief For compatibility with old code. + */ + public void CheckRun(int line) + { + CheckRunStack(); + } + + /** + * @brief Called at beginning of complex functions to see if they + * are nested too deep possibly in a recursive loop. + */ + public void CheckRunStack() + { + if(m_StackLeft < stackLimit) + throw new OutOfStackException(); + + CheckRunQuick(); + } + + /** + * @brief Called in each iteration of a loop to see if running too long. + */ + public void CheckRunQuick() + { + // if (suspendOnCheckRunHold || suspendOnCheckRunTemp) + CheckRunWork(); + } + + /** + * @brief Called during CallMode_SAVE to create a stackframe save object that saves + * local variables and calling point within the function. + * @param funcName = name of function whose frame is being saved + * @param callNo = call number (ie, return address) within function to restart at + * @param nSaves = number of variables the function will save + * @returns an object[nSaves] where function can save variables + */ + public object[] CaptureStackFrame(string funcName, int callNo, int nSaves) + { + XMRStackFrame sf = new XMRStackFrame(); + sf.nextSF = stackFrames; + sf.funcName = funcName; + sf.callNo = callNo; + sf.objArray = new object[nSaves]; + stackFrames = sf; + return sf.objArray; + } + + /** + * @brief Called during CallMode_RESTORE to pop a stackframe object to restore + * local variables and calling point within the function. + * @param funcName = name of function whose frame is being restored + * @returns the object[nSaves] where function can retrieve variables + * callNo = as passed to CaptureStackFrame() indicating restart point + */ + public object[] RestoreStackFrame(string funcName, out int callNo) + { + XMRStackFrame sf = stackFrames; + if(sf.funcName != funcName) + throw new Exception("frame mismatch " + sf.funcName + " vs " + funcName); + + callNo = sf.callNo; + stackFrames = sf.nextSF; + return sf.objArray; + } + + /** + * @brief Convert all LSL_Integers in a list to System.Int32s, + * as required by llParcelMediaQuery(). + */ + public static LSL_List FixLLParcelMediaQuery(LSL_List oldlist) + { + object[] oldarray = oldlist.Data; + int len = oldarray.Length; + object[] newarray = new object[len]; + for(int i = 0; i < len; i++) + { + object obj = oldarray[i]; + if(obj is LSL_Integer) + obj = (int)(LSL_Integer)obj; + newarray[i] = obj; + } + return new LSL_List(newarray); + } + + /** + * @brief Convert *SOME* LSL_Integers in a list to System.Int32s, + * as required by llParcelMediaCommandList(). + */ + public static LSL_List FixLLParcelMediaCommandList(LSL_List oldlist) + { + object[] oldarray = oldlist.Data; + int len = oldarray.Length; + object[] newarray = new object[len]; + int verbatim = 0; + for(int i = 0; i < len; i++) + { + object obj = oldarray[i]; + if(--verbatim < 0) + { + if(obj is LSL_Integer) + obj = (int)(LSL_Integer)obj; + if(obj is int) + { + switch((int)obj) + { + case ScriptBaseClass.PARCEL_MEDIA_COMMAND_AUTO_ALIGN: + // leave next integer as LSL_Integer + verbatim = 1; + break; + + case ScriptBaseClass.PARCEL_MEDIA_COMMAND_SIZE: + // leave next two integers as LSL_Integer + verbatim = 2; + break; + + } + } + } + newarray[i] = obj; + } + return new LSL_List(newarray); + } + + public static int xmrHashCode(int i) + { + return i.GetHashCode(); + } + + public static int xmrHashCode(double f) + { + return f.GetHashCode(); + } + + public static int xmrHashCode(object o) + { + return o.GetHashCode(); + } + + public static int xmrHashCode(string s) + { + return s.GetHashCode(); + } + + public string xmrTypeName(object o) + { + /* + * Basic types return constant strings of the script-visible type name. + */ + if(o is XMR_Array) + return "array"; + if(o is bool) + return "bool"; + if(o is char) + return "char"; + if(o is Exception) + return "exception"; + if(o is double) + return "float"; + if(o is float) + return "float"; + if(o is LSL_Float) + return "float"; + if(o is int) + return "integer"; + if(o is LSL_Integer) + return "integer"; + if(o is LSL_List) + return "list"; + if(o is LSL_Rotation) + return "rotation"; + if(o is LSL_String) + return "string"; + if(o is string) + return "string"; + if(o is LSL_Vector) + return "vector"; + + /* + * A script-defined interface is represented as an array of delegates. + * If that is the case, convert it to the object of the script-defined + * class that is implementing the interface. This should let the next + * step get the script-defined type name of the object. + */ + if(o is Delegate[]) + o = ((Delegate[])o)[0].Target; + + /* + * If script-defined class instance, get the script-defined + * type name. + */ + if(o is XMRSDTypeClObj) + return ((XMRSDTypeClObj)o).sdtcClass.longName.val; + + /* + * If it's a delegate, maybe we can look up its script-defined type name. + */ + Type ot = o.GetType(); + if(o is Delegate) + { + String os; + if(m_ObjCode.sdDelTypes.TryGetValue(ot, out os)) + return os; + } + + /* + * Don't know what it is, get the C#-level type name. + */ + return ot.ToString(); + } + + /** + * @brief Call the current state's event handler. + * @param ev = as returned by xmrEventDequeue saying which event handler to call + * and what argument list to pass to it. The llDetect...() parameters + * are as currently set for the script (use xmrEventLoadDets to set how + * you want them to be different). + */ + public void xmrEventCallHandler(LSL_List ev) + { + object[] data = ev.Data; + int evc = (int)(ev.GetLSLIntegerItem(0).value & 0xFFFFFFFF); + ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode, evc]; + if(seh != null) + { + int nargs = data.Length - 1; + object[] args = new object[nargs]; + Array.Copy(data, 1, args, 0, nargs); + + object[] saveEHArgs = this.ehArgs; + ScriptEventCode saveEventCode = this.eventCode; + + this.ehArgs = args; + this.eventCode = (ScriptEventCode)evc; + + seh(this); + + this.ehArgs = saveEHArgs; + this.eventCode = saveEventCode; + } + } + + /** + * @brief Sane substring functions. + */ + public string xmrSubstring(string s, int offset) + { + if(offset >= s.Length) + return ""; + return s.Substring(offset); + } + + // C# style + public string xmrSubstring(string s, int offset, int length) + { + if(length <= 0) + return ""; + if(offset >= s.Length) + return ""; + if(length > s.Length - offset) + length = s.Length - offset; + return s.Substring(offset, length); + } + + // java style + public string xmrJSubstring(string s, int beg, int end) + { + if(end <= beg) + return ""; + if(beg >= s.Length) + return ""; + if(end > s.Length) + end = s.Length; + return s.Substring(beg, end - beg); + } + + /** + * @brief String begins and ends with test. + */ + public bool xmrStringStartsWith(string s, string t) + { + return s.StartsWith(t); + } + + public bool xmrStringEndsWith(string s, string t) + { + return s.EndsWith(t); + } + + /** + * @brief [Last]IndexOf with starting position (just like C#) + */ + public int xmrStringIndexOf(string haystack, string needle) + { + return haystack.IndexOf(needle); + } + + public int xmrStringIndexOf(string haystack, string needle, int startat) + { + return haystack.IndexOf(needle, startat); + } + + public int xmrStringLastIndexOf(string haystack, string needle) + { + return haystack.LastIndexOf(needle); + } + + public int xmrStringLastIndexOf(string haystack, string needle, int startat) + { + return haystack.LastIndexOf(needle, startat); + } + + /** + * @brief These conversions throw exceptions if there is anything stinky... + */ + public double xmrString2Float(string s) + { + return double.Parse(s, CultureInfo.InvariantCulture); + } + + public int xmrString2Integer(string s) + { + s = s.Trim(); + if(s.StartsWith("0x") || s.StartsWith("0X")) + return int.Parse(s.Substring(2), NumberStyles.HexNumber); + + return int.Parse(s, CultureInfo.InvariantCulture); + } + + public LSL_Rotation xmrString2Rotation(string s) + { + s = s.Trim(); + if(!s.StartsWith("<") || !s.EndsWith(">")) + throw new FormatException("doesn't begin with < and end with >"); + + s = s.Substring(1, s.Length - 2); + string[] splitup = s.Split(justacomma, 5); + if(splitup.Length != 4) + throw new FormatException("doesn't have exactly 3 commas"); + + double x = double.Parse(splitup[0], CultureInfo.InvariantCulture); + double y = double.Parse(splitup[1], CultureInfo.InvariantCulture); + double z = double.Parse(splitup[2], CultureInfo.InvariantCulture); + double w = double.Parse(splitup[3], CultureInfo.InvariantCulture); + return new LSL_Rotation(x, y, z, w); + } + + public LSL_Vector xmrString2Vector(string s) + { + s = s.Trim(); + if(!s.StartsWith("<") || !s.EndsWith(">")) + throw new FormatException("doesn't begin with < and end with >"); + + s = s.Substring(1, s.Length - 2); + string[] splitup = s.Split(justacomma, 4); + if(splitup.Length != 3) + throw new FormatException("doesn't have exactly 2 commas"); + + double x = double.Parse(splitup[0], CultureInfo.InvariantCulture); + double y = double.Parse(splitup[1], CultureInfo.InvariantCulture); + double z = double.Parse(splitup[2], CultureInfo.InvariantCulture); + return new LSL_Vector(x, y, z); + } + + /** + * @brief Access C#-style formatted numeric conversions. + */ + public string xmrFloat2String(double val, string fmt) + { + return val.ToString(fmt, CultureInfo.InvariantCulture); + } + + public string xmrInteger2String(int val, string fmt) + { + return val.ToString(fmt, CultureInfo.InvariantCulture); + } + + public string xmrRotation2String(LSL_Rotation val, string fmt) + { + return "<" + val.x.ToString(fmt, CultureInfo.InvariantCulture) + "," + + val.y.ToString(fmt, CultureInfo.InvariantCulture) + "," + + val.z.ToString(fmt, CultureInfo.InvariantCulture) + "," + + val.s.ToString(fmt, CultureInfo.InvariantCulture) + ">"; + } + + public string xmrVector2String(LSL_Vector val, string fmt) + { + return "<" + val.x.ToString(fmt, CultureInfo.InvariantCulture) + "," + + val.y.ToString(fmt, CultureInfo.InvariantCulture) + "," + + val.z.ToString(fmt, CultureInfo.InvariantCulture) + ">"; + } + + /** + * @brief Get a delegate for a script-defined function. + * @param name = name of the function including arg types, eg, + * "Verify(array,list,string)" + * @param sig = script-defined type name + * @param targ = function's 'this' pointer or null if static + * @returns delegate for the script-defined function + */ + public Delegate GetScriptMethodDelegate(string name, string sig, object targ) + { + DynamicMethod dm = m_ObjCode.dynamicMethods[name]; + TokenDeclSDTypeDelegate dt = (TokenDeclSDTypeDelegate)m_ObjCode.sdObjTypesName[sig]; + return dm.CreateDelegate(dt.GetSysType(), targ); + } + + /** + * @brief Try to cast the thrown object to the given script-defined type. + * @param thrown = what object was thrown + * @param inst = what script instance we are running in + * @param sdtypeindex = script-defined type to try to cast it to + * @returns null: thrown is not castable to sdtypename + * else: an object casted to sdtypename + */ + public static object XMRSDTypeCatchTryCastToSDType(object thrown, XMRInstAbstract inst, int sdtypeindex) + { + TokenDeclSDType sdType = inst.m_ObjCode.sdObjTypesIndx[sdtypeindex]; + + /* + * If it is a script-defined interface object, convert to the original XMRSDTypeClObj. + */ + if(thrown is Delegate[]) + { + thrown = ((Delegate[])thrown)[0].Target; + } + + /* + * If it is a script-defined delegate object, make sure it is an instance of the expected type. + */ + if(thrown is Delegate) + { + Type ot = thrown.GetType(); + Type tt = sdType.GetSysType(); + return (ot == tt) ? thrown : null; + } + + /* + * If it is a script-defined class object, make sure it is an instance of the expected class. + */ + if(thrown is XMRSDTypeClObj) + { + + /* + * Step from the object's actual class rootward. + * If we find the requested class along the way, the cast is valid. + * If we run off the end of the root, the cast is not valid. + */ + for(TokenDeclSDTypeClass ac = ((XMRSDTypeClObj)thrown).sdtcClass; ac != null; ac = ac.extends) + { + if(ac == sdType) + return thrown; + } + } + + /* + * Don't know what it is, assume it is not what caller wants. + */ + return null; + } + + /** + * @brief Allocate and access fixed-dimension arrays. + */ + public static object xmrFixedArrayAllocC(int len) + { + return new char[len]; + } + public static object xmrFixedArrayAllocF(int len) + { + return new double[len]; + } + public static object xmrFixedArrayAllocI(int len) + { + return new int[len]; + } + public static object xmrFixedArrayAllocO(int len) + { + return new object[len]; + } + + public static char xmrFixedArrayGetC(object arr, int idx) + { + return ((char[])arr)[idx]; + } + public static double xmrFixedArrayGetF(object arr, int idx) + { + return ((double[])arr)[idx]; + } + public static int xmrFixedArrayGetI(object arr, int idx) + { + return ((int[])arr)[idx]; + } + public static object xmrFixedArrayGetO(object arr, int idx) + { + return ((object[])arr)[idx]; + } + + public static void xmrFixedArraySetC(object arr, int idx, char val) + { + ((char[])arr)[idx] = val; + } + public static void xmrFixedArraySetF(object arr, int idx, double val) + { + ((double[])arr)[idx] = val; + } + public static void xmrFixedArraySetI(object arr, int idx, int val) + { + ((int[])arr)[idx] = val; + } + public static void xmrFixedArraySetO(object arr, int idx, object val) + { + ((object[])arr)[idx] = val; + } + + /** + * @brief Copy from one script-defined array to another. + * @param srcobj = source script-defined array class object pointer + * @param srcstart = offset in source array to start copying from + * @param dstobj = destination script-defined array class object pointer + * @param dststart = offset in destination arry to start copying to + * @param count = number of elements to copy + */ + public static void xmrArrayCopy(object srcobj, int srcstart, object dstobj, int dststart, int count) + { + /* + * The script writer should only pass us script-defined class objects. + * Throw exception otherwise. + */ + XMRSDTypeClObj srcsdt = (XMRSDTypeClObj)srcobj; + XMRSDTypeClObj dstsdt = (XMRSDTypeClObj)dstobj; + + /* + * Get the script-visible type name of the arrays, brackets and all. + */ + string srctypename = srcsdt.sdtcClass.longName.val; + string dsttypename = dstsdt.sdtcClass.longName.val; + + /* + * The part before the first '[' of each should match exactly, + * meaning the basic data type (eg, float, List) is the same. + * And there must be a '[' in each meaning that it is a script-defined array type. + */ + int i = srctypename.IndexOf('['); + int j = dsttypename.IndexOf('['); + if((i < 0) || (j < 0)) + throw new InvalidCastException("non-array passed: " + srctypename + " and/or " + dsttypename); + if((i != j) || !srctypename.StartsWith(dsttypename.Substring(0, j))) + throw new ArrayTypeMismatchException(srctypename + " vs " + dsttypename); + + /* + * The number of brackets must match exactly. + * This permits copying from something like a float[,][] to something like a float[][]. + * But you cannot copy from a float[][] to a float[] or wisa wersa. + * Counting either '[' or ']' would work equally well. + */ + int srclen = srctypename.Length; + int dstlen = dsttypename.Length; + int srcjags = 0; + int dstjags = 0; + while(++i < srclen) + if(srctypename[i] == ']') + srcjags++; + while(++j < dstlen) + if(dsttypename[j] == ']') + dstjags++; + if(dstjags != srcjags) + throw new ArrayTypeMismatchException(srctypename + " vs " + dsttypename); + + /* + * Perform the copy. + */ + Array srcarray = (Array)srcsdt.instVars.iarObjects[0]; + Array dstarray = (Array)dstsdt.instVars.iarObjects[0]; + Array.Copy(srcarray, srcstart, dstarray, dststart, count); + } + + /** + * @brief Copy from an array to a list. + * @param srcar = the array to copy from + * @param start = where to start in the array + * @param count = number of elements + * @returns the list + */ + public static LSL_List xmrArray2List(object srcar, int start, int count) + { + /* + * Get the script-visible type of the array. + * We only do arrays. + */ + XMRSDTypeClObj array = (XMRSDTypeClObj)srcar; + TokenDeclSDTypeClass sdtClass = array.sdtcClass; + if(sdtClass.arrayOfRank == 0) + throw new InvalidCastException("only do arrays not " + sdtClass.longName.val); + + /* + * Validate objects they want to put in the list. + * We can't allow anything funky that OpenSim runtime doesn't expect. + */ + Array srcarray = (Array)array.instVars.iarObjects[0]; + object[] output = new object[count]; + for(int i = 0; i < count; i++) + { + object src = srcarray.GetValue(i + start); + if(src == null) + throw new NullReferenceException("null element " + i); + if(src is double) + { + output[i] = new LSL_Float((double)src); + continue; + } + if(src is int) + { + output[i] = new LSL_Integer((int)src); + continue; + } + if(src is LSL_Rotation) + { + output[i] = src; + continue; + } + if(src is LSL_Vector) + { + output[i] = src; + continue; + } + if(src is string) + { + output[i] = new LSL_String((string)src); + continue; + } + throw new InvalidCastException("invalid element " + i + " type " + src.GetType().Name); + } + + /* + * Make a list out of that now immutable array. + */ + return new LSL_List(output); + } + + /** + * @brief Copy from a list to an array. + * @param srclist = list to copy from + * @param srcstart = where to start in the list + * @param dstobj = array to copy to + * @param dststart = where to start in the array + * @param count = number of elements + */ + public static void xmrList2Array(LSL_List srclist, int srcstart, object dstobj, int dststart, int count) + { + /* + * Get the script-visible type of the destination. + * We only do arrays. + */ + XMRSDTypeClObj dstarray = (XMRSDTypeClObj)dstobj; + TokenDeclSDTypeClass sdtClass = dstarray.sdtcClass; + if(sdtClass.arrayOfType == null) + throw new InvalidCastException("only do arrays not " + sdtClass.longName.val); + + /* + * Copy from the immutable array to the mutable array. + * Strip off any LSL wrappers as the script code doesn't expect any. + */ + object[] srcarr = srclist.Data; + Array dstarr = (Array)dstarray.instVars.iarObjects[0]; + + for(int i = 0; i < count; i++) + { + object obj = srcarr[i + srcstart]; + if(obj is LSL_Float) + obj = ((LSL_Float)obj).value; + else if(obj is LSL_Integer) + obj = ((LSL_Integer)obj).value; + else if(obj is LSL_String) + obj = ((LSL_String)obj).m_string; + dstarr.SetValue(obj, i + dststart); + } + } + + /** + * @brief Copy from an array of characters to a string. + * @param srcar = the array to copy from + * @param start = where to start in the array + * @param count = number of elements + * @returns the string + */ + public static string xmrChars2String(object srcar, int start, int count) + { + /* + * Make sure they gave us a script-defined array object. + */ + XMRSDTypeClObj array = (XMRSDTypeClObj)srcar; + TokenDeclSDTypeClass sdtClass = array.sdtcClass; + if(sdtClass.arrayOfRank == 0) + throw new InvalidCastException("only do arrays not " + sdtClass.longName.val); + + /* + * We get a type cast error from mono if they didn't give us a character array. + * But if it is ok, create a string from the requested characters. + */ + char[] srcarray = (char[])array.instVars.iarObjects[0]; + return new string(srcarray, start, count); + } + + /** + * @brief Copy from a string to a character array. + * @param srcstr = string to copy from + * @param srcstart = where to start in the string + * @param dstobj = array to copy to + * @param dststart = where to start in the array + * @param count = number of elements + */ + public static void xmrString2Chars(string srcstr, int srcstart, object dstobj, int dststart, int count) + { + /* + * Make sure they gave us a script-defined array object. + */ + XMRSDTypeClObj dstarray = (XMRSDTypeClObj)dstobj; + TokenDeclSDTypeClass sdtClass = dstarray.sdtcClass; + if(sdtClass.arrayOfType == null) + throw new InvalidCastException("only do arrays not " + sdtClass.longName.val); + + /* + * We get a type cast error from mono if they didn't give us a character array. + * But if it is ok, copy from the string to the character array. + */ + char[] dstarr = (char[])dstarray.instVars.iarObjects[0]; + for(int i = 0; i < count; i++) + dstarr[i + dststart] = srcstr[i + srcstart]; + } + + /** + * @brief Implement osParseJSON() so we return an array to the script. + * No coherent example of its use in scripts on web found. + * see http://www.json.org/ for more details on JSON + */ + private static LSL_List nullList = new LSL_List(new object[0]); + public new XMR_Array osParseJSON(string json) + { + XMR_Array dict = new XMR_Array(this); + int idx = ParseJSON(dict, nullList, json, 0); + while(idx < json.Length) + { + if(json[idx] > ' ') + throw new Exception("left-over json " + json); + idx++; + } + return dict; + } + + private static int ParseJSON(XMR_Array dict, LSL_List keys, string json, int idx) + { + char c; + + while((c = json[idx++]) <= ' ') + { + } + switch(c) + { + + // '{' ':' [ ',' ':' ... ] '}' + case '{': + do + { + string key = ParseJSONString(json, ref idx); + while((c = json[idx++]) <= ' ') + { + } + if(c != ':') + throw new Exception("missing : after key"); + idx = ParseJSON(dict, ParseJSONKeyAdd(keys, key), json, idx); + while((c = json[idx++]) <= ' ') + { + } + } while(c == ','); + if(c != '}') + throw new Exception("missing , or } after value"); + break; + + + // '[' [ ',' ... ] ']' + case '[': + int index = 0; + do + { + object key = index++; + idx = ParseJSON(dict, ParseJSONKeyAdd(keys, key), json, idx); + while((c = json[idx++]) <= ' ') + { + } + } while(c == ','); + if(c != ']') + throw new Exception("missing , or ] after value"); + break; + + + // '"''"' + case '"': + { + --idx; + string val = ParseJSONString(json, ref idx); + dict.SetByKey(keys, val); + break; + } + // true false null + case 't': + if(json.Substring(idx, 3) != "rue") + throw new Exception("bad true in json"); + idx += 3; + dict.SetByKey(keys, 1); + break; + + case 'f': + if(json.Substring(idx, 4) != "alse") + throw new Exception("bad false in json"); + idx += 4; + dict.SetByKey(keys, 0); + break; + + case 'n': + if(json.Substring(idx, 3) != "ull") + throw new Exception("bad null in json"); + idx += 3; + dict.SetByKey(keys, null); + break; + + // otherwise assume it's a number + default: + { + --idx; + object val = ParseJSONNumber(json, ref idx); + dict.SetByKey(keys, val); + break; + } + } + return idx; + } + + // Given the key for a whole array, create a key for a given element of the array + private static LSL_List ParseJSONKeyAdd(LSL_List oldkeys, object key) + { + int oldkeyslen = oldkeys.Length; + object[] array = oldkeys.Data; + Array.Resize(ref array, oldkeyslen + 1); + array[oldkeyslen] = key; + return new LSL_List(array); + } + + // Parse out a JSON string + private static string ParseJSONString(string json, ref int idx) + { + char c; + + while((c = json[idx++]) <= ' ') + { + } + if(c != '"') + throw new Exception("bad start of json string"); + + StringBuilder sb = new StringBuilder(); + while((c = json[idx++]) != '"') + { + if(c == '\\') + { + c = json[idx++]; + switch(c) + { + case 'b': + c = '\b'; + break; + + case 'f': + c = '\f'; + break; + + case 'n': + c = '\n'; + break; + + case 'r': + c = '\r'; + break; + + case 't': + c = '\t'; + break; + + case 'u': + c = (char)Int32.Parse(json.Substring(idx, 4), + System.Globalization.NumberStyles.HexNumber); + idx += 4; + break; + + default: + break; + } + } + sb.Append(c); + } + return sb.ToString(); + } + + // Parse out a JSON number + private static object ParseJSONNumber(string json, ref int idx) + { + char c; + + while((c = json[idx++]) <= ' ') + { + } + + bool expneg = false; + bool isneg = false; + int decpt = -1; + int expon = 0; + int ival = 0; + double dval = 0; + + if(c == '-') + { + isneg = true; + c = json[idx++]; + } + if((c < '0') || (c > '9')) + throw new Exception("bad json number"); + + while((c >= '0') && (c <= '9')) + { + dval *= 10; + ival *= 10; + dval += c - '0'; + ival += c - '0'; + c = '\0'; + if(idx < json.Length) + c = json[idx++]; + } + if(c == '.') + { + decpt = 0; + c = '\0'; + if(idx < json.Length) + c = json[idx++]; + while((c >= '0') && (c <= '9')) + { + dval *= 10; + dval += c - '0'; + decpt++; + c = '\0'; + if(idx < json.Length) + c = json[idx++]; + } + } + if((c == 'e') || (c == 'E')) + { + if(decpt < 0) + decpt = 0; + c = json[idx++]; + if(c == '-') + expneg = true; + if((c == '-') || (c == '+')) + c = json[idx++]; + while((c >= '0') && (c <= '9')) + { + expon *= 10; + expon += c - '0'; + c = '\0'; + if(idx < json.Length) + c = json[idx++]; + } + if(expneg) + expon = -expon; + } + + if(c != 0) + --idx; + if(decpt < 0) + { + if(isneg) + ival = -ival; + return ival; + } + else + { + if(isneg) + dval = -dval; + dval *= Math.Pow(10, expon - decpt); + return dval; + } + } + + /** + * @brief Exception-related runtime calls. + */ + // Return exception message (no type information just the message) + public static string xmrExceptionMessage(Exception ex) + { + return ex.Message; + } + + // Return stack trace (no type or message, just stack trace lines: at ... \n) + public string xmrExceptionStackTrace(Exception ex) + { + return XMRExceptionStackString(ex); + } + + // Return value thrown by a throw statement + public static object xmrExceptionThrownValue(Exception ex) + { + return ((ScriptThrownException)ex).thrown; + } + + // Return exception's short type name, eg, NullReferenceException, ScriptThrownException, etc. + public static string xmrExceptionTypeName(Exception ex) + { + return ex.GetType().Name; + } + + // internal use only: converts any IL addresses in script-defined methods to source location equivalent + // Mono ex.StackTrace: + // at OpenSim.Region.ScriptEngine.YEngine.TypeCast.ObjectToInteger (System.Object x) [0x0005e] in /home/kunta/opensim-0.9/addon-modules/YEngine/Module/MMRScriptTypeCast.cs:750 + // at (wrapper dynamic-method) System.Object:default state_entry (OpenSim.Region.ScriptEngine.YEngine.XMRInstAbstract) [0x00196] + + // Microsoft ex.StackTrace: + // at OpenSim.Region.ScriptEngine.YEngine.TypeCast.ObjectToInteger(Object x) in C:\Users\mrieker\opensim-0.9-source\addon-modules\YEngine\Module\MMRScriptTypeCast.cs:line 750 + // at default state_entry (XMRInstAbstract ) + public string XMRExceptionStackString(Exception ex) + { + string stwhole = ex.StackTrace; + string[] stlines = stwhole.Split(new char[] { '\n' }); + StringBuilder sb = new StringBuilder(); + foreach(string st in stlines) + { + string stline = st.Trim(); + if(stline == "") + continue; + + // strip 'at' off the front of line + if(stline.StartsWith("at ")) + { + stline = stline.Substring(3); + } + + // strip '(wrapper ...' off front of line + if(stline.StartsWith("(wrapper dynamic-method) System.Object:")) + { + stline = stline.Substring(39); + } + + // strip the (systemargtypes...) from our dynamic method names cuz it's messy + // 'default state_entry (XMRInstAbstract )' + // => 'default state_entry' + // 'CallSomethingThatThrows(string) (OpenSim.Region.ScriptEngine.YEngine.XMRInstance,string)' + // => 'CallSomethingThatThrows(string)' + int kwin = stline.IndexOf(" in "); + int br0x = stline.IndexOf(" [0x"); + int pastCloseParen = stline.Length; + if((kwin >= 0) && (br0x >= 0)) + pastCloseParen = Math.Min(kwin, br0x); + else if(kwin >= 0) + pastCloseParen = kwin; + else if(br0x >= 0) + pastCloseParen = br0x; + else + pastCloseParen = stline.Length; + int endFuncName = pastCloseParen; + while(endFuncName > 0) + { + if(stline[--endFuncName] == '(') + break; + } + while(endFuncName > 0) + { + if(stline[endFuncName - 1] != ' ') + break; + --endFuncName; + } + string funcName = stline.Substring(0, endFuncName); + KeyValuePair[] srcLocs; + if(m_ObjCode.scriptSrcLocss.TryGetValue(funcName, out srcLocs)) + { + stline = stline.Substring(0, endFuncName) + stline.Substring(pastCloseParen); + kwin = stline.IndexOf(" in "); + br0x = stline.IndexOf(" [0x"); + } + + // keyword 'in' is just before filename:linenumber that goes to end of line + // trim up the corresponding filename (ie, remove useless path info) + if(kwin >= 0) + { + int begfn = kwin + 4; + int slash = begfn; + for(int i = begfn; i < stline.Length; i++) + { + char c = stline[i]; + if((c == '/') || (c == '\\')) + slash = i + 1; + } + stline = stline.Substring(0, begfn) + stline.Substring(slash); + } + else if(srcLocs != null) + { + + // no filename:linenumber info, try to convert IL offset + if(br0x >= 0) + { + try + { + int begiloffs = br0x + 4; + int endiloffs = stline.IndexOf("]", begiloffs); + int iloffset = int.Parse(stline.Substring(begiloffs, endiloffs - begiloffs), + System.Globalization.NumberStyles.HexNumber); + + int srcLocIdx; + int srcLocLen = srcLocs.Length; + for(srcLocIdx = 0; ++srcLocIdx < srcLocLen;) + { + if(iloffset < srcLocs[srcLocIdx].Key) + break; + } + ScriptSrcLoc srcLoc = srcLocs[--srcLocIdx].Value; + + stline = stline.Substring(0, br0x) + " <" + + srcLoc.file + '(' + srcLoc.line + ',' + srcLoc.posn + ")>"; + } + catch + { + } + } + } + + // put edited line in output string + if(sb.Length > 0) + sb.AppendLine(); + sb.Append(" at "); + sb.Append(stline); + } + return sb.ToString(); + } + + /** + * @brief List fonts available. + */ + public LSL_List xmrFontsAvailable() + { + System.Drawing.FontFamily[] families = System.Drawing.FontFamily.Families; + object[] output = new object[families.Length]; + for(int i = 0; i < families.Length; i++) + output[i] = new LSL_String(families[i].Name); + + return new LSL_List(output); + } + + /************************\ + * Used by decompiler * + \************************/ + + public bool xmrRotationToBool(LSL_Rotation x) + { + return TypeCast.RotationToBool(x); + } + public bool xmrStringToBool(string x) + { + return TypeCast.StringToBool(x); + } + public bool xmrVectorToBool(LSL_Vector x) + { + return TypeCast.VectorToBool(x); + } + public bool xmrKeyToBool(string x) + { + return TypeCast.KeyToBool(x); + } + public bool xmrListToBool(LSL_List x) + { + return TypeCast.ListToBool(x); + } + + public int xmrStringCompare(string x, string y) + { + return string.Compare(x, y); + } + + /** + * @brief types of data we serialize + */ + private enum Ser: byte + { + NULL, + EVENTCODE, + LSLFLOAT, + LSLINT, + LSLKEY, + LSLLIST, + LSLROT, + LSLSTR, + LSLVEC, + SYSARRAY, + SYSDOUB, + SYSFLOAT, + SYSINT, + SYSSTR, + XMRARRAY, + DUPREF, + SYSBOOL, + XMRINST, + DELEGATE, + SDTCLOBJ, + SYSCHAR, + SYSERIAL, + THROWNEX + } + + /** + * @brief Write state out to a stream. + * Do not change script state. + */ + public void MigrateOut(BinaryWriter mow) + { + try + { + this.migrateOutWriter = mow; + this.migrateOutObjects = new Dictionary(); + this.migrateOutLists = new Dictionary(); + this.SendObjValue(this.ehArgs); + mow.Write(this.doGblInit); + mow.Write(this.stateCode); + mow.Write((int)this.eventCode); + this.glblVars.SendArrays(this.SendObjValue); + if(this.newStateCode >= 0) + { + mow.Write("**newStateCode**"); + mow.Write(this.newStateCode); + } + for(XMRStackFrame thisSF = this.stackFrames; thisSF != null; thisSF = thisSF.nextSF) + { + mow.Write(thisSF.funcName); + mow.Write(thisSF.callNo); + this.SendObjValue(thisSF.objArray); + } + mow.Write(""); + } + finally + { + this.migrateOutWriter = null; + this.migrateOutObjects = null; + this.migrateOutLists = null; + } + } + + /** + * @brief Write an object to the output stream. + * @param graph = object to send + */ + private BinaryWriter migrateOutWriter; + private Dictionary migrateOutObjects; + private Dictionary migrateOutLists; + public void SendObjValue(object graph) + { + BinaryWriter mow = this.migrateOutWriter; + + /* + * Value types (including nulls) are always output directly. + */ + if(graph == null) + { + mow.Write((byte)Ser.NULL); + return; + } + if(graph is ScriptEventCode) + { + mow.Write((byte)Ser.EVENTCODE); + mow.Write((int)graph); + return; + } + if(graph is LSL_Float) + { + mow.Write((byte)Ser.LSLFLOAT); + mow.Write((double)((LSL_Float)graph).value); + return; + } + if(graph is LSL_Integer) + { + mow.Write((byte)Ser.LSLINT); + mow.Write((int)((LSL_Integer)graph).value); + return; + } + if(graph is LSL_Key) + { + mow.Write((byte)Ser.LSLKEY); + LSL_Key key = (LSL_Key)graph; + SendObjValue(key.m_string); // m_string can be null + return; + } + if(graph is LSL_Rotation) + { + mow.Write((byte)Ser.LSLROT); + mow.Write((double)((LSL_Rotation)graph).x); + mow.Write((double)((LSL_Rotation)graph).y); + mow.Write((double)((LSL_Rotation)graph).z); + mow.Write((double)((LSL_Rotation)graph).s); + return; + } + if(graph is LSL_String) + { + mow.Write((byte)Ser.LSLSTR); + LSL_String str = (LSL_String)graph; + SendObjValue(str.m_string); // m_string can be null + return; + } + if(graph is LSL_Vector) + { + mow.Write((byte)Ser.LSLVEC); + mow.Write((double)((LSL_Vector)graph).x); + mow.Write((double)((LSL_Vector)graph).y); + mow.Write((double)((LSL_Vector)graph).z); + return; + } + if(graph is bool) + { + mow.Write((byte)Ser.SYSBOOL); + mow.Write((bool)graph); + return; + } + if(graph is double) + { + mow.Write((byte)Ser.SYSDOUB); + mow.Write((double)graph); + return; + } + if(graph is float) + { + mow.Write((byte)Ser.SYSFLOAT); + mow.Write((float)graph); + return; + } + if(graph is int) + { + mow.Write((byte)Ser.SYSINT); + mow.Write((int)graph); + return; + } + if(graph is char) + { + mow.Write((byte)Ser.SYSCHAR); + mow.Write((char)graph); + return; + } + + /* + * Script instance pointer is always just that. + */ + if(graph == this) + { + mow.Write((byte)Ser.XMRINST); + return; + } + + /* + * Convert lists to object type. + * This is compatible with old migration data and also + * two vars pointing to same list won't duplicate it. + */ + if(graph is LSL_List) + { + object[] data = ((LSL_List)graph).Data; + ObjLslList oll; + if(!this.migrateOutLists.TryGetValue(data, out oll)) + { + oll = new ObjLslList(); + oll.objarray = data; + this.migrateOutLists[data] = oll; + } + graph = oll; + } + + /* + * If this same exact object was already serialized, + * just output an index telling the receiver to use + * that same old object, rather than creating a whole + * new object with the same values. Also this prevents + * self-referencing objects (like arrays) from causing + * an infinite loop. + */ + int ident; + if(this.migrateOutObjects.TryGetValue(graph, out ident)) + { + mow.Write((byte)Ser.DUPREF); + mow.Write(ident); + return; + } + + /* + * Object not seen before, save its address with an unique + * ident number that the receiver can easily regenerate. + */ + ident = this.migrateOutObjects.Count; + this.migrateOutObjects.Add(graph, ident); + + /* + * Now output the object's value(s). + * If the object self-references, the object is alreay entered + * in the dictionary and so the self-reference will just emit + * a DUPREF tag instead of trying to output the whole object + * again. + */ + if(graph is ObjLslList) + { + mow.Write((byte)Ser.LSLLIST); + ObjLslList oll = (ObjLslList)graph; + SendObjValue(oll.objarray); + } + else if(graph is XMR_Array) + { + mow.Write((byte)Ser.XMRARRAY); + ((XMR_Array)graph).SendArrayObj(this.SendObjValue); + } + else if(graph is Array) + { + Array array = (Array)graph; + mow.Write((byte)Ser.SYSARRAY); + mow.Write(SysType2String(array.GetType().GetElementType())); + mow.Write((int)array.Length); + for(int i = 0; i < array.Length; i++) + this.SendObjValue(array.GetValue(i)); + } + else if(graph is string) + { + mow.Write((byte)Ser.SYSSTR); + mow.Write((string)graph); + } + else if(graph is Delegate) + { + Delegate del = (Delegate)graph; + mow.Write((byte)Ser.DELEGATE); + mow.Write(del.Method.Name); + Type delType = del.GetType(); + foreach(KeyValuePair kvp in m_ObjCode.sdObjTypesName) + { + TokenDeclSDType sdt = kvp.Value; + if(sdt is TokenDeclSDTypeDelegate) + { + TokenDeclSDTypeDelegate sdtd = (TokenDeclSDTypeDelegate)sdt; + if(sdtd.GetSysType() == delType) + { + mow.Write(kvp.Key); + goto found; + } + } + } + throw new Exception("cant find script-defined delegate for " + del.Method.Name + " type " + del.GetType()); + found: + SendObjValue(del.Target); + } + else if(graph is XMRSDTypeClObj) + { + mow.Write((byte)Ser.SDTCLOBJ); + ((XMRSDTypeClObj)graph).Capture(this.SendObjValue); + } + else if(graph is ScriptThrownException) + { + MemoryStream memoryStream = new MemoryStream(); + System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter = + new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); + bformatter.Serialize(memoryStream, graph); + byte[] rawBytes = memoryStream.ToArray(); + mow.Write((byte)Ser.THROWNEX); + mow.Write((int)rawBytes.Length); + mow.Write(rawBytes); + SendObjValue(((ScriptThrownException)graph).thrown); + } + else + { + MemoryStream memoryStream = new MemoryStream(); + System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter = + new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); + bformatter.Serialize(memoryStream, graph); + byte[] rawBytes = memoryStream.ToArray(); + mow.Write((byte)Ser.SYSERIAL); + mow.Write((int)rawBytes.Length); + mow.Write(rawBytes); + } + } + + /** + * @brief Use short strings for known type names. + */ + private static string SysType2String(Type type) + { + if(type.IsArray && (type.GetArrayRank() == 1)) + { + string str = KnownSysType2String(type.GetElementType()); + if(str != null) + return str + "[]"; + } + else + { + string str = KnownSysType2String(type); + if(str != null) + return str; + } + return type.ToString(); + } + private static string KnownSysType2String(Type type) + { + if(type == typeof(bool)) + return "bo"; + if(type == typeof(char)) + return "ch"; + if(type == typeof(Delegate)) + return "de"; + if(type == typeof(double)) + return "do"; + if(type == typeof(float)) + return "fl"; + if(type == typeof(int)) + return "in"; + if(type == typeof(LSL_List)) + return "li"; + if(type == typeof(object)) + return "ob"; + if(type == typeof(LSL_Rotation)) + return "ro"; + if(type == typeof(XMRSDTypeClObj)) + return "sc"; + if(type == typeof(string)) + return "st"; + if(type == typeof(LSL_Vector)) + return "ve"; + if(type == typeof(XMR_Array)) + return "xa"; + return null; + } + private static Type String2SysType(string str) + { + if(str.EndsWith("[]")) + return String2SysType(str.Substring(0, str.Length - 2)).MakeArrayType(); + + if(str == "bo") + return typeof(bool); + if(str == "ch") + return typeof(char); + if(str == "de") + return typeof(Delegate); + if(str == "do") + return typeof(double); + if(str == "fl") + return typeof(float); + if(str == "in") + return typeof(int); + if(str == "li") + return typeof(LSL_List); + if(str == "ob") + return typeof(object); + if(str == "ro") + return typeof(LSL_Rotation); + if(str == "sc") + return typeof(XMRSDTypeClObj); + if(str == "st") + return typeof(string); + if(str == "ve") + return typeof(LSL_Vector); + if(str == "xa") + return typeof(XMR_Array); + return Type.GetType(str, true); + } + + /** + * @brief Read state in from a stream. + */ + public void MigrateIn(BinaryReader mir) + { + try + { + this.migrateInReader = mir; + this.migrateInObjects = new Dictionary(); + this.ehArgs = (object[])this.RecvObjValue(); + this.doGblInit = mir.ReadBoolean(); + this.stateCode = mir.ReadInt32(); + this.eventCode = (ScriptEventCode)mir.ReadInt32(); + this.newStateCode = -1; + this.glblVars.RecvArrays(this.RecvObjValue); + XMRStackFrame lastSF = null; + string funcName; + while((funcName = mir.ReadString()) != "") + { + if(funcName == "**newStateCode**") + { + this.newStateCode = mir.ReadInt32(); + continue; + } + XMRStackFrame thisSF = new XMRStackFrame(); + thisSF.funcName = funcName; + thisSF.callNo = mir.ReadInt32(); + thisSF.objArray = (object[])this.RecvObjValue(); + if(lastSF == null) + this.stackFrames = thisSF; + else + lastSF.nextSF = thisSF; + lastSF = thisSF; + } + } + finally + { + this.migrateInReader = null; + this.migrateInObjects = null; + } + } + + /** + * @brief Read a single value from the stream. + * @returns value (boxed as needed) + */ + private BinaryReader migrateInReader; + private Dictionary migrateInObjects; + public object RecvObjValue() + { + BinaryReader mir = this.migrateInReader; + int ident = this.migrateInObjects.Count; + Ser code = (Ser)mir.ReadByte(); + switch(code) + { + case Ser.NULL: + return null; + + case Ser.EVENTCODE: + return (ScriptEventCode)mir.ReadInt32(); + + case Ser.LSLFLOAT: + return new LSL_Float(mir.ReadDouble()); + + case Ser.LSLINT: + return new LSL_Integer(mir.ReadInt32()); + + case Ser.LSLKEY: + return new LSL_Key((string)RecvObjValue()); + + case Ser.LSLLIST: + { + this.migrateInObjects.Add(ident, null); // placeholder + object[] data = (object[])RecvObjValue(); // read data, maybe using another index + LSL_List list = new LSL_List(data); // make LSL-level list + this.migrateInObjects[ident] = list; // fill in slot + return list; + } + + case Ser.LSLROT: + { + double x = mir.ReadDouble(); + double y = mir.ReadDouble(); + double z = mir.ReadDouble(); + double w = mir.ReadDouble(); + return new LSL_Rotation(x, y, z, w); + } + case Ser.LSLSTR: + return new LSL_String((string)RecvObjValue()); + + case Ser.LSLVEC: + { + double x = mir.ReadDouble(); + double y = mir.ReadDouble(); + double z = mir.ReadDouble(); + return new LSL_Vector(x, y, z); + } + + case Ser.SYSARRAY: + { + Type eletype = String2SysType(mir.ReadString()); + int length = mir.ReadInt32(); + Array array = Array.CreateInstance(eletype, length); + this.migrateInObjects.Add(ident, array); + for(int i = 0; i < length; i++) + array.SetValue(RecvObjValue(), i); + return array; + } + + case Ser.SYSBOOL: + return mir.ReadBoolean(); + + case Ser.SYSDOUB: + return mir.ReadDouble(); + + case Ser.SYSFLOAT: + return mir.ReadSingle(); + + case Ser.SYSINT: + return mir.ReadInt32(); + + case Ser.SYSCHAR: + return mir.ReadChar(); + + case Ser.SYSSTR: + string s = mir.ReadString(); + this.migrateInObjects.Add(ident, s); + return s; + + case Ser.XMRARRAY: + { + XMR_Array array = new XMR_Array(this); + this.migrateInObjects.Add(ident, array); + array.RecvArrayObj(this.RecvObjValue); + return array; + } + + case Ser.DUPREF: + { + ident = mir.ReadInt32(); + object obj = this.migrateInObjects[ident]; + if(obj is ObjLslList) + obj = new LSL_List(((ObjLslList)obj).objarray); + return obj; + } + + case Ser.XMRINST: + return this; + + case Ser.DELEGATE: + this.migrateInObjects.Add(ident, null); // placeholder + string name = mir.ReadString(); // function name + string sig = mir.ReadString(); // delegate type + object targ = this.RecvObjValue(); // 'this' object + Delegate del = this.GetScriptMethodDelegate(name, sig, targ); + this.migrateInObjects[ident] = del; // actual value + return del; + + case Ser.SDTCLOBJ: + XMRSDTypeClObj clobj = new XMRSDTypeClObj(); + this.migrateInObjects.Add(ident, clobj); + clobj.Restore(this, this.RecvObjValue); + return clobj; + + case Ser.SYSERIAL: + { + int rawLength = mir.ReadInt32(); + byte[] rawBytes = mir.ReadBytes(rawLength); + MemoryStream memoryStream = new MemoryStream(rawBytes); + System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter = + new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); + object graph = bformatter.Deserialize(memoryStream); + this.migrateInObjects.Add(ident, graph); + return graph; + } + + case Ser.THROWNEX: + { + int rawLength = mir.ReadInt32(); + byte[] rawBytes = mir.ReadBytes(rawLength); + MemoryStream memoryStream = new MemoryStream(rawBytes); + System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter = + new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); + object graph = bformatter.Deserialize(memoryStream); + this.migrateInObjects.Add(ident, graph); + ((ScriptThrownException)graph).thrown = RecvObjValue(); + return graph; + } + + default: + throw new Exception("bad stream code " + code.ToString()); + } + } + + // wrapper around list object arrays to make sure they are always object types for migration purposes + private class ObjLslList + { + public object[] objarray; + } + } + + // Any xmr...() methods that call CheckRun() must be tagged with this attribute + // so the ScriptCodeGen will know the method is non-trivial. + public class xmrMethodCallsCheckRunAttribute: Attribute + { + } + + // Any xmr...() methods in xmrengtest that call Stub() must be + // tagged with this attribute so the -builtins option will tell the user that + // they are a stub function. + public class xmrMethodIsNoisyAttribute: Attribute + { + } + + // Any script callable methods that really return a key not a string should be + // tagged with this attribute so the compiler will know they return type key and + // not type string. + public class xmrMethodReturnsKeyAttribute: Attribute + { + } + + [SerializableAttribute] + public class OutOfHeapException: Exception + { + public OutOfHeapException(int oldtotal, int newtotal, int limit) + : base("oldtotal=" + oldtotal + ", newtotal=" + newtotal + ", limit=" + limit) + { + } + } + + [SerializableAttribute] + public class OutOfStackException: Exception + { + } +} -- cgit v1.1