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

2110 lines
84 KiB
C#

/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
using System;
using System.Collections.Generic;
using System.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<somethingorother>()
public abstract LSL_List xmrEventDequeue (double timeout, int returnMask1, int returnMask2,
int backgroundMask1, int backgroundMask2);
[xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
public abstract void xmrEventEnqueue (LSL_List ev);
[xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
public abstract LSL_List xmrEventSaveDets ();
[xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
public abstract void xmrEventLoadDets (LSL_List dpList);
[xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
public abstract void xmrTrapRegionCrossing (int en);
[xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
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 <samestate>;' 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<string>) 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)
{
// '{' <keystring> ':' <value> [ ',' <keystring> ':' <value> ... ] '}'
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;
}
// '[' <value> [ ',' <value> ... ] ']'
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;
}
// '"'<string>'"'
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<object> (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) <IL 0x00d65, 0x03a53>
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<int, ScriptSrcLoc>[] 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<object,int> ();
this.migrateOutLists = new Dictionary<object[],ObjLslList> ();
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<object,int> migrateOutObjects;
private Dictionary<object[],ObjLslList> 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<string, TokenDeclSDType> 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<int, object> ();
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<int, object> 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<somethingorother>() 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 { }
}