/* * 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.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.XMREngine { 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 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 (); public abstract int xmrStackLeft (); [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); [xmrMethodIsNoisyAttribute] // calls Stub() public abstract void xmrTrapRegionCrossing (int en); [xmrMethodIsNoisyAttribute] // calls Stub() public abstract bool xmrSetObjRegPosRotAsync (LSL_Vector pos, LSL_Rotation rot, int options, int evcode, LSL_List evargs); /************************************\ * Constants available to scripts * \************************************/ public const int XMRSORPRA_FLYACROSS = 0x00000001; /**************************************************\ * 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 (xmrStackLeft () < 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 bool xmrSetObjRegPosRotAsync (LSL_Vector pos, LSL_Rotation rot, int evcode, LSL_List evargs) { return xmrSetObjRegPosRotAsync (pos, rot, 0, evcode, evargs); } 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 // at (wrapper dynamic-method) object.__seh_0_30_default_state_entry (OpenSim.Region.ScriptEngine.XMREngine.XMRInstAbstract) public string XMRExceptionStackString (Exception ex) { string st = ex.StackTrace; StringBuilder sb = new StringBuilder (); int wrapDynMethObj = 0; int leftOffAt = 0; while ((wrapDynMethObj = st.IndexOf ("(wrapper dynamic-method) System.Object:", ++ wrapDynMethObj)) >= 0) { try { int begFuncName = wrapDynMethObj + 39; int endFuncName = st.IndexOf (" (", begFuncName); string funcName = st.Substring (begFuncName, endFuncName - begFuncName); KeyValuePair[] srcLocs = m_ObjCode.scriptSrcLocss[funcName]; int il0xPrefix = st.IndexOf (" [0x", endFuncName); int begILHex = il0xPrefix + 4; int endILHex = st.IndexOf (']', begILHex); string ilHex = st.Substring (begILHex, endILHex - begILHex); int offset = Int32.Parse (ilHex, System.Globalization.NumberStyles.HexNumber); int srcLocIdx; int srcLocLen = srcLocs.Length; for (srcLocIdx = 0; ++ srcLocIdx < srcLocLen;) { if (offset < srcLocs[srcLocIdx].Key) break; } ScriptSrcLoc srcLoc = srcLocs[--srcLocIdx].Value; sb.Append (st.Substring (leftOffAt, wrapDynMethObj - leftOffAt)); sb.Append (st.Substring (begFuncName, endFuncName - begFuncName)); sb.Append (" <"); sb.Append (srcLoc.file); sb.Append ('('); sb.Append (srcLoc.line); sb.Append (','); sb.Append (srcLoc.posn); sb.Append (")>"); leftOffAt = ++ endILHex; } catch { } } sb.Append (st.Substring (leftOffAt)); 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 s = mir.ReadDouble (); return new LSL_Rotation (x, y, z, s); } 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; } } /** * @brief Common access to script microthread. */ public interface IScriptUThread : IDisposable { Exception ResumeEx (); // called by macrothread to resume execution at most recent Hiber() Exception StartEx (); // called by macrothread to start execution at CallSEH() int Active (); // called by macrothread to query state of microthread int StackLeft (); // called by microthread to query amount of remaining stack space void Hiber (); // called by microthread to hibernate } // 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 { } }