/* * 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 { } }