add experimental script engine XMRengine donated by mrieker (DreamNation) And our Melanie. ***DANGER*** ***TESTONLY*** ***disable HG*** dont leave running when not looking... tp/crossing to Xengine will reset scripts. i do see a few issues but should be testable, so we can decide if we should invest more on it.
parent
c70b77a528
commit
83e2fee71b
|
@ -284,22 +284,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
// Remove from: Timers
|
||||
m_Timer[engine].UnSetTimerEvents(localID, itemID);
|
||||
|
||||
// Remove from: HttpRequest
|
||||
IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface<IHttpRequestModule>();
|
||||
if (iHttpReq != null)
|
||||
iHttpReq.StopHttpRequest(localID, itemID);
|
||||
|
||||
IWorldComm comms = engine.World.RequestModuleInterface<IWorldComm>();
|
||||
if (comms != null)
|
||||
comms.DeleteListener(itemID);
|
||||
|
||||
IXMLRPC xmlrpc = engine.World.RequestModuleInterface<IXMLRPC>();
|
||||
if (xmlrpc != null)
|
||||
if(engine.World != null)
|
||||
{
|
||||
xmlrpc.DeleteChannels(itemID);
|
||||
xmlrpc.CancelSRDRequests(itemID);
|
||||
}
|
||||
// Remove from: HttpRequest
|
||||
IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface<IHttpRequestModule>();
|
||||
if (iHttpReq != null)
|
||||
iHttpReq.StopHttpRequest(localID, itemID);
|
||||
|
||||
IWorldComm comms = engine.World.RequestModuleInterface<IWorldComm>();
|
||||
if (comms != null)
|
||||
comms.DeleteListener(itemID);
|
||||
|
||||
IXMLRPC xmlrpc = engine.World.RequestModuleInterface<IXMLRPC>();
|
||||
if (xmlrpc != null)
|
||||
{
|
||||
xmlrpc.DeleteChannels(itemID);
|
||||
xmlrpc.CancelSRDRequests(itemID);
|
||||
}
|
||||
}
|
||||
// Remove Sensors
|
||||
m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine {
|
||||
|
||||
public class DelegateCommon {
|
||||
private string sig; // rettype(arg1type,arg2type,...), eg, "void(list,string,integer)"
|
||||
private Type type; // resultant delegate type
|
||||
|
||||
private static Dictionary<string, DelegateCommon> delegateCommons = new Dictionary<string, DelegateCommon> ();
|
||||
private static Dictionary<Type, DelegateCommon> delegateCommonsBySysType = new Dictionary<Type, DelegateCommon> ();
|
||||
private static ModuleBuilder delegateModuleBuilder = null;
|
||||
public static Type[] constructorArgTypes = new Type[] { typeof (object), typeof (IntPtr) };
|
||||
|
||||
private DelegateCommon () { }
|
||||
|
||||
public static Type GetType (System.Type ret, System.Type[] args, string sig)
|
||||
{
|
||||
DelegateCommon dc;
|
||||
lock (delegateCommons) {
|
||||
if (!delegateCommons.TryGetValue (sig, out dc)) {
|
||||
dc = new DelegateCommon ();
|
||||
dc.sig = sig;
|
||||
dc.type = CreateDelegateType (sig, ret, args);
|
||||
delegateCommons.Add (sig, dc);
|
||||
delegateCommonsBySysType.Add (dc.type, dc);
|
||||
}
|
||||
}
|
||||
return dc.type;
|
||||
}
|
||||
|
||||
public static Type TryGetType (string sig)
|
||||
{
|
||||
DelegateCommon dc;
|
||||
lock (delegateCommons) {
|
||||
if (!delegateCommons.TryGetValue (sig, out dc)) dc = null;
|
||||
}
|
||||
return (dc == null) ? null : dc.type;
|
||||
}
|
||||
|
||||
public static string TryGetName (Type t)
|
||||
{
|
||||
DelegateCommon dc;
|
||||
lock (delegateCommons) {
|
||||
if (!delegateCommonsBySysType.TryGetValue (t, out dc)) dc = null;
|
||||
}
|
||||
return (dc == null) ? null : dc.sig;
|
||||
}
|
||||
|
||||
// http://blog.bittercoder.com/PermaLink,guid,a770377a-b1ad-4590-9145-36381757a52b.aspx
|
||||
private static Type CreateDelegateType (string name, Type retType, Type[] argTypes)
|
||||
{
|
||||
if (delegateModuleBuilder == null) {
|
||||
AssemblyName assembly = new AssemblyName();
|
||||
assembly.Name = "CustomDelegateAssembly";
|
||||
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run);
|
||||
delegateModuleBuilder = assemblyBuilder.DefineDynamicModule("CustomDelegateModule");
|
||||
}
|
||||
|
||||
TypeBuilder typeBuilder = delegateModuleBuilder.DefineType(name,
|
||||
TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class |
|
||||
TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof (MulticastDelegate));
|
||||
|
||||
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(
|
||||
MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
|
||||
CallingConventions.Standard, constructorArgTypes);
|
||||
constructorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
|
||||
|
||||
MethodBuilder methodBuilder = typeBuilder.DefineMethod("Invoke",
|
||||
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot |
|
||||
MethodAttributes.Virtual, retType, argTypes);
|
||||
methodBuilder.SetImplementationFlags(MethodImplAttributes.Managed | MethodImplAttributes.Runtime);
|
||||
|
||||
return typeBuilder.CreateType();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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 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 interface IEventHandlers {
|
||||
void at_rot_target (int tnum, LSL_Rotation targetrot, LSL_Rotation ourrot);
|
||||
void at_target (int tnum, LSL_Vector targetpos, LSL_Vector ourpos);
|
||||
void attach (string id);
|
||||
void changed (int change);
|
||||
void collision (int num_detected);
|
||||
void collision_end (int num_detected);
|
||||
void collision_start (int num_detected);
|
||||
void control (string id, int held, int change);
|
||||
void dataserver (string queryid, string data);
|
||||
void email (string time, string address, string subj, string message, int num_left);
|
||||
void http_request (string request_id, string method, string body);
|
||||
void http_response (string request_id, int status, LSL_List metadata, string body);
|
||||
void land_collision (LSL_Vector pos);
|
||||
void land_collision_end (LSL_Vector pos);
|
||||
void land_collision_start (LSL_Vector pos);
|
||||
void link_message (int sender_num, int num, string str, string id);
|
||||
void listen (int channel, string name, string id, string message);
|
||||
void money (string id, int amount);
|
||||
void moving_end ();
|
||||
void moving_start ();
|
||||
void no_sensor ();
|
||||
void not_at_rot_target ();
|
||||
void not_at_target ();
|
||||
void object_rez (string id);
|
||||
void on_rez (int start_param);
|
||||
void remote_data (int event_type, string channel, string message_id, string sender, int idata, string sdata);
|
||||
void run_time_permissions (int perm);
|
||||
void sensor (int num_detected);
|
||||
void state_entry ();
|
||||
void state_exit ();
|
||||
void timer ();
|
||||
void touch (int num_detected);
|
||||
void touch_start (int num_detected);
|
||||
void touch_end (int num_detected);
|
||||
void transaction_result(string id, int success, string data);
|
||||
void path_update(int type, LSL_List data);
|
||||
void region_cross(LSL_Vector newpos, LSL_Vector oldpos);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine {
|
||||
|
||||
public class InternalFuncDict : VarDict {
|
||||
|
||||
/**
|
||||
* @brief build dictionary of internal functions from an interface.
|
||||
* @param iface = interface with function definitions
|
||||
* @param inclSig = true: catalog by name with arg sig, eg, llSay(integer,string)
|
||||
* false: catalog by simple name only, eg, state_entry
|
||||
* @returns dictionary of function definition tokens
|
||||
*/
|
||||
public InternalFuncDict (Type iface, bool inclSig)
|
||||
: base (false)
|
||||
{
|
||||
/*
|
||||
* Loop through list of all methods declared in the interface.
|
||||
*/
|
||||
System.Reflection.MethodInfo[] ifaceMethods = iface.GetMethods ();
|
||||
foreach (System.Reflection.MethodInfo ifaceMethod in ifaceMethods) {
|
||||
string key = ifaceMethod.Name;
|
||||
|
||||
/*
|
||||
* Only do ones that begin with lower-case letters...
|
||||
* as any others can't be referenced by scripts
|
||||
*/
|
||||
if ((key[0] < 'a') || (key[0] > 'z')) continue;
|
||||
|
||||
try {
|
||||
|
||||
/*
|
||||
* Create a corresponding TokenDeclVar struct.
|
||||
*/
|
||||
System.Reflection.ParameterInfo[] parameters = ifaceMethod.GetParameters ();
|
||||
TokenArgDecl argDecl = new TokenArgDecl (null);
|
||||
for (int i = 0; i < parameters.Length; i++) {
|
||||
System.Reflection.ParameterInfo param = parameters[i];
|
||||
TokenType type = TokenType.FromSysType (null, param.ParameterType);
|
||||
TokenName name = new TokenName (null, param.Name);
|
||||
argDecl.AddArg (type, name);
|
||||
}
|
||||
TokenDeclVar declFunc = new TokenDeclVar (null, null, null);
|
||||
declFunc.name = new TokenName (null, key);
|
||||
declFunc.retType = TokenType.FromSysType (null, ifaceMethod.ReturnType);
|
||||
declFunc.argDecl = argDecl;
|
||||
|
||||
/*
|
||||
* Add the TokenDeclVar struct to the dictionary.
|
||||
*/
|
||||
this.AddEntry (declFunc);
|
||||
} catch (Exception except) {
|
||||
|
||||
string msg = except.ToString ();
|
||||
int i = msg.IndexOf ("\n");
|
||||
if (i > 0) msg = msg.Substring (0, i);
|
||||
Console.WriteLine ("InternalFuncDict*: {0}: {1}", key, msg);
|
||||
|
||||
///??? IGNORE ANY THAT FAIL - LIKE UNRECOGNIZED TYPE ???///
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Compile a script to produce a ScriptObjCode object
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
public partial class XMRInstance
|
||||
{
|
||||
/**
|
||||
* @brief Compile a script to produce a ScriptObjCode object
|
||||
* @returns object code pointer or null if compile error
|
||||
* also can throw compile error exception
|
||||
*/
|
||||
public ScriptObjCode Compile ()
|
||||
{
|
||||
bool oldObjFile = false;
|
||||
Stream objFileStream = null;
|
||||
StreamWriter asmFileWriter = null;
|
||||
string envar = null;
|
||||
string sourceHash = null;
|
||||
TextWriter saveSource = null;
|
||||
|
||||
string asmFileName = GetScriptFileName (m_ScriptObjCodeKey + ".xmrasm");
|
||||
string lslFileName = GetScriptFileName (m_ScriptObjCodeKey + ".lsl");
|
||||
string objFileName = GetScriptFileName (m_ScriptObjCodeKey + ".xmrobj");
|
||||
string tmpFileName = GetScriptFileName (m_ScriptObjCodeKey + ".xmrtmp");
|
||||
|
||||
/*
|
||||
* If we already have an object file, don't bother compiling.
|
||||
*/
|
||||
if (!m_ForceRecomp && File.Exists (objFileName)) {
|
||||
objFileStream = File.OpenRead (objFileName);
|
||||
oldObjFile = true;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* If source file empty, try to read from asset server.
|
||||
*/
|
||||
if (EmptySource (m_SourceCode)) {
|
||||
m_SourceCode = FetchSource (m_CameFrom);
|
||||
}
|
||||
|
||||
/*
|
||||
* Maybe write script source to a file for debugging.
|
||||
*/
|
||||
envar = Environment.GetEnvironmentVariable ("MMRScriptCompileSaveSource");
|
||||
if ((envar != null) && ((envar[0] & 1) != 0)) {
|
||||
m_log.Debug ("[XMREngine]: MMRScriptCompileSaveSource: saving to " + lslFileName);
|
||||
saveSource = File.CreateText (lslFileName);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse source string into tokens.
|
||||
*/
|
||||
TokenBegin tokenBegin;
|
||||
try {
|
||||
tokenBegin = TokenBegin.Construct(m_CameFrom, saveSource, ErrorHandler, m_SourceCode, out sourceHash);
|
||||
} finally {
|
||||
if (saveSource != null) saveSource.Close ();
|
||||
}
|
||||
if (tokenBegin == null) {
|
||||
m_log.Debug ("[XMREngine]: parsing errors on " + m_ScriptObjCodeKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create object file one way or another.
|
||||
*/
|
||||
try {
|
||||
objFileStream = File.Create (tmpFileName);
|
||||
|
||||
/*
|
||||
* Create abstract syntax tree from raw tokens.
|
||||
*/
|
||||
TokenScript tokenScript = ScriptReduce.Reduce(tokenBegin);
|
||||
if (tokenScript == null) {
|
||||
m_log.Warn ("[XMREngine]: reduction errors on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")");
|
||||
PrintCompilerErrors ();
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile abstract syntax tree to write object file.
|
||||
*/
|
||||
BinaryWriter objFileWriter = new BinaryWriter (objFileStream);
|
||||
bool ok = ScriptCodeGen.CodeGen(tokenScript, objFileWriter, sourceHash);
|
||||
if (!ok) {
|
||||
m_log.Warn ("[XMREngine]: compile error on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")");
|
||||
PrintCompilerErrors ();
|
||||
objFileStream.Close ();
|
||||
return null;
|
||||
}
|
||||
objFileStream.Close ();
|
||||
|
||||
/*
|
||||
* File has been completely written.
|
||||
* If there is an old one laying around, delete it now.
|
||||
* Then re-open the new file for reading from the beginning.
|
||||
*/
|
||||
if (File.Exists (objFileName)) {
|
||||
File.Replace (tmpFileName, objFileName, null);
|
||||
} else {
|
||||
File.Move (tmpFileName, objFileName);
|
||||
}
|
||||
objFileStream = File.OpenRead (objFileName);
|
||||
} finally {
|
||||
|
||||
/*
|
||||
* In case something went wrong writing temp file, delete it.
|
||||
*/
|
||||
try {
|
||||
File.Delete (tmpFileName);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we just wrote the .xmrobj file, maybe save disassembly.
|
||||
*/
|
||||
envar = Environment.GetEnvironmentVariable ("MMRScriptCompileSaveILGen");
|
||||
if ((envar != null) && ((envar[0] & 1) != 0)) {
|
||||
m_log.Debug ("[XMREngine]: MMRScriptCompileSaveILGen: saving to " + asmFileName);
|
||||
asmFileWriter = File.CreateText (asmFileName);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read object file to create ScriptObjCode object.
|
||||
* Maybe also write disassembly to a file for debugging.
|
||||
*/
|
||||
BinaryReader objFileReader = new BinaryReader (objFileStream);
|
||||
ScriptObjCode scriptObjCode = null;
|
||||
try {
|
||||
scriptObjCode = new ScriptObjCode (objFileReader, asmFileWriter, null);
|
||||
if (scriptObjCode != null) {
|
||||
scriptObjCode.fileDateUtc = File.GetLastWriteTimeUtc (objFileName);
|
||||
}
|
||||
} finally {
|
||||
objFileReader.Close ();
|
||||
if (asmFileWriter != null) {
|
||||
asmFileWriter.Flush ();
|
||||
asmFileWriter.Close ();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Maybe an old object file has reached its expiration date.
|
||||
*/
|
||||
if (oldObjFile && (scriptObjCode != null) && scriptObjCode.IsExpired ()) {
|
||||
m_log.Debug ("[XMREngine]: expiration reached on " + m_ScriptObjCodeKey + ", reloading");
|
||||
m_ForceRecomp = true;
|
||||
scriptObjCode = Compile ();
|
||||
}
|
||||
|
||||
return scriptObjCode;
|
||||
}
|
||||
|
||||
private void PrintCompilerErrors ()
|
||||
{
|
||||
m_log.Info ("[XMREngine]: - " + m_Part.GetWorldPosition () + " " + m_DescName);
|
||||
foreach (string error in m_CompilerErrors) {
|
||||
m_log.Info ("[XMREngine]: - " + error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check for empty source, allowing for a first line of //... script engine selector.
|
||||
*/
|
||||
public static bool EmptySource (string source)
|
||||
{
|
||||
int len = source.Length;
|
||||
bool skipeol = false;
|
||||
for (int i = 0; i < len; i ++) {
|
||||
char c = source[i];
|
||||
skipeol &= c != '\n';
|
||||
skipeol |= (c == '/') && (i + 1 < len) && (source[i+1] == '/');
|
||||
if ((c > ' ') && !skipeol) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
|
||||
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
|
||||
using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
|
||||
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
|
||||
using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
|
||||
using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
|
||||
using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine {
|
||||
|
||||
public class ScriptConst {
|
||||
|
||||
public static Dictionary<string, ScriptConst> scriptConstants = Init ();
|
||||
|
||||
/**
|
||||
* @brief look up the value of a given built-in constant.
|
||||
* @param name = name of constant
|
||||
* @returns null: no constant by that name defined
|
||||
* else: pointer to ScriptConst struct
|
||||
*/
|
||||
public static ScriptConst Lookup (string name)
|
||||
{
|
||||
ScriptConst sc;
|
||||
if (!scriptConstants.TryGetValue (name, out sc)) sc = null;
|
||||
return sc;
|
||||
}
|
||||
|
||||
private static Dictionary<string, ScriptConst> Init ()
|
||||
{
|
||||
Dictionary<string, ScriptConst> sc = new Dictionary<string, ScriptConst> ();
|
||||
|
||||
/*
|
||||
* For every event code, define XMREVENTCODE_<eventname> and XMREVENTMASKn_<eventname> symbols.
|
||||
*/
|
||||
for (int i = 0; i < 64; i ++) {
|
||||
try {
|
||||
string s = ((ScriptEventCode)i).ToString ();
|
||||
if ((s.Length > 0) && (s[0] >= 'a') && (s[0] <= 'z')) {
|
||||
new ScriptConst (sc,
|
||||
"XMREVENTCODE_" + s,
|
||||
new CompValuInteger (new TokenTypeInt (null), i));
|
||||
int n = i / 32 + 1;
|
||||
int m = 1 << (i % 32);
|
||||
new ScriptConst (sc,
|
||||
"XMREVENTMASK" + n + "_" + s,
|
||||
new CompValuInteger (new TokenTypeInt (null), m));
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
|
||||
/*
|
||||
* Also get all the constants from XMRInstAbstract and ScriptBaseClass etc as well.
|
||||
*/
|
||||
for (Type t = typeof (XMRInstAbstract); t != typeof (object); t = t.BaseType) {
|
||||
AddInterfaceConstants (sc, t.GetFields ());
|
||||
}
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add all constants defined by the given interface.
|
||||
*/
|
||||
// this one accepts only upper-case named fields
|
||||
public static void AddInterfaceConstants (Dictionary<string, ScriptConst> sc, FieldInfo[] allFields)
|
||||
{
|
||||
List<FieldInfo> ucfs = new List<FieldInfo> (allFields.Length);
|
||||
foreach (FieldInfo f in allFields) {
|
||||
string fieldName = f.Name;
|
||||
int i;
|
||||
for (i = fieldName.Length; -- i >= 0;) {
|
||||
if ("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".IndexOf (fieldName[i]) < 0) break;
|
||||
}
|
||||
if (i < 0) ucfs.Add (f);
|
||||
}
|
||||
AddInterfaceConstants (sc, ucfs.GetEnumerator ());
|
||||
}
|
||||
|
||||
// this one accepts all fields given to it
|
||||
public static void AddInterfaceConstants (Dictionary<string, ScriptConst> sc, IEnumerator<FieldInfo> fields)
|
||||
{
|
||||
if (sc == null) sc = scriptConstants;
|
||||
|
||||
for (fields.Reset (); fields.MoveNext ();) {
|
||||
FieldInfo constField = fields.Current;
|
||||
Type fieldType = constField.FieldType;
|
||||
CompValu cv;
|
||||
|
||||
/*
|
||||
* The location of a simple number is the number itself.
|
||||
* Access to the value gets compiled as an ldc instruction.
|
||||
*/
|
||||
if (fieldType == typeof (double)) {
|
||||
cv = new CompValuFloat (new TokenTypeFloat (null),
|
||||
(double)(double)constField.GetValue (null));
|
||||
} else if (fieldType == typeof (int)) {
|
||||
cv = new CompValuInteger (new TokenTypeInt (null),
|
||||
(int)constField.GetValue (null));
|
||||
} else if (fieldType == typeof (LSL_Integer)) {
|
||||
cv = new CompValuInteger (new TokenTypeInt (null),
|
||||
((LSL_Integer)constField.GetValue (null)).value);
|
||||
}
|
||||
|
||||
/*
|
||||
* The location of a string is the string itself.
|
||||
* Access to the value gets compiled as an ldstr instruction.
|
||||
*/
|
||||
else if (fieldType == typeof (string)) {
|
||||
cv = new CompValuString (new TokenTypeStr (null),
|
||||
(string)constField.GetValue (null));
|
||||
} else if (fieldType == typeof (LSL_String)) {
|
||||
cv = new CompValuString (new TokenTypeStr (null),
|
||||
(string)(LSL_String)constField.GetValue (null));
|
||||
}
|
||||
|
||||
/*
|
||||
* The location of everything else (objects) is the static field in the interface definition.
|
||||
* Access to the value gets compiled as an ldsfld instruction.
|
||||
*/
|
||||
else {
|
||||
cv = new CompValuSField (TokenType.FromSysType (null, fieldType), constField);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add to dictionary.
|
||||
*/
|
||||
new ScriptConst (sc, constField.Name, cv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add arbitrary constant available to script compilation.
|
||||
* CAUTION: These values get compiled-in to a script and must not
|
||||
* change over time as previously compiled scripts will
|
||||
* still have the old values.
|
||||
*/
|
||||
public static ScriptConst AddConstant (string name, object value)
|
||||
{
|
||||
CompValu cv = null;
|
||||
|
||||
if (value is char) {
|
||||
cv = new CompValuChar (new TokenTypeChar (null), (char)value);
|
||||
}
|
||||
if (value is double) {
|
||||
cv = new CompValuFloat (new TokenTypeFloat (null), (double)(double)value);
|
||||
}
|
||||
if (value is float) {
|
||||
cv = new CompValuFloat (new TokenTypeFloat (null), (double)(float)value);
|
||||
}
|
||||
if (value is int) {
|
||||
cv = new CompValuInteger (new TokenTypeInt (null), (int)value);
|
||||
}
|
||||
if (value is string) {
|
||||
cv = new CompValuString (new TokenTypeStr (null), (string)value);
|
||||
}
|
||||
|
||||
if (value is LSL_Float) {
|
||||
cv = new CompValuFloat (new TokenTypeFloat (null), (double)((LSL_Float)value).value);
|
||||
}
|
||||
if (value is LSL_Integer) {
|
||||
cv = new CompValuInteger (new TokenTypeInt (null), ((LSL_Integer)value).value);
|
||||
}
|
||||
if (value is LSL_Rotation) {
|
||||
LSL_Rotation r = (LSL_Rotation)value;
|
||||
CompValu x = new CompValuFloat (new TokenTypeFloat (null), r.x);
|
||||
CompValu y = new CompValuFloat (new TokenTypeFloat (null), r.y);
|
||||
CompValu z = new CompValuFloat (new TokenTypeFloat (null), r.z);
|
||||
CompValu s = new CompValuFloat (new TokenTypeFloat (null), r.s);
|
||||
cv = new CompValuRot (new TokenTypeRot (null), x, y, z, s);
|
||||
}
|
||||
if (value is LSL_String) {
|
||||
cv = new CompValuString (new TokenTypeStr (null), (string)(LSL_String)value);
|
||||
}
|
||||
if (value is LSL_Vector) {
|
||||
LSL_Vector v = (LSL_Vector)value;
|
||||
CompValu x = new CompValuFloat (new TokenTypeFloat (null), v.x);
|
||||
CompValu y = new CompValuFloat (new TokenTypeFloat (null), v.y);
|
||||
CompValu z = new CompValuFloat (new TokenTypeFloat (null), v.z);
|
||||
cv = new CompValuVec (new TokenTypeVec (null), x, y, z);
|
||||
}
|
||||
|
||||
if (value is OpenMetaverse.Quaternion) {
|
||||
OpenMetaverse.Quaternion r = (OpenMetaverse.Quaternion)value;
|
||||
CompValu x = new CompValuFloat (new TokenTypeFloat (null), r.X);
|
||||
CompValu y = new CompValuFloat (new TokenTypeFloat (null), r.Y);
|
||||
CompValu z = new CompValuFloat (new TokenTypeFloat (null), r.Z);
|
||||
CompValu s = new CompValuFloat (new TokenTypeFloat (null), r.W);
|
||||
cv = new CompValuRot (new TokenTypeRot (null), x, y, z, s);
|
||||
}
|
||||
if (value is OpenMetaverse.UUID) {
|
||||
cv = new CompValuString (new TokenTypeKey (null), value.ToString ());
|
||||
}
|
||||
if (value is OpenMetaverse.Vector3) {
|
||||
OpenMetaverse.Vector3 v = (OpenMetaverse.Vector3)value;
|
||||
CompValu x = new CompValuFloat (new TokenTypeFloat (null), v.X);
|
||||
CompValu y = new CompValuFloat (new TokenTypeFloat (null), v.Y);
|
||||
CompValu z = new CompValuFloat (new TokenTypeFloat (null), v.Z);
|
||||
cv = new CompValuVec (new TokenTypeVec (null), x, y, z);
|
||||
}
|
||||
|
||||
if (cv == null) throw new Exception ("bad type " + value.GetType ().Name);
|
||||
return new ScriptConst (scriptConstants, name, cv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Instance variables
|
||||
*/
|
||||
public string name;
|
||||
public CompValu rVal;
|
||||
|
||||
private ScriptConst (Dictionary<string, ScriptConst> lc, string name, CompValu rVal)
|
||||
{
|
||||
lc.Add (name, this);
|
||||
this.name = name;
|
||||
this.rVal = rVal;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine {
|
||||
|
||||
/**
|
||||
* @brief List of event codes that can be passed to StartEventHandler().
|
||||
* Must have same name as corresponding event handler name, so
|
||||
* the compiler will know what column in the seht to put the
|
||||
* event handler entrypoint in.
|
||||
*
|
||||
* Also, ScriptConst.Init() builds symbols of name XMREVENTCODE_<name>
|
||||
* and XMREVENTMASK<n>_<name> with the values and masks of all symbols
|
||||
* in range 0..63 that begin with a lower-case letter for scripts to
|
||||
* reference.
|
||||
*/
|
||||
public enum ScriptEventCode : int {
|
||||
|
||||
// used by XMRInstance to indicate no event being processed
|
||||
None = -1,
|
||||
|
||||
// must be bit numbers of equivalent values in ...
|
||||
// OpenSim.Region.ScriptEngine.Shared.ScriptBase.scriptEvents
|
||||
// ... so they can be passed to m_Part.SetScriptEvents().
|
||||
attach = 0,
|
||||
state_exit = 1,
|
||||
timer = 2,
|
||||
touch = 3,
|
||||
collision = 4,
|
||||
collision_end = 5,
|
||||
collision_start = 6,
|
||||
control = 7,
|
||||
dataserver = 8,
|
||||
email = 9,
|
||||
http_response = 10,
|
||||
land_collision = 11,
|
||||
land_collision_end = 12,
|
||||
land_collision_start = 13,
|
||||
at_target = 14,
|
||||
listen = 15,
|
||||
money = 16,
|
||||
moving_end = 17,
|
||||
moving_start = 18,
|
||||
not_at_rot_target = 19,
|
||||
not_at_target = 20,
|
||||
touch_start = 21,
|
||||
object_rez = 22,
|
||||
remote_data = 23,
|
||||
at_rot_target = 24,
|
||||
transaction_result = 25,
|
||||
run_time_permissions = 28,
|
||||
touch_end = 29,
|
||||
state_entry = 30,
|
||||
|
||||
// events not passed to m_Part.SetScriptEvents().
|
||||
changed = 33,
|
||||
link_message = 34,
|
||||
no_sensor = 35,
|
||||
on_rez = 36,
|
||||
sensor = 37,
|
||||
http_request = 38,
|
||||
|
||||
path_update = 40,
|
||||
|
||||
// XMRE specific
|
||||
region_cross = 63,
|
||||
|
||||
// marks highest numbered event, ie, number of columns in seht.
|
||||
Size = 64
|
||||
}
|
||||
}
|
|
@ -0,0 +1,664 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
|
||||
using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
|
||||
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
|
||||
using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
|
||||
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
|
||||
using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
|
||||
using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
|
||||
using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
|
||||
|
||||
/**
|
||||
* @brief Generate code for the backend API calls.
|
||||
*/
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
public abstract class TokenDeclInline : TokenDeclVar {
|
||||
public static VarDict inlineFunctions = CreateDictionary ();
|
||||
|
||||
public abstract void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args);
|
||||
|
||||
private static string[] noCheckRuns;
|
||||
private static string[] keyReturns;
|
||||
|
||||
protected bool isTaggedCallsCheckRun;
|
||||
|
||||
/**
|
||||
* @brief Create a dictionary of inline backend API functions.
|
||||
*/
|
||||
private static VarDict CreateDictionary ()
|
||||
{
|
||||
/*
|
||||
* For those listed in noCheckRun, we just generate the call (simple computations).
|
||||
* For all others, we generate the call then a call to CheckRun().
|
||||
*/
|
||||
noCheckRuns = new string[] {
|
||||
"llBase64ToString",
|
||||
"llCSV2List",
|
||||
"llDeleteSubList",
|
||||
"llDeleteSubString",
|
||||
"llDumpList2String",
|
||||
"llEscapeURL",
|
||||
"llEuler2Rot",
|
||||
"llGetListEntryType",
|
||||
"llGetListLength",
|
||||
"llGetSubString",
|
||||
"llGetUnixTime",
|
||||
"llInsertString",
|
||||
"llList2CSV",
|
||||
"llList2Float",
|
||||
"llList2Integer",
|
||||
"llList2Key",
|
||||
"llList2List",
|
||||
"llList2ListStrided",
|
||||
"llList2Rot",
|
||||
"llList2String",
|
||||
"llList2Vector",
|
||||
"llListFindList",
|
||||
"llListInsertList",
|
||||
"llListRandomize",
|
||||
"llListReplaceList",
|
||||
"llListSort",
|
||||
"llListStatistics",
|
||||
"llMD5String",
|
||||
"llParseString2List",
|
||||
"llParseStringKeepNulls",
|
||||
"llRot2Euler",
|
||||
"llStringLength",
|
||||
"llStringToBase64",
|
||||
"llStringTrim",
|
||||
"llSubStringIndex",
|
||||
"llUnescapeURL"
|
||||
};
|
||||
|
||||
/*
|
||||
* These functions really return a 'key' even though we see them as
|
||||
* returning 'string' because OpenSim has key and string as same type.
|
||||
*/
|
||||
keyReturns = new string[] {
|
||||
"llAvatarOnLinkSitTarget",
|
||||
"llAvatarOnSitTarget",
|
||||
"llDetectedKey",
|
||||
"llDetectedOwner",
|
||||
"llGenerateKey",
|
||||
"llGetCreator",
|
||||
"llGetInventoryCreator",
|
||||
"llGetInventoryKey",
|
||||
"llGetKey",
|
||||
"llGetLandOwnerAt",
|
||||
"llGetLinkKey",
|
||||
"llGetNotecardLine",
|
||||
"llGetNumberOfNotecardLines",
|
||||
"llGetOwner",
|
||||
"llGetOwnerKey",
|
||||
"llGetPermissionsKey",
|
||||
"llHTTPRequest",
|
||||
"llList2Key",
|
||||
"llRequestAgentData",
|
||||
"llRequestDisplayName",
|
||||
"llRequestInventoryData",
|
||||
"llRequestSecureURL",
|
||||
"llRequestSimulatorData",
|
||||
"llRequestURL",
|
||||
"llRequestUsername",
|
||||
"llSendRemoteData",
|
||||
"llTransferLindenDollars"
|
||||
};
|
||||
|
||||
VarDict ifd = new VarDict (false);
|
||||
|
||||
Type[] oneDoub = new Type[] { typeof (double) };
|
||||
Type[] twoDoubs = new Type[] { typeof (double), typeof (double) };
|
||||
|
||||
/*
|
||||
* Mono generates an FPU instruction for many math calls.
|
||||
*/
|
||||
new TokenDeclInline_LLAbs (ifd);
|
||||
new TokenDeclInline_Math (ifd, "llAcos(float)", "Acos", oneDoub);
|
||||
new TokenDeclInline_Math (ifd, "llAsin(float)", "Asin", oneDoub);
|
||||
new TokenDeclInline_Math (ifd, "llAtan2(float,float)", "Atan2", twoDoubs);
|
||||
new TokenDeclInline_Math (ifd, "llCos(float)", "Cos", oneDoub);
|
||||
new TokenDeclInline_Math (ifd, "llFabs(float)", "Abs", oneDoub);
|
||||
new TokenDeclInline_Math (ifd, "llLog(float)", "Log", oneDoub);
|
||||
new TokenDeclInline_Math (ifd, "llLog10(float)", "Log10", oneDoub);
|
||||
new TokenDeclInline_Math (ifd, "llPow(float,float)", "Pow", twoDoubs);
|
||||
new TokenDeclInline_LLRound (ifd);
|
||||
new TokenDeclInline_Math (ifd, "llSin(float)", "Sin", oneDoub);
|
||||
new TokenDeclInline_Math (ifd, "llSqrt(float)", "Sqrt", oneDoub);
|
||||
new TokenDeclInline_Math (ifd, "llTan(float)", "Tan", oneDoub);
|
||||
|
||||
/*
|
||||
* Something weird about the code generation for these calls, so they all have their own handwritten code generators.
|
||||
*/
|
||||
new TokenDeclInline_GetFreeMemory (ifd);
|
||||
new TokenDeclInline_GetUsedMemory (ifd);
|
||||
|
||||
/*
|
||||
* These are all the xmr...() calls directly in XMRInstAbstract.
|
||||
* Includes the calls from ScriptBaseClass that has all the stubs
|
||||
* which convert XMRInstAbstract to the various <NAME>_Api contexts.
|
||||
*/
|
||||
MethodInfo[] absmeths = typeof (XMRInstAbstract).GetMethods ();
|
||||
AddInterfaceMethods (ifd, absmeths, null);
|
||||
|
||||
return ifd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add API functions from the given interface to list of built-in functions.
|
||||
* Only functions beginning with a lower-case letter are entered, all others ignored.
|
||||
* @param ifd = internal function dictionary to add them to
|
||||
* @param ifaceMethods = list of API functions
|
||||
* @param acf = which field in XMRInstanceSuperType holds method's 'this' pointer
|
||||
*/
|
||||
// this one accepts only names beginning with a lower-case letter
|
||||
public static void AddInterfaceMethods (VarDict ifd, MethodInfo[] ifaceMethods, FieldInfo acf)
|
||||
{
|
||||
List<MethodInfo> lcms = new List<MethodInfo> (ifaceMethods.Length);
|
||||
foreach (MethodInfo meth in ifaceMethods)
|
||||
{
|
||||
string name = meth.Name;
|
||||
if ((name[0] >= 'a') && (name[0] <= 'z')) {
|
||||
lcms.Add (meth);
|
||||
}
|
||||
}
|
||||
AddInterfaceMethods (ifd, lcms.GetEnumerator (), acf);
|
||||
}
|
||||
|
||||
// this one accepts all methods given to it
|
||||
public static void AddInterfaceMethods (VarDict ifd, IEnumerator<MethodInfo> ifaceMethods, FieldInfo acf)
|
||||
{
|
||||
if (ifd == null) ifd = inlineFunctions;
|
||||
|
||||
for (ifaceMethods.Reset (); ifaceMethods.MoveNext ();) {
|
||||
MethodInfo ifaceMethod = ifaceMethods.Current;
|
||||
string key = ifaceMethod.Name;
|
||||
|
||||
try {
|
||||
/*
|
||||
* See if we will generate a call to CheckRun() right
|
||||
* after we generate a call to the function.
|
||||
* If function begins with xmr, assume we will not call CheckRun()
|
||||
* Otherwise, assume we will call CheckRun()
|
||||
*/
|
||||
bool dcr = !key.StartsWith ("xmr");
|
||||
foreach (string ncr in noCheckRuns) {
|
||||
if (ncr == key) {
|
||||
dcr = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add function to dictionary.
|
||||
*/
|
||||
new TokenDeclInline_BEApi (ifd, dcr, ifaceMethod, acf);
|
||||
} catch {
|
||||
///??? IGNORE ANY THAT FAIL - LIKE UNRECOGNIZED TYPE ???///
|
||||
///??? and OVERLOADED NAMES ???///
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add an inline function definition to the dictionary.
|
||||
* @param ifd = dictionary to add inline definition to
|
||||
* @param doCheckRun = true iff the generated code or the function itself can possibly call CheckRun()
|
||||
* @param nameArgSig = inline function signature string, in form <name>(<arglsltypes>,...)
|
||||
* @param retType = return type, use TokenTypeVoid if no return value
|
||||
*/
|
||||
protected TokenDeclInline (VarDict ifd,
|
||||
bool doCheckRun,
|
||||
string nameArgSig,
|
||||
TokenType retType)
|
||||
: base (null, null, null)
|
||||
{
|
||||
this.retType = retType;
|
||||
this.triviality = doCheckRun ? Triviality.complex : Triviality.trivial;
|
||||
|
||||
int j = nameArgSig.IndexOf ('(');
|
||||
this.name = new TokenName (null, nameArgSig.Substring (0, j ++));
|
||||
|
||||
this.argDecl = new TokenArgDecl (null);
|
||||
if (nameArgSig[j] != ')') {
|
||||
int i;
|
||||
TokenName name;
|
||||
TokenType type;
|
||||
|
||||
for (i = j; nameArgSig[i] != ')'; i ++) {
|
||||
if (nameArgSig[i] == ',') {
|
||||
type = TokenType.FromLSLType (null, nameArgSig.Substring (j, i - j));
|
||||
name = new TokenName (null, "arg" + this.argDecl.varDict.Count);
|
||||
this.argDecl.AddArg (type, name);
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
type = TokenType.FromLSLType (null, nameArgSig.Substring (j, i - j));
|
||||
name = new TokenName (null, "arg" + this.argDecl.varDict.Count);
|
||||
this.argDecl.AddArg (type, name);
|
||||
}
|
||||
|
||||
this.location = new CompValuInline (this);
|
||||
if (ifd == null) ifd = inlineFunctions;
|
||||
ifd.AddEntry (this);
|
||||
}
|
||||
|
||||
protected TokenDeclInline (VarDict ifd,
|
||||
bool doCheckRun,
|
||||
MethodInfo methInfo)
|
||||
: base (null, null, null)
|
||||
{
|
||||
TokenType retType = TokenType.FromSysType (null, methInfo.ReturnType);
|
||||
|
||||
this.isTaggedCallsCheckRun = IsTaggedCallsCheckRun (methInfo);
|
||||
this.name = new TokenName (null, methInfo.Name);
|
||||
this.retType = GetRetType (methInfo, retType);
|
||||
this.argDecl = GetArgDecl (methInfo.GetParameters ());
|
||||
this.triviality = (doCheckRun || this.isTaggedCallsCheckRun) ? Triviality.complex : Triviality.trivial;
|
||||
this.location = new CompValuInline (this);
|
||||
|
||||
if (ifd == null) ifd = inlineFunctions;
|
||||
ifd.AddEntry (this);
|
||||
}
|
||||
|
||||
private static TokenArgDecl GetArgDecl (ParameterInfo[] parameters)
|
||||
{
|
||||
TokenArgDecl argDecl = new TokenArgDecl (null);
|
||||
foreach (ParameterInfo pi in parameters) {
|
||||
TokenType type = TokenType.FromSysType (null, pi.ParameterType);
|
||||
TokenName name = new TokenName (null, pi.Name);
|
||||
argDecl.AddArg (type, name);
|
||||
}
|
||||
return argDecl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The above code assumes all methods beginning with 'xmr' are trivial, ie,
|
||||
* they do not call CheckRun() and also we do not generate a CheckRun()
|
||||
* call after they return. So if an 'xmr' method does call CheckRun(), it
|
||||
* must be tagged with attribute 'xmrMethodCallsCheckRunAttribute' so we know
|
||||
* the method is not trivial. But in neither case do we emit our own call
|
||||
* to CheckRun(), the 'xmr' method must do its own. We do however set up a
|
||||
* call label before the call to the non-trivial 'xmr' method so when we are
|
||||
* restoring the call stack, the restore will call directly in to the 'xmr'
|
||||
* method without re-executing any code before the call to the 'xmr' method.
|
||||
*/
|
||||
private static bool IsTaggedCallsCheckRun (MethodInfo methInfo)
|
||||
{
|
||||
return (methInfo != null) &&
|
||||
Attribute.IsDefined (methInfo, typeof (xmrMethodCallsCheckRunAttribute));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The dumbass OpenSim has key and string as the same type so non-ll
|
||||
* methods must be tagged with xmrMethodReturnsKeyAttribute if we
|
||||
* are to think they return a key type, otherwise we will think they
|
||||
* return string.
|
||||
*/
|
||||
private static TokenType GetRetType (MethodInfo methInfo, TokenType retType)
|
||||
{
|
||||
if ((methInfo != null) && (retType != null) && (retType is TokenTypeStr)) {
|
||||
if (Attribute.IsDefined (methInfo, typeof (xmrMethodReturnsKeyAttribute))) {
|
||||
return ChangeToKeyType (retType);
|
||||
}
|
||||
|
||||
string mn = methInfo.Name;
|
||||
foreach (string kr in keyReturns) {
|
||||
if (kr == mn) return ChangeToKeyType (retType);
|
||||
}
|
||||
|
||||
}
|
||||
return retType;
|
||||
}
|
||||
private static TokenType ChangeToKeyType (TokenType retType)
|
||||
{
|
||||
if (retType is TokenTypeLSLString) {
|
||||
retType = new TokenTypeLSLKey (null);
|
||||
} else {
|
||||
retType = new TokenTypeKey (null);
|
||||
}
|
||||
return retType;
|
||||
}
|
||||
|
||||
public virtual MethodInfo GetMethodInfo ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print out a list of all the built-in functions and constants.
|
||||
*/
|
||||
public delegate void WriteLine (string str);
|
||||
public static void PrintBuiltins (bool inclNoisyTag, WriteLine writeLine)
|
||||
{
|
||||
writeLine ("\nBuilt-in functions:\n");
|
||||
SortedDictionary<string, TokenDeclInline> bifs = new SortedDictionary<string, TokenDeclInline> ();
|
||||
foreach (TokenDeclVar bif in TokenDeclInline.inlineFunctions) {
|
||||
bifs.Add (bif.fullName, (TokenDeclInline)bif);
|
||||
}
|
||||
foreach (TokenDeclInline bif in bifs.Values) {
|
||||
char noisy = (!inclNoisyTag || !IsTaggedNoisy (bif.GetMethodInfo ())) ? ' ' : (bif.retType is TokenTypeVoid) ? 'N' : 'R';
|
||||
writeLine (noisy + " " + bif.retType.ToString ().PadLeft (8) + " " + bif.fullName);
|
||||
}
|
||||
if (inclNoisyTag) {
|
||||
writeLine ("\nN - stub that writes name and arguments to stdout");
|
||||
writeLine ("R - stub that writes name and arguments to stdout then reads return value from stdin");
|
||||
writeLine (" format is: function_name : return_value");
|
||||
writeLine (" example: llKey2Name:\"Kunta Kinte\"");
|
||||
}
|
||||
|
||||
writeLine ("\nBuilt-in constants:\n");
|
||||
SortedDictionary<string, ScriptConst> scs = new SortedDictionary<string, ScriptConst> ();
|
||||
int widest = 0;
|
||||
foreach (ScriptConst sc in ScriptConst.scriptConstants.Values) {
|
||||
if (widest < sc.name.Length) widest = sc.name.Length;
|
||||
scs.Add (sc.name, sc);
|
||||
}
|
||||
foreach (ScriptConst sc in scs.Values) {
|
||||
writeLine (" " + sc.rVal.type.ToString ().PadLeft (8) + " " + sc.name.PadRight (widest) + " = " + BuiltInConstVal (sc.rVal));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsTaggedNoisy (MethodInfo methInfo)
|
||||
{
|
||||
return (methInfo != null) && Attribute.IsDefined (methInfo, typeof (xmrMethodIsNoisyAttribute));
|
||||
}
|
||||
|
||||
public static string BuiltInConstVal (CompValu rVal)
|
||||
{
|
||||
if (rVal is CompValuInteger) {
|
||||
int x = ((CompValuInteger)rVal).x;
|
||||
return "0x" + x.ToString ("X8") + " = " + x.ToString ().PadLeft (11);
|
||||
}
|
||||
if (rVal is CompValuFloat) return ((CompValuFloat)rVal).x.ToString ();
|
||||
if (rVal is CompValuString) {
|
||||
StringBuilder sb = new StringBuilder ();
|
||||
PrintParam (sb, ((CompValuString)rVal).x);
|
||||
return sb.ToString ();
|
||||
}
|
||||
if (rVal is CompValuSField) {
|
||||
FieldInfo fi = ((CompValuSField)rVal).field;
|
||||
StringBuilder sb = new StringBuilder ();
|
||||
PrintParam (sb, fi.GetValue (null));
|
||||
return sb.ToString ();
|
||||
}
|
||||
return rVal.ToString (); // just prints the type
|
||||
}
|
||||
|
||||
public static void PrintParam (StringBuilder sb, object p)
|
||||
{
|
||||
if (p == null) {
|
||||
sb.Append ("null");
|
||||
} else if (p is LSL_List) {
|
||||
sb.Append ('[');
|
||||
object[] d = ((LSL_List)p).Data;
|
||||
for (int i = 0; i < d.Length; i ++) {
|
||||
if (i > 0) sb.Append (',');
|
||||
PrintParam (sb, d[i]);
|
||||
}
|
||||
sb.Append (']');
|
||||
} else if (p is LSL_Rotation) {
|
||||
LSL_Rotation r = (LSL_Rotation)p;
|
||||
sb.Append ('<');
|
||||
sb.Append (r.x);
|
||||
sb.Append (',');
|
||||
sb.Append (r.y);
|
||||
sb.Append (',');
|
||||
sb.Append (r.z);
|
||||
sb.Append (',');
|
||||
sb.Append (r.s);
|
||||
sb.Append ('>');
|
||||
} else if (p is LSL_String) {
|
||||
PrintParamString (sb, (string)(LSL_String)p);
|
||||
} else if (p is LSL_Vector) {
|
||||
LSL_Vector v = (LSL_Vector)p;
|
||||
sb.Append ('<');
|
||||
sb.Append (v.x);
|
||||
sb.Append (',');
|
||||
sb.Append (v.y);
|
||||
sb.Append (',');
|
||||
sb.Append (v.z);
|
||||
sb.Append ('>');
|
||||
} else if (p is string) {
|
||||
PrintParamString (sb, (string)p);
|
||||
} else {
|
||||
sb.Append (p.ToString ());
|
||||
}
|
||||
}
|
||||
|
||||
public static void PrintParamString (StringBuilder sb, string p)
|
||||
{
|
||||
sb.Append ('"');
|
||||
foreach (char c in p) {
|
||||
if (c == '\b') {
|
||||
sb.Append ("\\b");
|
||||
continue;
|
||||
}
|
||||
if (c == '\n') {
|
||||
sb.Append ("\\n");
|
||||
continue;
|
||||
}
|
||||
if (c == '\r') {
|
||||
sb.Append ("\\r");
|
||||
continue;
|
||||
}
|
||||
if (c == '\t') {
|
||||
sb.Append ("\\t");
|
||||
continue;
|
||||
}
|
||||
if (c == '"') {
|
||||
sb.Append ("\\\"");
|
||||
continue;
|
||||
}
|
||||
if (c == '\\') {
|
||||
sb.Append ("\\\\");
|
||||
continue;
|
||||
}
|
||||
sb.Append (c);
|
||||
}
|
||||
sb.Append ('"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Code generators...
|
||||
* @param scg = script we are generating code for
|
||||
* @param result = type/location for result (type matches function definition)
|
||||
* @param args = type/location of arguments (types match function definition)
|
||||
*/
|
||||
|
||||
public class TokenDeclInline_LLAbs : TokenDeclInline {
|
||||
public TokenDeclInline_LLAbs (VarDict ifd)
|
||||
: base (ifd, false, "llAbs(integer)", new TokenTypeInt (null)) { }
|
||||
|
||||
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
|
||||
{
|
||||
ScriptMyLabel itsPosLabel = scg.ilGen.DefineLabel ("llAbstemp");
|
||||
|
||||
args[0].PushVal (scg, errorAt);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Dup);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Bge_S, itsPosLabel);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Neg);
|
||||
scg.ilGen.MarkLabel (itsPosLabel);
|
||||
result.Pop (scg, errorAt, retType);
|
||||
}
|
||||
}
|
||||
|
||||
public class TokenDeclInline_Math : TokenDeclInline {
|
||||
private MethodInfo methInfo;
|
||||
|
||||
public TokenDeclInline_Math (VarDict ifd, string sig, string name, Type[] args)
|
||||
: base (ifd, false, sig, new TokenTypeFloat (null))
|
||||
{
|
||||
methInfo = ScriptCodeGen.GetStaticMethod (typeof (System.Math), name, args);
|
||||
}
|
||||
|
||||
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
|
||||
{
|
||||
for (int i = 0; i < args.Length; i ++) {
|
||||
args[i].PushVal (scg, errorAt, argDecl.types[i]);
|
||||
}
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
|
||||
result.Pop (scg, errorAt, retType);
|
||||
}
|
||||
}
|
||||
|
||||
public class TokenDeclInline_LLRound : TokenDeclInline {
|
||||
|
||||
private static MethodInfo roundMethInfo = ScriptCodeGen.GetStaticMethod (typeof (System.Math), "Round",
|
||||
new Type[] { typeof (double), typeof (MidpointRounding) });
|
||||
|
||||
public TokenDeclInline_LLRound (VarDict ifd)
|
||||
: base (ifd, false, "llRound(float)", new TokenTypeInt (null)) { }
|
||||
|
||||
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
|
||||
{
|
||||
args[0].PushVal (scg, errorAt, new TokenTypeFloat (null));
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, (int)System.MidpointRounding.AwayFromZero);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, roundMethInfo);
|
||||
result.Pop (scg, errorAt, new TokenTypeFloat (null));
|
||||
}
|
||||
}
|
||||
|
||||
public class TokenDeclInline_GetFreeMemory : TokenDeclInline {
|
||||
private static readonly MethodInfo getFreeMemMethInfo = typeof (XMRInstAbstract).GetMethod ("xmrHeapLeft", new Type[] { });
|
||||
|
||||
public TokenDeclInline_GetFreeMemory (VarDict ifd)
|
||||
: base (ifd, false, "llGetFreeMemory()", new TokenTypeInt (null)) { }
|
||||
|
||||
// appears as llGetFreeMemory() in script source code
|
||||
// but actually calls xmrHeapLeft()
|
||||
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
|
||||
{
|
||||
scg.PushXMRInst ();
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, getFreeMemMethInfo);
|
||||
result.Pop (scg, errorAt, new TokenTypeInt (null));
|
||||
}
|
||||
}
|
||||
|
||||
public class TokenDeclInline_GetUsedMemory : TokenDeclInline {
|
||||
private static readonly MethodInfo getUsedMemMethInfo = typeof (XMRInstAbstract).GetMethod ("xmrHeapUsed", new Type[] { });
|
||||
|
||||
public TokenDeclInline_GetUsedMemory (VarDict ifd)
|
||||
: base (ifd, false, "llGetUsedMemory()", new TokenTypeInt (null)) { }
|
||||
|
||||
// appears as llGetUsedMemory() in script source code
|
||||
// but actually calls xmrHeapUsed()
|
||||
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
|
||||
{
|
||||
scg.PushXMRInst ();
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, getUsedMemMethInfo);
|
||||
result.Pop (scg, errorAt, new TokenTypeInt (null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate code for the usual ll...() functions.
|
||||
*/
|
||||
public class TokenDeclInline_BEApi : TokenDeclInline {
|
||||
private static readonly MethodInfo fixLLParcelMediaQuery = ScriptCodeGen.GetStaticMethod
|
||||
(typeof (XMRInstAbstract), "FixLLParcelMediaQuery", new Type[] { typeof (LSL_List) });
|
||||
|
||||
private static readonly MethodInfo fixLLParcelMediaCommandList = ScriptCodeGen.GetStaticMethod
|
||||
(typeof (XMRInstAbstract), "FixLLParcelMediaCommandList", new Type[] { typeof (LSL_List) });
|
||||
|
||||
public bool doCheckRun;
|
||||
private FieldInfo apiContextField;
|
||||
private MethodInfo methInfo;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param ifd = dictionary to add the function to
|
||||
* @param dcr = append a call to CheckRun()
|
||||
* @param methInfo = ll...() method to be called
|
||||
*/
|
||||
public TokenDeclInline_BEApi (VarDict ifd, bool dcr, MethodInfo methInfo, FieldInfo acf)
|
||||
: base (ifd, dcr, methInfo)
|
||||
{
|
||||
this.methInfo = methInfo;
|
||||
doCheckRun = dcr;
|
||||
apiContextField = acf;
|
||||
}
|
||||
|
||||
public override MethodInfo GetMethodInfo ()
|
||||
{
|
||||
return methInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate call to backend API function (eg llSay()) maybe followed by a call to CheckRun().
|
||||
* @param scg = script being compiled
|
||||
* @param result = where to place result (might be void)
|
||||
* @param args = script-visible arguments to pass to API function
|
||||
*/
|
||||
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
|
||||
{
|
||||
if (isTaggedCallsCheckRun) { // see if 'xmr' method that calls CheckRun() internally
|
||||
new ScriptCodeGen.CallLabel (scg, errorAt); // if so, put a call label immediately before it
|
||||
// .. so restoring the frame will jump immediately to the
|
||||
// .. call without re-executing any code before this
|
||||
}
|
||||
if (!methInfo.IsStatic) {
|
||||
scg.PushXMRInst (); // XMRInstanceSuperType pointer
|
||||
if (apiContextField != null) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldfld, apiContextField);
|
||||
// 'this' pointer for API function
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < args.Length; i ++) { // push arguments, boxing/unboxing as needed
|
||||
args[i].PushVal (scg, errorAt, argDecl.types[i]);
|
||||
}
|
||||
if (methInfo.Name == "llParcelMediaQuery") {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaQuery);
|
||||
}
|
||||
if (methInfo.Name == "llParcelMediaCommandList") {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaCommandList);
|
||||
}
|
||||
if (methInfo.IsVirtual) { // call API function
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Callvirt, methInfo);
|
||||
} else {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
|
||||
}
|
||||
result.Pop (scg, errorAt, retType); // pop result, boxing/unboxing as needed
|
||||
if (isTaggedCallsCheckRun) {
|
||||
scg.openCallLabel = null;
|
||||
}
|
||||
if (doCheckRun) {
|
||||
scg.EmitCallCheckRun (errorAt, false); // maybe call CheckRun()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
public interface ScriptMyILGen
|
||||
{
|
||||
string methName { get; }
|
||||
ScriptMyLocal DeclareLocal (Type type, string name);
|
||||
ScriptMyLabel DefineLabel (string name);
|
||||
void BeginExceptionBlock ();
|
||||
void BeginCatchBlock (Type excType);
|
||||
void BeginFinallyBlock ();
|
||||
void EndExceptionBlock ();
|
||||
void Emit (Token errorAt, OpCode opcode);
|
||||
void Emit (Token errorAt, OpCode opcode, FieldInfo field);
|
||||
void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal);
|
||||
void Emit (Token errorAt, OpCode opcode, Type type);
|
||||
void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel);
|
||||
void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels);
|
||||
void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method);
|
||||
void Emit (Token errorAt, OpCode opcode, MethodInfo method);
|
||||
void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor);
|
||||
void Emit (Token errorAt, OpCode opcode, double value);
|
||||
void Emit (Token errorAt, OpCode opcode, float value);
|
||||
void Emit (Token errorAt, OpCode opcode, int value);
|
||||
void Emit (Token errorAt, OpCode opcode, string value);
|
||||
void MarkLabel (ScriptMyLabel myLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief One of these per label defined in the function.
|
||||
*/
|
||||
public class ScriptMyLabel {
|
||||
public string name;
|
||||
public int number;
|
||||
|
||||
public GraphNodeMarkLabel whereAmI;
|
||||
public Type[] stackDepth;
|
||||
public bool[] stackBoxeds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief One of these per local variable defined in the function.
|
||||
*/
|
||||
public class ScriptMyLocal {
|
||||
public string name;
|
||||
public Type type;
|
||||
public int number;
|
||||
|
||||
public bool isReferenced;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* 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.XMREngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
public delegate void ScriptEventHandler (XMRInstAbstract instance);
|
||||
|
||||
/*
|
||||
* This object represents the output of the compilation.
|
||||
* Once the compilation is complete, its contents should be
|
||||
* considered 'read-only', so it can be shared among multiple
|
||||
* instances of the script.
|
||||
*
|
||||
* It gets created by ScriptCodeGen.
|
||||
* It gets used by XMRInstance to create script instances.
|
||||
*/
|
||||
public class ScriptObjCode
|
||||
{
|
||||
public string sourceHash; // source text hash code
|
||||
|
||||
public XMRInstArSizes glblSizes = new XMRInstArSizes ();
|
||||
// number of global variables of various types
|
||||
|
||||
public string[] stateNames; // convert state number to corresponding string
|
||||
|
||||
public ScriptEventHandler[,] scriptEventHandlerTable;
|
||||
// entrypoints to all event handler functions
|
||||
// 1st subscript = state code number (0=default)
|
||||
// 2nd subscript = event code number
|
||||
// null entry means no handler defined for that state,event
|
||||
|
||||
public Dictionary<string, TokenDeclSDType> sdObjTypesName;
|
||||
// all script-defined types by name
|
||||
public TokenDeclSDType[] sdObjTypesIndx;
|
||||
// all script-defined types by sdTypeIndex
|
||||
|
||||
public Dictionary<Type, string> sdDelTypes;
|
||||
// all script-defined delegates (including anonymous)
|
||||
|
||||
public Dictionary<string, DynamicMethod> dynamicMethods;
|
||||
// all dyanmic methods
|
||||
|
||||
public Dictionary<string, KeyValuePair<int, ScriptSrcLoc>[]> scriptSrcLocss;
|
||||
// method,iloffset -> source file,line,posn
|
||||
|
||||
public int refCount; // used by engine to keep track of number of
|
||||
// instances that are using this object code
|
||||
|
||||
public Dictionary<string,Dictionary<int,string>> globalVarNames = new Dictionary<string,Dictionary<int,string>> ();
|
||||
|
||||
public DateTime fileDateUtc;
|
||||
public int expiryDays = Int32.MaxValue;
|
||||
public bool IsExpired ()
|
||||
{
|
||||
return (DateTime.UtcNow.Ticks - fileDateUtc.Ticks) / 10000000 / 86400 >= expiryDays;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fill in ScriptObjCode from an XMREngine object file.
|
||||
* 'objFileReader' is a serialized form of the CIL code we generated
|
||||
* 'asmFileWriter' is where we write the disassembly to (or null if not wanted)
|
||||
* 'srcFileWriter' is where we write the decompilation to (or null if not wanted)
|
||||
* Throws an exception if there is any error (theoretically).
|
||||
*/
|
||||
public ScriptObjCode (BinaryReader objFileReader, TextWriter asmFileWriter, TextWriter srcFileWriter)
|
||||
{
|
||||
/*
|
||||
* Check version number to make sure we know how to process file contents.
|
||||
*/
|
||||
char[] ocm = objFileReader.ReadChars (ScriptCodeGen.OBJECT_CODE_MAGIC.Length);
|
||||
if (new String (ocm) != ScriptCodeGen.OBJECT_CODE_MAGIC) {
|
||||
throw new Exception ("not an XMR object file (bad magic)");
|
||||
}
|
||||
int cvv = objFileReader.ReadInt32 ();
|
||||
if (cvv != ScriptCodeGen.COMPILED_VERSION_VALUE) {
|
||||
throw new CVVMismatchException (cvv, ScriptCodeGen.COMPILED_VERSION_VALUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in simple parts of scriptObjCode object.
|
||||
*/
|
||||
sourceHash = objFileReader.ReadString ();
|
||||
expiryDays = objFileReader.ReadInt32 ();
|
||||
glblSizes.ReadFromFile (objFileReader);
|
||||
|
||||
int nStates = objFileReader.ReadInt32 ();
|
||||
|
||||
stateNames = new string[nStates];
|
||||
for (int i = 0; i < nStates; i ++) {
|
||||
stateNames[i] = objFileReader.ReadString ();
|
||||
if (asmFileWriter != null) {
|
||||
asmFileWriter.WriteLine (" state[{0}] = {1}", i, stateNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (asmFileWriter != null) {
|
||||
glblSizes.WriteAsmFile (asmFileWriter, "numGbl");
|
||||
}
|
||||
|
||||
string gblName;
|
||||
while ((gblName = objFileReader.ReadString ()) != "") {
|
||||
string gblType = objFileReader.ReadString ();
|
||||
int gblIndex = objFileReader.ReadInt32 ();
|
||||
Dictionary<int,string> names;
|
||||
if (!globalVarNames.TryGetValue (gblType, out names)) {
|
||||
names = new Dictionary<int,string> ();
|
||||
globalVarNames.Add (gblType, names);
|
||||
}
|
||||
names.Add (gblIndex, gblName);
|
||||
if (asmFileWriter != null) {
|
||||
asmFileWriter.WriteLine (" {0} = {1}[{2}]", gblName, gblType, gblIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in script-defined types.
|
||||
*/
|
||||
sdObjTypesName = new Dictionary<string, TokenDeclSDType> ();
|
||||
sdDelTypes = new Dictionary<Type, string> ();
|
||||
int maxIndex = -1;
|
||||
while ((gblName = objFileReader.ReadString ()) != "") {
|
||||
TokenDeclSDType sdt = TokenDeclSDType.ReadFromFile (sdObjTypesName,
|
||||
gblName, objFileReader, asmFileWriter);
|
||||
sdObjTypesName.Add (gblName, sdt);
|
||||
if (maxIndex < sdt.sdTypeIndex) maxIndex = sdt.sdTypeIndex;
|
||||
if (sdt is TokenDeclSDTypeDelegate) {
|
||||
sdDelTypes.Add (sdt.GetSysType (), gblName);
|
||||
}
|
||||
}
|
||||
sdObjTypesIndx = new TokenDeclSDType[maxIndex+1];
|
||||
foreach (TokenDeclSDType sdt in sdObjTypesName.Values) {
|
||||
sdObjTypesIndx[sdt.sdTypeIndex] = sdt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now fill in the methods (the hard part).
|
||||
*/
|
||||
scriptEventHandlerTable = new ScriptEventHandler[nStates,(int)ScriptEventCode.Size];
|
||||
dynamicMethods = new Dictionary<string, DynamicMethod> ();
|
||||
scriptSrcLocss = new Dictionary<string, KeyValuePair<int, ScriptSrcLoc>[]> ();
|
||||
|
||||
ObjectTokens objectTokens = null;
|
||||
if (asmFileWriter != null) {
|
||||
objectTokens = new OTDisassemble (this, asmFileWriter);
|
||||
} else if (srcFileWriter != null) {
|
||||
objectTokens = new OTDecompile (this, srcFileWriter);
|
||||
}
|
||||
|
||||
try {
|
||||
ScriptObjWriter.CreateObjCode (sdObjTypesName, objFileReader, this, objectTokens);
|
||||
} finally {
|
||||
if (objectTokens != null) objectTokens.Close ();
|
||||
}
|
||||
|
||||
/*
|
||||
* We enter all script event handler methods in the ScriptEventHandler table.
|
||||
* They are named: <statename> <eventname>
|
||||
*/
|
||||
foreach (KeyValuePair<string, DynamicMethod> kvp in dynamicMethods) {
|
||||
string methName = kvp.Key;
|
||||
int i = methName.IndexOf (' ');
|
||||
if (i < 0) continue;
|
||||
string stateName = methName.Substring (0, i);
|
||||
string eventName = methName.Substring (++ i);
|
||||
int stateCode;
|
||||
for (stateCode = stateNames.Length; -- stateCode >= 0;) {
|
||||
if (stateNames[stateCode] == stateName) break;
|
||||
}
|
||||
int eventCode = (int)Enum.Parse (typeof (ScriptEventCode), eventName);
|
||||
scriptEventHandlerTable[stateCode,eventCode] =
|
||||
(ScriptEventHandler)kvp.Value.CreateDelegate (typeof (ScriptEventHandler));
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in all script-defined class vtables.
|
||||
*/
|
||||
foreach (TokenDeclSDType sdt in sdObjTypesIndx) {
|
||||
if ((sdt != null) && (sdt is TokenDeclSDTypeClass)) {
|
||||
TokenDeclSDTypeClass sdtc = (TokenDeclSDTypeClass)sdt;
|
||||
sdtc.FillVTables (this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called once for every method found in objFileReader file.
|
||||
* It enters the method in the ScriptObjCode object table so it can be called.
|
||||
*/
|
||||
public void EndMethod (DynamicMethod method, Dictionary<int, ScriptSrcLoc> srcLocs)
|
||||
{
|
||||
/*
|
||||
* Save method object code pointer.
|
||||
*/
|
||||
dynamicMethods.Add (method.Name, method);
|
||||
|
||||
/*
|
||||
* Build and sort iloffset -> source code location array.
|
||||
*/
|
||||
int n = srcLocs.Count;
|
||||
KeyValuePair<int, ScriptSrcLoc>[] srcLocArray = new KeyValuePair<int, ScriptSrcLoc>[n];
|
||||
n = 0;
|
||||
foreach (KeyValuePair<int, ScriptSrcLoc> kvp in srcLocs) srcLocArray[n++] = kvp;
|
||||
Array.Sort (srcLocArray, endMethodWrapper);
|
||||
|
||||
/*
|
||||
* Save sorted array.
|
||||
*/
|
||||
scriptSrcLocss.Add (method.Name, srcLocArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called once for every method found in objFileReader file.
|
||||
* It enters the method in the ScriptObjCode object table so it can be called.
|
||||
*/
|
||||
private static EndMethodWrapper endMethodWrapper = new EndMethodWrapper ();
|
||||
private class EndMethodWrapper : System.Collections.IComparer {
|
||||
public int Compare (object x, object y)
|
||||
{
|
||||
KeyValuePair<int, ScriptSrcLoc> kvpx = (KeyValuePair<int, ScriptSrcLoc>)x;
|
||||
KeyValuePair<int, ScriptSrcLoc> kvpy = (KeyValuePair<int, ScriptSrcLoc>)y;
|
||||
return kvpx.Key - kvpy.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,947 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
|
||||
using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
|
||||
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
|
||||
using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
|
||||
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
|
||||
using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
|
||||
using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
|
||||
using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
|
||||
|
||||
/**
|
||||
* @brief Wrapper class for ILGenerator.
|
||||
* It writes the object code to a file and can then make real ILGenerator calls
|
||||
* based on the file's contents.
|
||||
*/
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
public enum ScriptObjWriterCode : byte {
|
||||
BegMethod, EndMethod, TheEnd,
|
||||
DclLabel, DclLocal, DclMethod, MarkLabel,
|
||||
EmitNull, EmitField, EmitLocal, EmitType, EmitLabel, EmitMethodExt,
|
||||
EmitMethodInt, EmitCtor, EmitDouble, EmitFloat, EmitInteger, EmitString,
|
||||
EmitLabels,
|
||||
BegExcBlk, BegCatBlk, BegFinBlk, EndExcBlk
|
||||
}
|
||||
|
||||
public class ScriptObjWriter : ScriptMyILGen
|
||||
{
|
||||
private static Dictionary<short, OpCode> opCodes = PopulateOpCodes ();
|
||||
private static Dictionary<string, Type> string2Type = PopulateS2T ();
|
||||
private static Dictionary<Type, string> type2String = PopulateT2S ();
|
||||
|
||||
private static MethodInfo monoGetCurrentOffset = typeof (ILGenerator).GetMethod ("Mono_GetCurrentOffset",
|
||||
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null,
|
||||
new Type[] { typeof (ILGenerator) }, null);
|
||||
|
||||
private static readonly OpCode[] opCodesLdcI4M1P8 = new OpCode[] {
|
||||
OpCodes.Ldc_I4_M1, OpCodes.Ldc_I4_0, OpCodes.Ldc_I4_1, OpCodes.Ldc_I4_2, OpCodes.Ldc_I4_3,
|
||||
OpCodes.Ldc_I4_4, OpCodes.Ldc_I4_5, OpCodes.Ldc_I4_6, OpCodes.Ldc_I4_7, OpCodes.Ldc_I4_8
|
||||
};
|
||||
|
||||
private BinaryWriter objFileWriter;
|
||||
private string lastErrorAtFile = "";
|
||||
private int lastErrorAtLine = 0;
|
||||
private int lastErrorAtPosn = 0;
|
||||
|
||||
private Dictionary<Type, string> sdTypesRev = new Dictionary<Type, string> ();
|
||||
public int labelNumber = 0;
|
||||
public int localNumber = 0;
|
||||
|
||||
private string _methName;
|
||||
public string methName { get { return _methName; } }
|
||||
|
||||
public Type retType;
|
||||
public Type[] argTypes;
|
||||
|
||||
/**
|
||||
* @brief Begin function declaration
|
||||
* @param sdTypes = script-defined types
|
||||
* @param methName = name of the method being declared, eg, "Verify(array,list,string)"
|
||||
* @param retType = its return value type
|
||||
* @param argTypes[] = its argument types
|
||||
* @param objFileWriter = file to write its object code to
|
||||
*
|
||||
* After calling this function, the following functions should be called:
|
||||
* this.BegMethod ();
|
||||
* this.<as required> ();
|
||||
* this.EndMethod ();
|
||||
*
|
||||
* The design of this object is such that many constructors may be called,
|
||||
* but once a BegMethod() is called for one of the objects, no method may
|
||||
* called for any of the other objects until EndMethod() is called (or it
|
||||
* would break up the object stream for that method). But we need to have
|
||||
* many constructors possible so we get function headers at the beginning
|
||||
* of the object file in case there are forward references to the functions.
|
||||
*/
|
||||
public ScriptObjWriter (TokenScript tokenScript, string methName, Type retType, Type[] argTypes, string[] argNames, BinaryWriter objFileWriter)
|
||||
{
|
||||
this._methName = methName;
|
||||
this.retType = retType;
|
||||
this.argTypes = argTypes;
|
||||
this.objFileWriter = objFileWriter;
|
||||
|
||||
/*
|
||||
* Build list that translates system-defined types to script defined types.
|
||||
*/
|
||||
foreach (TokenDeclSDType sdt in tokenScript.sdSrcTypesValues) {
|
||||
Type sys = sdt.GetSysType();
|
||||
if (sys != null) sdTypesRev[sys] = sdt.longName.val;
|
||||
}
|
||||
|
||||
/*
|
||||
* This tells the reader to call 'new DynamicMethod()' to create
|
||||
* the function header. Then any forward reference calls to this
|
||||
* method will have a MethodInfo struct to call.
|
||||
*/
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.DclMethod);
|
||||
objFileWriter.Write (methName);
|
||||
objFileWriter.Write (GetStrFromType (retType));
|
||||
|
||||
int nArgs = argTypes.Length;
|
||||
objFileWriter.Write (nArgs);
|
||||
for (int i = 0; i < nArgs; i ++) {
|
||||
objFileWriter.Write (GetStrFromType (argTypes[i]));
|
||||
objFileWriter.Write (argNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin outputting object code for the function
|
||||
*/
|
||||
public void BegMethod ()
|
||||
{
|
||||
/*
|
||||
* This tells the reader to call methodInfo.GetILGenerator()
|
||||
* so it can start writing CIL code for the method.
|
||||
*/
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.BegMethod);
|
||||
objFileWriter.Write (methName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End of object code for the function
|
||||
*/
|
||||
public void EndMethod ()
|
||||
{
|
||||
/*
|
||||
* This tells the reader that all code for the method has
|
||||
* been written and so it will typically call CreateDelegate()
|
||||
* to finalize the method and create an entrypoint.
|
||||
*/
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EndMethod);
|
||||
|
||||
objFileWriter = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Declare a local variable for use by the function
|
||||
*/
|
||||
public ScriptMyLocal DeclareLocal (Type type, string name)
|
||||
{
|
||||
ScriptMyLocal myLocal = new ScriptMyLocal ();
|
||||
myLocal.type = type;
|
||||
myLocal.name = name;
|
||||
myLocal.number = localNumber ++;
|
||||
myLocal.isReferenced = true; // so ScriptCollector won't optimize references away
|
||||
return DeclareLocal (myLocal);
|
||||
}
|
||||
public ScriptMyLocal DeclareLocal (ScriptMyLocal myLocal)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.DclLocal);
|
||||
objFileWriter.Write (myLocal.number);
|
||||
objFileWriter.Write (myLocal.name);
|
||||
objFileWriter.Write (GetStrFromType (myLocal.type));
|
||||
return myLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Define a label for use by the function
|
||||
*/
|
||||
public ScriptMyLabel DefineLabel (string name)
|
||||
{
|
||||
ScriptMyLabel myLabel = new ScriptMyLabel ();
|
||||
myLabel.name = name;
|
||||
myLabel.number = labelNumber ++;
|
||||
return DefineLabel (myLabel);
|
||||
}
|
||||
public ScriptMyLabel DefineLabel (ScriptMyLabel myLabel)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.DclLabel);
|
||||
objFileWriter.Write (myLabel.number);
|
||||
objFileWriter.Write (myLabel.name);
|
||||
return myLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief try/catch blocks.
|
||||
*/
|
||||
public void BeginExceptionBlock ()
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.BegExcBlk);
|
||||
}
|
||||
|
||||
public void BeginCatchBlock (Type excType)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.BegCatBlk);
|
||||
objFileWriter.Write (GetStrFromType (excType));
|
||||
}
|
||||
|
||||
public void BeginFinallyBlock ()
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.BegFinBlk);
|
||||
}
|
||||
|
||||
public void EndExceptionBlock ()
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EndExcBlk);
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitNull);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, FieldInfo field)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitField);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (GetStrFromType (field.ReflectedType));
|
||||
objFileWriter.Write (field.Name);
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitLocal);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (myLocal.number);
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, Type type)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitType);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (GetStrFromType (type));
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitLabel);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (myLabel.number);
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitLabels);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
int nLabels = myLabels.Length;
|
||||
objFileWriter.Write (nLabels);
|
||||
for (int i = 0; i < nLabels; i ++) {
|
||||
objFileWriter.Write (myLabels[i].number);
|
||||
}
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method)
|
||||
{
|
||||
if (method == null) throw new ArgumentNullException ("method");
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitMethodInt);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (method.methName);
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, MethodInfo method)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitMethodExt);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (method.Name);
|
||||
objFileWriter.Write (GetStrFromType (method.ReflectedType));
|
||||
ParameterInfo[] parms = method.GetParameters ();
|
||||
int nArgs = parms.Length;
|
||||
objFileWriter.Write (nArgs);
|
||||
for (int i = 0; i < nArgs; i ++) {
|
||||
objFileWriter.Write (GetStrFromType (parms[i].ParameterType));
|
||||
}
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitCtor);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (GetStrFromType (ctor.ReflectedType));
|
||||
ParameterInfo[] parms = ctor.GetParameters ();
|
||||
int nArgs = parms.Length;
|
||||
objFileWriter.Write (nArgs);
|
||||
for (int i = 0; i < nArgs; i ++) {
|
||||
objFileWriter.Write (GetStrFromType (parms[i].ParameterType));
|
||||
}
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, double value)
|
||||
{
|
||||
if (opcode != OpCodes.Ldc_R8) {
|
||||
throw new Exception ("bad opcode " + opcode.ToString ());
|
||||
}
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitDouble);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (value);
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, float value)
|
||||
{
|
||||
if (opcode != OpCodes.Ldc_R4) {
|
||||
throw new Exception ("bad opcode " + opcode.ToString ());
|
||||
}
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitFloat);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (value);
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, int value)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitInteger);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (value);
|
||||
}
|
||||
|
||||
public void Emit (Token errorAt, OpCode opcode, string value)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.EmitString);
|
||||
WriteOpCode (errorAt, opcode);
|
||||
objFileWriter.Write (value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Declare that the target of a label is the next instruction.
|
||||
*/
|
||||
public void MarkLabel (ScriptMyLabel myLabel)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.MarkLabel);
|
||||
objFileWriter.Write (myLabel.number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write end-of-file marker to binary file.
|
||||
*/
|
||||
public static void TheEnd (BinaryWriter objFileWriter)
|
||||
{
|
||||
objFileWriter.Write ((byte)ScriptObjWriterCode.TheEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Take an object file created by ScriptObjWriter() and convert it to a series of dynamic methods.
|
||||
* @param sdTypes = script-defined types
|
||||
* @param objReader = where to read object file from (as written by ScriptObjWriter above).
|
||||
* @param scriptObjCode.EndMethod = called for each method defined at the end of the methods definition
|
||||
* @param objectTokens = write disassemble/decompile data (or null if not wanted)
|
||||
*/
|
||||
public static void CreateObjCode (Dictionary<string, TokenDeclSDType> sdTypes, BinaryReader objReader,
|
||||
ScriptObjCode scriptObjCode, ObjectTokens objectTokens)
|
||||
{
|
||||
Dictionary<string, DynamicMethod> methods = new Dictionary<string, DynamicMethod> ();
|
||||
DynamicMethod method = null;
|
||||
ILGenerator ilGen = null;
|
||||
Dictionary<int, Label> labels = new Dictionary<int, Label> ();
|
||||
Dictionary<int, LocalBuilder> locals = new Dictionary<int, LocalBuilder> ();
|
||||
Dictionary<int, string> labelNames = new Dictionary<int, string> ();
|
||||
Dictionary<int, string> localNames = new Dictionary<int, string> ();
|
||||
object[] ilGenArg = new object[1];
|
||||
int offset = 0;
|
||||
Dictionary<int, ScriptSrcLoc> srcLocs = null;
|
||||
string srcFile = "";
|
||||
int srcLine = 0;
|
||||
int srcPosn = 0;
|
||||
|
||||
while (true) {
|
||||
|
||||
/*
|
||||
* Get IL instruction offset at beginning of instruction.
|
||||
*/
|
||||
offset = 0;
|
||||
if ((ilGen != null) && (monoGetCurrentOffset != null)) {
|
||||
offset = (int)monoGetCurrentOffset.Invoke (null, ilGenArg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and decode next internal format code from input file (.xmrobj file).
|
||||
*/
|
||||
ScriptObjWriterCode code = (ScriptObjWriterCode)objReader.ReadByte ();
|
||||
switch (code) {
|
||||
|
||||
/*
|
||||
* Reached end-of-file so we are all done.
|
||||
*/
|
||||
case ScriptObjWriterCode.TheEnd: {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Beginning of method's contents.
|
||||
* Method must have already been declared via DclMethod
|
||||
* so all we need is its name to retrieve from methods[].
|
||||
*/
|
||||
case ScriptObjWriterCode.BegMethod: {
|
||||
string methName = objReader.ReadString ();
|
||||
|
||||
method = methods[methName];
|
||||
ilGen = method.GetILGenerator ();
|
||||
ilGenArg[0] = ilGen;
|
||||
|
||||
labels.Clear ();
|
||||
locals.Clear ();
|
||||
labelNames.Clear ();
|
||||
localNames.Clear ();
|
||||
|
||||
srcLocs = new Dictionary<int, ScriptSrcLoc> ();
|
||||
if (objectTokens != null) objectTokens.BegMethod (method);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* End of method's contents (ie, an OpCodes.Ret was probably just output).
|
||||
* Call the callback to tell it the method is complete, and it can do whatever
|
||||
* it wants with the method.
|
||||
*/
|
||||
case ScriptObjWriterCode.EndMethod: {
|
||||
ilGen = null;
|
||||
ilGenArg[0] = null;
|
||||
scriptObjCode.EndMethod (method, srcLocs);
|
||||
srcLocs = null;
|
||||
if (objectTokens != null) objectTokens.EndMethod ();
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Declare a label for branching to.
|
||||
*/
|
||||
case ScriptObjWriterCode.DclLabel: {
|
||||
int number = objReader.ReadInt32 ();
|
||||
string name = objReader.ReadString ();
|
||||
|
||||
labels.Add (number, ilGen.DefineLabel ());
|
||||
labelNames.Add (number, name + "_" + number.ToString ());
|
||||
if (objectTokens != null) objectTokens.DefineLabel (number, name);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Declare a local variable to store into.
|
||||
*/
|
||||
case ScriptObjWriterCode.DclLocal: {
|
||||
int number = objReader.ReadInt32 ();
|
||||
string name = objReader.ReadString ();
|
||||
string type = objReader.ReadString ();
|
||||
Type syType = GetTypeFromStr (sdTypes, type);
|
||||
|
||||
locals.Add (number, ilGen.DeclareLocal (syType));
|
||||
localNames.Add (number, name + "_" + number.ToString ());
|
||||
if (objectTokens != null) objectTokens.DefineLocal (number, name, type, syType);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Declare a method that will subsequently be defined.
|
||||
* We create the DynamicMethod object at this point in case there
|
||||
* are forward references from other method bodies.
|
||||
*/
|
||||
case ScriptObjWriterCode.DclMethod: {
|
||||
string methName = objReader.ReadString ();
|
||||
Type retType = GetTypeFromStr (sdTypes, objReader.ReadString ());
|
||||
int nArgs = objReader.ReadInt32 ();
|
||||
|
||||
Type[] argTypes = new Type[nArgs];
|
||||
string[] argNames = new string[nArgs];
|
||||
for (int i = 0; i < nArgs; i ++) {
|
||||
argTypes[i] = GetTypeFromStr (sdTypes, objReader.ReadString ());
|
||||
argNames[i] = objReader.ReadString ();
|
||||
}
|
||||
methods.Add (methName, new DynamicMethod (methName, retType, argTypes));
|
||||
if (objectTokens != null) objectTokens.DefineMethod (methName, retType, argTypes, argNames);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark a previously declared label at this spot.
|
||||
*/
|
||||
case ScriptObjWriterCode.MarkLabel: {
|
||||
int number = objReader.ReadInt32 ();
|
||||
|
||||
ilGen.MarkLabel (labels[number]);
|
||||
|
||||
if (objectTokens != null) objectTokens.MarkLabel (offset, number);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try/Catch blocks.
|
||||
*/
|
||||
case ScriptObjWriterCode.BegExcBlk: {
|
||||
ilGen.BeginExceptionBlock ();
|
||||
if (objectTokens != null) objectTokens.BegExcBlk (offset);
|
||||
break;
|
||||
}
|
||||
|
||||
case ScriptObjWriterCode.BegCatBlk: {
|
||||
Type excType = GetTypeFromStr (sdTypes, objReader.ReadString ());
|
||||
ilGen.BeginCatchBlock (excType);
|
||||
if (objectTokens != null) objectTokens.BegCatBlk (offset, excType);
|
||||
break;
|
||||
}
|
||||
|
||||
case ScriptObjWriterCode.BegFinBlk: {
|
||||
ilGen.BeginFinallyBlock ();
|
||||
if (objectTokens != null) objectTokens.BegFinBlk (offset);
|
||||
break;
|
||||
}
|
||||
|
||||
case ScriptObjWriterCode.EndExcBlk: {
|
||||
ilGen.EndExceptionBlock ();
|
||||
if (objectTokens != null) objectTokens.EndExcBlk (offset);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an opcode with no operand.
|
||||
*/
|
||||
case ScriptObjWriterCode.EmitNull: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitNull (offset, opCode);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an opcode with a FieldInfo operand.
|
||||
*/
|
||||
case ScriptObjWriterCode.EmitField: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
Type reflectedType = GetTypeFromStr (sdTypes, objReader.ReadString ());
|
||||
string fieldName = objReader.ReadString ();
|
||||
|
||||
FieldInfo field = reflectedType.GetField (fieldName);
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, field);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitField (offset, opCode, field);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an opcode with a LocalBuilder operand.
|
||||
*/
|
||||
case ScriptObjWriterCode.EmitLocal: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
int number = objReader.ReadInt32 ();
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, locals[number]);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitLocal (offset, opCode, number);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an opcode with a Type operand.
|
||||
*/
|
||||
case ScriptObjWriterCode.EmitType: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
string name = objReader.ReadString ();
|
||||
Type type = GetTypeFromStr (sdTypes, name);
|
||||
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, type);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitType (offset, opCode, type);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an opcode with a Label operand.
|
||||
*/
|
||||
case ScriptObjWriterCode.EmitLabel: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
int number = objReader.ReadInt32 ();
|
||||
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, labels[number]);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitLabel (offset, opCode, number);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an opcode with a Label array operand.
|
||||
*/
|
||||
case ScriptObjWriterCode.EmitLabels: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
int nLabels = objReader.ReadInt32 ();
|
||||
Label[] lbls = new Label[nLabels];
|
||||
int[] nums = new int[nLabels];
|
||||
for (int i = 0; i < nLabels; i ++) {
|
||||
nums[i] = objReader.ReadInt32 ();
|
||||
lbls[i] = labels[nums[i]];
|
||||
}
|
||||
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, lbls);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitLabels (offset, opCode, nums);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an opcode with a MethodInfo operand (such as a call) of an external function.
|
||||
*/
|
||||
case ScriptObjWriterCode.EmitMethodExt: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
string methName = objReader.ReadString ();
|
||||
Type methType = GetTypeFromStr (sdTypes, objReader.ReadString ());
|
||||
int nArgs = objReader.ReadInt32 ();
|
||||
|
||||
Type[] argTypes = new Type[nArgs];
|
||||
for (int i = 0; i < nArgs; i ++) {
|
||||
argTypes[i] = GetTypeFromStr (sdTypes, objReader.ReadString ());
|
||||
}
|
||||
MethodInfo methInfo = methType.GetMethod (methName, argTypes);
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, methInfo);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitMethod (offset, opCode, methInfo);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an opcode with a MethodInfo operand of an internal function
|
||||
* (previously declared via DclMethod).
|
||||
*/
|
||||
case ScriptObjWriterCode.EmitMethodInt: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
string methName = objReader.ReadString ();
|
||||
|
||||
MethodInfo methInfo = methods[methName];
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, methInfo);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitMethod (offset, opCode, methInfo);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an opcode with a ConstructorInfo operand.
|
||||
*/
|
||||
case ScriptObjWriterCode.EmitCtor: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
Type ctorType = GetTypeFromStr (sdTypes, objReader.ReadString ());
|
||||
int nArgs = objReader.ReadInt32 ();
|
||||
Type[] argTypes = new Type[nArgs];
|
||||
for (int i = 0; i < nArgs; i ++) {
|
||||
argTypes[i] = GetTypeFromStr (sdTypes, objReader.ReadString ());
|
||||
}
|
||||
|
||||
ConstructorInfo ctorInfo = ctorType.GetConstructor (argTypes);
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, ctorInfo);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitCtor (offset, opCode, ctorInfo);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an opcode with a constant operand of various types.
|
||||
*/
|
||||
case ScriptObjWriterCode.EmitDouble: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
double value = objReader.ReadDouble ();
|
||||
|
||||
if (opCode != OpCodes.Ldc_R8) {
|
||||
throw new Exception ("bad opcode " + opCode.ToString ());
|
||||
}
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, value);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitDouble (offset, opCode, value);
|
||||
break;
|
||||
}
|
||||
|
||||
case ScriptObjWriterCode.EmitFloat: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
float value = objReader.ReadSingle ();
|
||||
|
||||
if (opCode != OpCodes.Ldc_R4) {
|
||||
throw new Exception ("bad opcode " + opCode.ToString ());
|
||||
}
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, value);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitFloat (offset, opCode, value);
|
||||
break;
|
||||
}
|
||||
|
||||
case ScriptObjWriterCode.EmitInteger: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
int value = objReader.ReadInt32 ();
|
||||
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
|
||||
if (opCode == OpCodes.Ldc_I4) {
|
||||
if ((value >= -1) && (value <= 8)) {
|
||||
opCode = opCodesLdcI4M1P8[value+1];
|
||||
ilGen.Emit (opCode);
|
||||
if (objectTokens != null) objectTokens.EmitNull (offset, opCode);
|
||||
break;
|
||||
}
|
||||
if ((value >= 0) && (value <= 127)) {
|
||||
opCode = OpCodes.Ldc_I4_S;
|
||||
ilGen.Emit (OpCodes.Ldc_I4_S, (sbyte)value);
|
||||
goto pemitint;
|
||||
}
|
||||
}
|
||||
|
||||
ilGen.Emit (opCode, value);
|
||||
pemitint:
|
||||
if (objectTokens != null) objectTokens.EmitInteger (offset, opCode, value);
|
||||
break;
|
||||
}
|
||||
|
||||
case ScriptObjWriterCode.EmitString: {
|
||||
OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
|
||||
string value = objReader.ReadString ();
|
||||
|
||||
SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
|
||||
ilGen.Emit (opCode, value);
|
||||
|
||||
if (objectTokens != null) objectTokens.EmitString (offset, opCode, value);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Who knows what?
|
||||
*/
|
||||
default: throw new Exception ("bad ScriptObjWriterCode " + ((byte)code).ToString ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate array to quickly translate OpCode.Value to full OpCode struct.
|
||||
*/
|
||||
private static Dictionary<short, OpCode> PopulateOpCodes ()
|
||||
{
|
||||
Dictionary<short, OpCode> opCodeDict = new Dictionary<short, OpCode> ();
|
||||
FieldInfo[] fields = typeof (OpCodes).GetFields ();
|
||||
for (int i = 0; i < fields.Length; i ++) {
|
||||
OpCode opcode = (OpCode)fields[i].GetValue (null);
|
||||
opCodeDict.Add (opcode.Value, opcode);
|
||||
}
|
||||
return opCodeDict;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write opcode out to file.
|
||||
*/
|
||||
private void WriteOpCode (Token errorAt, OpCode opcode)
|
||||
{
|
||||
if (errorAt == null) {
|
||||
objFileWriter.Write ("");
|
||||
objFileWriter.Write (lastErrorAtLine);
|
||||
objFileWriter.Write (lastErrorAtPosn);
|
||||
} else {
|
||||
if (errorAt.file != lastErrorAtFile) {
|
||||
objFileWriter.Write (errorAt.file);
|
||||
lastErrorAtFile = errorAt.file;
|
||||
} else {
|
||||
objFileWriter.Write ("");
|
||||
}
|
||||
objFileWriter.Write (errorAt.line);
|
||||
objFileWriter.Write (errorAt.posn);
|
||||
lastErrorAtLine = errorAt.line;
|
||||
lastErrorAtPosn = errorAt.posn;
|
||||
}
|
||||
objFileWriter.Write (opcode.Value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read opcode in from file.
|
||||
*/
|
||||
private static OpCode ReadOpCode (BinaryReader objReader, ref string srcFile, ref int srcLine, ref int srcPosn)
|
||||
{
|
||||
string f = objReader.ReadString ();
|
||||
if (f != "") srcFile = f;
|
||||
srcLine = objReader.ReadInt32 ();
|
||||
srcPosn = objReader.ReadInt32 ();
|
||||
|
||||
short value = objReader.ReadInt16 ();
|
||||
return opCodes[value];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save an IL_offset -> source location translation entry
|
||||
* @param srcLocs = saved entries for the current function
|
||||
* @param offset = offset in IL object code for next instruction
|
||||
* @param src{File,Line,Posn} = location in source file corresponding to opcode
|
||||
* @returns with entry added to srcLocs
|
||||
*/
|
||||
private static void SaveSrcLoc (Dictionary<int, ScriptSrcLoc> srcLocs, int offset, string srcFile, int srcLine, int srcPosn)
|
||||
{
|
||||
ScriptSrcLoc srcLoc = new ScriptSrcLoc ();
|
||||
srcLoc.file = srcFile;
|
||||
srcLoc.line = srcLine;
|
||||
srcLoc.posn = srcPosn;
|
||||
srcLocs[offset] = srcLoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create type<->string conversions.
|
||||
* Using Type.AssemblyQualifiedName is horribly inefficient
|
||||
* and all our types should be known.
|
||||
*/
|
||||
private static Dictionary<string, Type> PopulateS2T ()
|
||||
{
|
||||
Dictionary<string, Type> s2t = new Dictionary<string, Type> ();
|
||||
|
||||
s2t.Add ("badcallx", typeof (ScriptBadCallNoException));
|
||||
s2t.Add ("binopstr", typeof (BinOpStr));
|
||||
s2t.Add ("bool", typeof (bool));
|
||||
s2t.Add ("char", typeof (char));
|
||||
s2t.Add ("delegate", typeof (Delegate));
|
||||
s2t.Add ("delarr[]", typeof (Delegate[]));
|
||||
s2t.Add ("double", typeof (double));
|
||||
s2t.Add ("exceptn", typeof (Exception));
|
||||
s2t.Add ("float", typeof (float));
|
||||
s2t.Add ("htlist", typeof (HeapTrackerList));
|
||||
s2t.Add ("htobject", typeof (HeapTrackerObject));
|
||||
s2t.Add ("htstring", typeof (HeapTrackerString));
|
||||
s2t.Add ("inlfunc", typeof (CompValuInline));
|
||||
s2t.Add ("int", typeof (int));
|
||||
s2t.Add ("int*", typeof (int).MakeByRefType ());
|
||||
s2t.Add ("intrlokd", typeof (System.Threading.Interlocked));
|
||||
s2t.Add ("lslfloat", typeof (LSL_Float));
|
||||
s2t.Add ("lslint", typeof (LSL_Integer));
|
||||
s2t.Add ("lsllist", typeof (LSL_List));
|
||||
s2t.Add ("lslrot", typeof (LSL_Rotation));
|
||||
s2t.Add ("lslstr", typeof (LSL_String));
|
||||
s2t.Add ("lslvec", typeof (LSL_Vector));
|
||||
s2t.Add ("math", typeof (Math));
|
||||
s2t.Add ("midround", typeof (MidpointRounding));
|
||||
s2t.Add ("object", typeof (object));
|
||||
s2t.Add ("object*", typeof (object).MakeByRefType ());
|
||||
s2t.Add ("object[]", typeof (object[]));
|
||||
s2t.Add ("scrbase", typeof (ScriptBaseClass));
|
||||
s2t.Add ("scrcode", typeof (ScriptCodeGen));
|
||||
s2t.Add ("sdtclobj", typeof (XMRSDTypeClObj));
|
||||
s2t.Add ("string", typeof (string));
|
||||
s2t.Add ("typecast", typeof (TypeCast));
|
||||
s2t.Add ("undstatx", typeof (ScriptUndefinedStateException));
|
||||
s2t.Add ("void", typeof (void));
|
||||
s2t.Add ("xmrarray", typeof (XMR_Array));
|
||||
s2t.Add ("xmrinst", typeof (XMRInstAbstract));
|
||||
|
||||
return s2t;
|
||||
}
|
||||
|
||||
private static Dictionary<Type, string> PopulateT2S ()
|
||||
{
|
||||
Dictionary<string, Type> s2t = PopulateS2T ();
|
||||
Dictionary<Type, string> t2s = new Dictionary<Type, string> ();
|
||||
foreach (KeyValuePair<string, Type> kvp in s2t) {
|
||||
t2s.Add (kvp.Value, kvp.Key);
|
||||
}
|
||||
return t2s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add to list of internally recognized types.
|
||||
*/
|
||||
public static void DefineInternalType (string name, Type type)
|
||||
{
|
||||
if (!string2Type.ContainsKey(name))
|
||||
{
|
||||
string2Type.Add (name, type);
|
||||
type2String.Add (type, name);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetStrFromType (Type t)
|
||||
{
|
||||
string s = GetStrFromTypeWork (t);
|
||||
return s;
|
||||
}
|
||||
private string GetStrFromTypeWork (Type t)
|
||||
{
|
||||
string s;
|
||||
|
||||
// internal fixed types like int and xmrarray etc
|
||||
if (type2String.TryGetValue (t, out s)) return s;
|
||||
|
||||
// script-defined types
|
||||
if (sdTypesRev.TryGetValue (t, out s)) return "sdt$" + s;
|
||||
|
||||
// inline function types
|
||||
s = TokenDeclSDTypeDelegate.TryGetInlineName (t);
|
||||
if (s != null) return s;
|
||||
|
||||
// last resort
|
||||
return t.AssemblyQualifiedName;
|
||||
}
|
||||
|
||||
private static Type GetTypeFromStr (Dictionary<string, TokenDeclSDType> sdTypes, string s)
|
||||
{
|
||||
Type t;
|
||||
|
||||
// internal fixed types like int and xmrarray etc
|
||||
if (string2Type.TryGetValue (s, out t)) return t;
|
||||
|
||||
// script-defined types
|
||||
if (s.StartsWith ("sdt$")) return sdTypes[s.Substring(4)].GetSysType ();
|
||||
|
||||
// inline function types
|
||||
t = TokenDeclSDTypeDelegate.TryGetInlineSysType (s);
|
||||
if (t != null) return t;
|
||||
|
||||
// last resort
|
||||
return Type.GetType (s, true);
|
||||
}
|
||||
}
|
||||
|
||||
public class ScriptSrcLoc {
|
||||
public string file;
|
||||
public int line;
|
||||
public int posn;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,819 @@
|
|||
/*
|
||||
* 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 OpenSim.Region.ScriptEngine.XMREngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief Generate script object code to perform type casting
|
||||
*/
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
|
||||
public class TypeCast {
|
||||
private delegate void CastDelegate (IScriptCodeGen scg, Token errorAt);
|
||||
|
||||
private static ConstructorInfo floatConstructorStringInfo = typeof (LSL_Float).GetConstructor (new Type[] { typeof (string) });
|
||||
private static ConstructorInfo integerConstructorStringInfo = typeof (LSL_Integer).GetConstructor (new Type[] { typeof (string) });
|
||||
private static ConstructorInfo lslFloatConstructorInfo = typeof (LSL_Float).GetConstructor (new Type[] { typeof (double) });
|
||||
private static ConstructorInfo lslIntegerConstructorInfo = typeof (LSL_Integer).GetConstructor (new Type[] { typeof (int) });
|
||||
private static ConstructorInfo lslStringConstructorInfo = typeof (LSL_String).GetConstructor (new Type[] { typeof (string) });
|
||||
private static ConstructorInfo rotationConstrucorStringInfo = typeof (LSL_Rotation).GetConstructor (new Type[] { typeof (string) });
|
||||
private static ConstructorInfo vectorConstrucorStringInfo = typeof (LSL_Vector).GetConstructor (new Type[] { typeof (string) });
|
||||
private static FieldInfo lslFloatValueFieldInfo = typeof (LSL_Float).GetField ("value");
|
||||
private static FieldInfo lslIntegerValueFieldInfo = typeof (LSL_Integer).GetField ("value");
|
||||
private static FieldInfo lslStringValueFieldInfo = typeof (LSL_String).GetField ("m_string");
|
||||
private static FieldInfo sdtcITableFieldInfo = typeof (XMRSDTypeClObj).GetField ("sdtcITable");
|
||||
private static MethodInfo boolToListMethodInfo = typeof (TypeCast).GetMethod ("BoolToList", new Type[] { typeof (bool) });
|
||||
private static MethodInfo boolToStringMethodInfo = typeof (TypeCast).GetMethod ("BoolToString", new Type[] { typeof (bool) });
|
||||
private static MethodInfo charToStringMethodInfo = typeof (TypeCast).GetMethod ("CharToString", new Type[] { typeof (char) });
|
||||
private static MethodInfo excToStringMethodInfo = typeof (TypeCast).GetMethod ("ExceptionToString", new Type[] { typeof (Exception), typeof (XMRInstAbstract) });
|
||||
private static MethodInfo floatToStringMethodInfo = typeof (TypeCast).GetMethod ("FloatToString", new Type[] { typeof (double) });
|
||||
private static MethodInfo intToStringMethodInfo = typeof (TypeCast).GetMethod ("IntegerToString", new Type[] { typeof (int) });
|
||||
private static MethodInfo keyToBoolMethodInfo = typeof (TypeCast).GetMethod ("KeyToBool", new Type[] { typeof (string) });
|
||||
private static MethodInfo listToBoolMethodInfo = typeof (TypeCast).GetMethod ("ListToBool", new Type[] { typeof (LSL_List) });
|
||||
private static MethodInfo listToStringMethodInfo = typeof (TypeCast).GetMethod ("ListToString", new Type[] { typeof (LSL_List) });
|
||||
private static MethodInfo objectToFloatMethodInfo = typeof (TypeCast).GetMethod ("ObjectToFloat", new Type[] { typeof (object) });
|
||||
private static MethodInfo objectToIntegerMethodInfo = typeof (TypeCast).GetMethod ("ObjectToInteger", new Type[] { typeof (object) });
|
||||
private static MethodInfo objectToListMethodInfo = typeof (TypeCast).GetMethod ("ObjectToList", new Type[] { typeof (object) });
|
||||
private static MethodInfo objectToRotationMethodInfo = typeof (TypeCast).GetMethod ("ObjectToRotation", new Type[] { typeof (object) });
|
||||
private static MethodInfo objectToStringMethodInfo = typeof (TypeCast).GetMethod ("ObjectToString", new Type[] { typeof (object) });
|
||||
private static MethodInfo objectToVectorMethodInfo = typeof (TypeCast).GetMethod ("ObjectToVector", new Type[] { typeof (object) });
|
||||
private static MethodInfo rotationToBoolMethodInfo = typeof (TypeCast).GetMethod ("RotationToBool", new Type[] { typeof (LSL_Rotation) });
|
||||
private static MethodInfo rotationToStringMethodInfo = typeof (TypeCast).GetMethod ("RotationToString", new Type[] { typeof (LSL_Rotation) });
|
||||
private static MethodInfo stringToBoolMethodInfo = typeof (TypeCast).GetMethod ("StringToBool", new Type[] { typeof (string) });
|
||||
private static MethodInfo vectorToBoolMethodInfo = typeof (TypeCast).GetMethod ("VectorToBool", new Type[] { typeof (LSL_Vector) });
|
||||
private static MethodInfo vectorToStringMethodInfo = typeof (TypeCast).GetMethod ("VectorToString", new Type[] { typeof (LSL_Vector) });
|
||||
private static MethodInfo sdTypeClassCastClass2ClassMethodInfo = typeof (XMRSDTypeClObj).GetMethod ("CastClass2Class", new Type[] { typeof (object), typeof (int) });
|
||||
private static MethodInfo sdTypeClassCastIFace2ClassMethodInfo = typeof (XMRSDTypeClObj).GetMethod ("CastIFace2Class", new Type[] { typeof (Delegate[]), typeof (int) });
|
||||
private static MethodInfo sdTypeClassCastObj2IFaceMethodInfo = typeof (XMRSDTypeClObj).GetMethod ("CastObj2IFace", new Type[] { typeof (object), typeof (string) });
|
||||
private static MethodInfo charToListMethodInfo = typeof (TypeCast).GetMethod ("CharToList", new Type[] { typeof (char) });
|
||||
private static MethodInfo excToListMethodInfo = typeof (TypeCast).GetMethod ("ExcToList", new Type[] { typeof (Exception) });
|
||||
private static MethodInfo vectorToListMethodInfo = typeof (TypeCast).GetMethod ("VectorToList", new Type[] { typeof (LSL_Vector) });
|
||||
private static MethodInfo floatToListMethodInfo = typeof (TypeCast).GetMethod ("FloatToList", new Type[] { typeof (double) });
|
||||
private static MethodInfo integerToListMethodInfo = typeof (TypeCast).GetMethod ("IntegerToList", new Type[] { typeof (int) });
|
||||
private static MethodInfo rotationToListMethodInfo = typeof (TypeCast).GetMethod ("RotationToList", new Type[] { typeof (LSL_Rotation) });
|
||||
private static MethodInfo stringToListMethodInfo = typeof (TypeCast).GetMethod ("StringToList", new Type[] { typeof (string) });
|
||||
|
||||
/*
|
||||
* List of all allowed type casts and how to perform the casting.
|
||||
*/
|
||||
private static Dictionary<string, CastDelegate> legalTypeCasts = CreateLegalTypeCasts ();
|
||||
|
||||
/**
|
||||
* @brief create a dictionary of legal type casts.
|
||||
* Defines what EXPLICIT type casts are allowed in addition to the IMPLICIT ones.
|
||||
* Key is of the form <oldtype> <newtype> for IMPLICIT casting.
|
||||
* Key is of the form <oldtype>*<newtype> for EXPLICIT casting.
|
||||
* Value is a delegate that generates code to perform the type cast.
|
||||
*/
|
||||
private static Dictionary<string, CastDelegate> CreateLegalTypeCasts ()
|
||||
{
|
||||
Dictionary<string, CastDelegate> ltc = new Dictionary<string, CastDelegate> ();
|
||||
|
||||
// IMPLICIT type casts (a space is in middle of the key)
|
||||
// EXPLICIT type casts (an * is in middle of the key)
|
||||
// In general, only mark explicit if it might throw an exception
|
||||
ltc.Add ("array object", TypeCastArray2Object);
|
||||
ltc.Add ("bool float", TypeCastBool2Float);
|
||||
ltc.Add ("bool integer", TypeCastBool2Integer);
|
||||
ltc.Add ("bool list", TypeCastBool2List);
|
||||
ltc.Add ("bool object", TypeCastBool2Object);
|
||||
ltc.Add ("bool string", TypeCastBool2String);
|
||||
ltc.Add ("char integer", TypeCastChar2Integer);
|
||||
ltc.Add ("char list", TypeCastChar2List);
|
||||
ltc.Add ("char object", TypeCastChar2Object);
|
||||
ltc.Add ("char string", TypeCastChar2String);
|
||||
ltc.Add ("exception list", TypeCastExc2List);
|
||||
ltc.Add ("exception object", TypeCastExc2Object);
|
||||
ltc.Add ("exception string", TypeCastExc2String);
|
||||
ltc.Add ("float bool", TypeCastFloat2Bool);
|
||||
ltc.Add ("float integer", TypeCastFloat2Integer);
|
||||
ltc.Add ("float list", TypeCastFloat2List);
|
||||
ltc.Add ("float object", TypeCastFloat2Object);
|
||||
ltc.Add ("float string", TypeCastFloat2String);
|
||||
ltc.Add ("integer bool", TypeCastInteger2Bool);
|
||||
ltc.Add ("integer char", TypeCastInteger2Char);
|
||||
ltc.Add ("integer float", TypeCastInteger2Float);
|
||||
ltc.Add ("integer list", TypeCastInteger2List);
|
||||
ltc.Add ("integer object", TypeCastInteger2Object);
|
||||
ltc.Add ("integer string", TypeCastInteger2String);
|
||||
ltc.Add ("list bool", TypeCastList2Bool);
|
||||
ltc.Add ("list object", TypeCastList2Object);
|
||||
ltc.Add ("list string", TypeCastList2String);
|
||||
ltc.Add ("object*array", TypeCastObject2Array);
|
||||
ltc.Add ("object*bool", TypeCastObject2Bool);
|
||||
ltc.Add ("object*char", TypeCastObject2Char);
|
||||
ltc.Add ("object*exception", TypeCastObject2Exc);
|
||||
ltc.Add ("object*float", TypeCastObject2Float);
|
||||
ltc.Add ("object*integer", TypeCastObject2Integer);
|
||||
ltc.Add ("object*list", TypeCastObject2List);
|
||||
ltc.Add ("object*rotation", TypeCastObject2Rotation);
|
||||
ltc.Add ("object string", TypeCastObject2String);
|
||||
ltc.Add ("object*vector", TypeCastObject2Vector);
|
||||
ltc.Add ("rotation bool", TypeCastRotation2Bool);
|
||||
ltc.Add ("rotation list", TypeCastRotation2List);
|
||||
ltc.Add ("rotation object", TypeCastRotation2Object);
|
||||
ltc.Add ("rotation string", TypeCastRotation2String);
|
||||
ltc.Add ("string bool", TypeCastString2Bool);
|
||||
ltc.Add ("string float", TypeCastString2Float);
|
||||
ltc.Add ("string integer", TypeCastString2Integer);
|
||||
ltc.Add ("string list", TypeCastString2List);
|
||||
ltc.Add ("string object", TypeCastString2Object);
|
||||
ltc.Add ("string rotation", TypeCastString2Rotation);
|
||||
ltc.Add ("string vector", TypeCastString2Vector);
|
||||
ltc.Add ("vector bool", TypeCastVector2Bool);
|
||||
ltc.Add ("vector list", TypeCastVector2List);
|
||||
ltc.Add ("vector object", TypeCastVector2Object);
|
||||
ltc.Add ("vector string", TypeCastVector2String);
|
||||
|
||||
return ltc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief See if the given type can be cast to the other implicitly.
|
||||
* @param dstType = type being cast to
|
||||
* @param srcType = type being cast from
|
||||
* @returns false: implicit cast not allowed
|
||||
* true: implicit cast allowed
|
||||
*/
|
||||
public static bool IsAssignableFrom (TokenType dstType, TokenType srcType)
|
||||
{
|
||||
/*
|
||||
* Do a 'dry run' of the casting operation, discarding any emits and not printing any errors.
|
||||
* But if the casting tries to print error(s), return false.
|
||||
* Otherwise assume the cast is allowed and return true.
|
||||
*/
|
||||
SCGIAF scg = new SCGIAF ();
|
||||
scg.ok = true;
|
||||
scg._ilGen = migiaf;
|
||||
CastTopOfStack (scg, null, srcType, dstType, false);
|
||||
return scg.ok;
|
||||
}
|
||||
|
||||
private struct SCGIAF : IScriptCodeGen {
|
||||
public bool ok;
|
||||
public ScriptMyILGen _ilGen;
|
||||
|
||||
// IScriptCodeGen
|
||||
public ScriptMyILGen ilGen { get { return _ilGen; } }
|
||||
public void ErrorMsg (Token token, string message) { ok = false; }
|
||||
public void PushDefaultValue (TokenType type) { }
|
||||
public void PushXMRInst () { }
|
||||
}
|
||||
|
||||
private static readonly MIGIAF migiaf = new MIGIAF ();
|
||||
private struct MIGIAF : ScriptMyILGen {
|
||||
// ScriptMyILGen
|
||||
public string methName { get { return null; } }
|
||||
public ScriptMyLocal DeclareLocal (Type type, string name) { return null; }
|
||||
public ScriptMyLabel DefineLabel (string name) { return null; }
|
||||
public void BeginExceptionBlock () { }
|
||||
public void BeginCatchBlock (Type excType) { }
|
||||
public void BeginFinallyBlock () { }
|
||||
public void EndExceptionBlock () { }
|
||||
public void Emit (Token errorAt, OpCode opcode) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, FieldInfo field) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, Type type) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, MethodInfo method) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, double value) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, float value) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, int value) { }
|
||||
public void Emit (Token errorAt, OpCode opcode, string value) { }
|
||||
public void MarkLabel (ScriptMyLabel myLabel) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Emit code that converts the top stack item from 'oldType' to 'newType'
|
||||
* @param scg = what script we are compiling
|
||||
* @param errorAt = token used for source location for error messages
|
||||
* @param oldType = type of item currently on the stack
|
||||
* @param newType = type to convert it to
|
||||
* @param explicitAllowed = false: only consider implicit casts
|
||||
* true: consider both implicit and explicit casts
|
||||
* @returns with code emitted for conversion (or error message output if not allowed, and stack left unchanged)
|
||||
*/
|
||||
public static void CastTopOfStack (IScriptCodeGen scg, Token errorAt, TokenType oldType, TokenType newType, bool explicitAllowed)
|
||||
{
|
||||
CastDelegate castDelegate;
|
||||
string oldString = oldType.ToString ();
|
||||
string newString = newType.ToString ();
|
||||
|
||||
/*
|
||||
* 'key' -> 'bool' is the only time we care about key being different than string.
|
||||
*/
|
||||
if ((oldString == "key") && (newString == "bool")) {
|
||||
LSLUnwrap (scg, errorAt, oldType);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, keyToBoolMethodInfo);
|
||||
LSLWrap (scg, errorAt, newType);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Treat key and string as same type for all other type casts.
|
||||
*/
|
||||
if (oldString == "key") oldString = "string";
|
||||
if (newString == "key") newString = "string";
|
||||
|
||||
/*
|
||||
* If the types are the same, there is no conceptual casting needed.
|
||||
* However, there may be wraping/unwraping to/from the LSL wrappers.
|
||||
*/
|
||||
if (oldString == newString) {
|
||||
if (oldType.ToLSLWrapType () != newType.ToLSLWrapType ()) {
|
||||
LSLUnwrap (scg, errorAt, oldType);
|
||||
LSLWrap (scg, errorAt, newType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Script-defined classes can be cast up and down the tree.
|
||||
*/
|
||||
if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeSDTypeClass)) {
|
||||
TokenDeclSDTypeClass oldSDTC = ((TokenTypeSDTypeClass)oldType).decl;
|
||||
TokenDeclSDTypeClass newSDTC = ((TokenTypeSDTypeClass)newType).decl;
|
||||
|
||||
// implicit cast allowed from leaf toward root
|
||||
for (TokenDeclSDTypeClass sdtc = oldSDTC; sdtc != null; sdtc = sdtc.extends) {
|
||||
if (sdtc == newSDTC) return;
|
||||
}
|
||||
|
||||
// explicit cast allowed from root toward leaf
|
||||
for (TokenDeclSDTypeClass sdtc = newSDTC; sdtc != null; sdtc = sdtc.extends) {
|
||||
if (sdtc == oldSDTC) {
|
||||
ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, newSDTC.sdTypeIndex);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastClass2ClassMethodInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// not on same branch
|
||||
goto illcast;
|
||||
}
|
||||
|
||||
/*
|
||||
* One script-defined interface type cannot be cast to another script-defined interface type,
|
||||
* unless the old interface declares that it implements the new interface. That proves that
|
||||
* the underlying object, no matter what type, implements the new interface.
|
||||
*/
|
||||
if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeSDTypeInterface)) {
|
||||
TokenDeclSDTypeInterface oldDecl = ((TokenTypeSDTypeInterface)oldType).decl;
|
||||
TokenDeclSDTypeInterface newDecl = ((TokenTypeSDTypeInterface)newType).decl;
|
||||
if (!oldDecl.Implements (newDecl)) goto illcast;
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldstr, newType.ToString ());
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastObj2IFaceMethodInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* A script-defined class type can be implicitly cast to a script-defined interface type that it
|
||||
* implements. The result is an array of delegates that give the class's implementation of the
|
||||
* various methods defined by the interface.
|
||||
*/
|
||||
if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeSDTypeInterface)) {
|
||||
TokenDeclSDTypeClass oldSDTC = ((TokenTypeSDTypeClass)oldType).decl;
|
||||
int intfIndex;
|
||||
if (!oldSDTC.intfIndices.TryGetValue (newType.ToString (), out intfIndex)) goto illcast;
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldfld, sdtcITableFieldInfo);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, intfIndex);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (Delegate[]));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* A script-defined interface type can be explicitly cast to a script-defined class type by
|
||||
* extracting the Target property from element 0 of the delegate array that is the interface
|
||||
* object and making sure it casts to the correct script-defined class type.
|
||||
*
|
||||
* But then only if the class type implements the interface type.
|
||||
*/
|
||||
if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeSDTypeClass)) {
|
||||
TokenTypeSDTypeInterface oldSDTI = (TokenTypeSDTypeInterface)oldType;
|
||||
TokenTypeSDTypeClass newSDTC = (TokenTypeSDTypeClass) newType;
|
||||
|
||||
if (!newSDTC.decl.CanCastToIntf (oldSDTI.decl)) goto illcast;
|
||||
|
||||
ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, newSDTC.decl.sdTypeIndex);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastIFace2ClassMethodInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* A script-defined interface type can be implicitly cast to object.
|
||||
*/
|
||||
if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeObject)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* An object can be explicitly cast to a script-defined interface.
|
||||
*/
|
||||
if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeInterface)) {
|
||||
ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldstr, newString);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastObj2IFaceMethodInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cast to void is always allowed, such as discarding value from 'i++' or function return value.
|
||||
*/
|
||||
if (newType is TokenTypeVoid) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Pop);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cast from undef to object or script-defined type is always allowed.
|
||||
*/
|
||||
if ((oldType is TokenTypeUndef) &&
|
||||
((newType is TokenTypeObject) ||
|
||||
(newType is TokenTypeSDTypeClass) ||
|
||||
(newType is TokenTypeSDTypeInterface))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Script-defined classes can be implicitly cast to objects.
|
||||
*/
|
||||
if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeObject)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Script-defined classes can be explicitly cast from objects and other script-defined classes.
|
||||
* Note that we must manually check that it is the correct SDTypeClass however because as far as
|
||||
* mono is concerned, all SDTypeClass's are the same.
|
||||
*/
|
||||
if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeClass)) {
|
||||
ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, ((TokenTypeSDTypeClass)newType).decl.sdTypeIndex);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastClass2ClassMethodInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delegates can be implicitly cast to/from objects.
|
||||
*/
|
||||
if ((oldType is TokenTypeSDTypeDelegate) && (newType is TokenTypeObject)) {
|
||||
return;
|
||||
}
|
||||
if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeDelegate)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Castclass, newType.ToSysType ());
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some actual conversion is needed, see if it is in table of legal casts.
|
||||
*/
|
||||
string key = oldString + " " + newString;
|
||||
if (!legalTypeCasts.TryGetValue (key, out castDelegate)) {
|
||||
key = oldString + "*" + newString;
|
||||
if (!legalTypeCasts.TryGetValue (key, out castDelegate)) goto illcast;
|
||||
ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, output cast. But make sure it is in native form without any LSL wrapping
|
||||
* before passing to our casting routine. Then if caller is expecting an LSL-
|
||||
* wrapped value on the stack upon return, wrap it up after our casting.
|
||||
*/
|
||||
LSLUnwrap (scg, errorAt, oldType);
|
||||
castDelegate (scg, errorAt);
|
||||
LSLWrap (scg, errorAt, newType);
|
||||
return;
|
||||
|
||||
illcast:
|
||||
scg.ErrorMsg (errorAt, "illegal to cast from " + oldString + " to " + newString);
|
||||
if (!(oldType is TokenTypeVoid)) scg.ilGen.Emit (errorAt, OpCodes.Pop);
|
||||
scg.PushDefaultValue (newType);
|
||||
}
|
||||
private static void ExplCheck (IScriptCodeGen scg, Token errorAt, bool explicitAllowed, string oldString, string newString)
|
||||
{
|
||||
if (!explicitAllowed) {
|
||||
scg.ErrorMsg (errorAt, "must explicitly cast from " + oldString + " to " + newString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If value on the stack is an LSL-style wrapped value, unwrap it.
|
||||
*/
|
||||
public static void LSLUnwrap (IScriptCodeGen scg, Token errorAt, TokenType type)
|
||||
{
|
||||
if (type.ToLSLWrapType () == typeof (LSL_Float)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslFloatValueFieldInfo);
|
||||
}
|
||||
if (type.ToLSLWrapType () == typeof (LSL_Integer)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslIntegerValueFieldInfo);
|
||||
}
|
||||
if (type.ToLSLWrapType () == typeof (LSL_String)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslStringValueFieldInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If caller wants the unwrapped value on stack wrapped LSL-style, wrap it.
|
||||
*/
|
||||
private static void LSLWrap (IScriptCodeGen scg, Token errorAt, TokenType type)
|
||||
{
|
||||
if (type.ToLSLWrapType () == typeof (LSL_Float)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslFloatConstructorInfo);
|
||||
}
|
||||
if (type.ToLSLWrapType () == typeof (LSL_Integer)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslIntegerConstructorInfo);
|
||||
}
|
||||
if (type.ToLSLWrapType () == typeof (LSL_String)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslStringConstructorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief These routines output code to perform casting.
|
||||
* They can assume there are no LSL wrapped values on input
|
||||
* and they should not output an LSL wrapped value.
|
||||
*/
|
||||
private static void TypeCastArray2Object (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
}
|
||||
private static void TypeCastBool2Float (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
if (typeof (double) == typeof (float)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Conv_R4);
|
||||
} else if (typeof (double) == typeof (double)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Conv_R8);
|
||||
} else {
|
||||
throw new Exception ("unknown type");
|
||||
}
|
||||
}
|
||||
private static void TypeCastBool2Integer (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
}
|
||||
private static void TypeCastBool2Object (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (bool));
|
||||
}
|
||||
private static void TypeCastChar2Integer (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
}
|
||||
private static void TypeCastChar2List (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, charToListMethodInfo);
|
||||
}
|
||||
private static void TypeCastChar2Object (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (char));
|
||||
}
|
||||
private static void TypeCastChar2String (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, charToStringMethodInfo);
|
||||
}
|
||||
private static void TypeCastExc2List (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, excToListMethodInfo);
|
||||
}
|
||||
private static void TypeCastExc2Object (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
}
|
||||
private static void TypeCastExc2String (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.PushXMRInst ();
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, excToStringMethodInfo);
|
||||
}
|
||||
private static void TypeCastFloat2Bool (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldc_R4, 0.0f);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ceq);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Xor);
|
||||
}
|
||||
private static void TypeCastFloat2Integer (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Conv_I4);
|
||||
}
|
||||
private static void TypeCastFloat2Object (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (double));
|
||||
}
|
||||
private static void TypeCastInteger2Bool (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ceq);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Xor);
|
||||
}
|
||||
private static void TypeCastInteger2Char (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
}
|
||||
private static void TypeCastInteger2Float (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
if (typeof (double) == typeof (float)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Conv_R4);
|
||||
} else if (typeof (double) == typeof (double)) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Conv_R8);
|
||||
} else {
|
||||
throw new Exception ("unknown type");
|
||||
}
|
||||
}
|
||||
private static void TypeCastInteger2Object (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (int));
|
||||
}
|
||||
private static void TypeCastList2Bool (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, listToBoolMethodInfo);
|
||||
}
|
||||
private static void TypeCastList2Object (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
if (typeof (LSL_List).IsValueType) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (LSL_List));
|
||||
}
|
||||
}
|
||||
private static void TypeCastObject2Array (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Castclass, typeof (XMR_Array));
|
||||
}
|
||||
private static void TypeCastObject2Bool (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Unbox_Any, typeof (bool));
|
||||
}
|
||||
private static void TypeCastObject2Char (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Unbox_Any, typeof (char));
|
||||
}
|
||||
private static void TypeCastObject2Exc (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Castclass, typeof (Exception));
|
||||
}
|
||||
private static void TypeCastObject2Float (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, objectToFloatMethodInfo);
|
||||
}
|
||||
private static void TypeCastObject2Integer (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, objectToIntegerMethodInfo);
|
||||
}
|
||||
private static void TypeCastObject2List (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
if (typeof (LSL_List).IsValueType) {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, objectToListMethodInfo);
|
||||
} else {
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Castclass, typeof (LSL_List));
|
||||
}
|
||||
}
|
||||
private static void TypeCastObject2Rotation (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, objectToRotationMethodInfo);
|
||||
}
|
||||
private static void TypeCastObject2Vector (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, objectToVectorMethodInfo);
|
||||
}
|
||||
private static void TypeCastRotation2Bool (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, rotationToBoolMethodInfo);
|
||||
}
|
||||
private static void TypeCastRotation2Object (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (LSL_Rotation));
|
||||
}
|
||||
private static void TypeCastString2Bool (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, stringToBoolMethodInfo);
|
||||
}
|
||||
private static void TypeCastString2Object (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
}
|
||||
private static void TypeCastString2Rotation (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Newobj, rotationConstrucorStringInfo);
|
||||
}
|
||||
private static void TypeCastString2Vector (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Newobj, vectorConstrucorStringInfo);
|
||||
}
|
||||
private static void TypeCastVector2Bool (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, vectorToBoolMethodInfo);
|
||||
}
|
||||
private static void TypeCastVector2List (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, vectorToListMethodInfo);
|
||||
}
|
||||
private static void TypeCastVector2Object (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (LSL_Vector));
|
||||
}
|
||||
private static void TypeCastBool2List (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, boolToListMethodInfo);
|
||||
}
|
||||
private static void TypeCastBool2String (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, boolToStringMethodInfo);
|
||||
}
|
||||
private static void TypeCastFloat2List (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, floatToListMethodInfo);
|
||||
}
|
||||
private static void TypeCastFloat2String (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, floatToStringMethodInfo);
|
||||
}
|
||||
private static void TypeCastInteger2List (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, integerToListMethodInfo);
|
||||
}
|
||||
private static void TypeCastInteger2String (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, intToStringMethodInfo);
|
||||
}
|
||||
private static void TypeCastList2String (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, listToStringMethodInfo);
|
||||
}
|
||||
private static void TypeCastObject2String (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, objectToStringMethodInfo);
|
||||
}
|
||||
private static void TypeCastRotation2List (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, rotationToListMethodInfo);
|
||||
}
|
||||
private static void TypeCastRotation2String (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, rotationToStringMethodInfo);
|
||||
}
|
||||
private static void TypeCastString2Float (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Newobj, floatConstructorStringInfo);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslFloatValueFieldInfo);
|
||||
}
|
||||
private static void TypeCastString2Integer (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Newobj, integerConstructorStringInfo);
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslIntegerValueFieldInfo);
|
||||
}
|
||||
private static void TypeCastString2List (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, stringToListMethodInfo);
|
||||
}
|
||||
private static void TypeCastVector2String (IScriptCodeGen scg, Token errorAt)
|
||||
{
|
||||
scg.ilGen.Emit (errorAt, OpCodes.Call, vectorToStringMethodInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Because the calls are funky, let the compiler handle them.
|
||||
*/
|
||||
public static bool RotationToBool (LSL_Rotation x) { return !x.Equals (ScriptBaseClass.ZERO_ROTATION); }
|
||||
public static bool StringToBool (string x) { return x.Length > 0; }
|
||||
public static bool VectorToBool (LSL_Vector x) { return !x.Equals (ScriptBaseClass.ZERO_VECTOR); }
|
||||
public static string BoolToString (bool x) { return x ? "1" : "0"; }
|
||||
public static string CharToString (char x) { return x.ToString (); }
|
||||
public static string FloatToString (double x) { return x.ToString ("0.000000"); }
|
||||
public static string IntegerToString (int x) { return x.ToString (); }
|
||||
public static bool KeyToBool (string x) { return (x != "") && (x != ScriptBaseClass.NULL_KEY); }
|
||||
public static bool ListToBool (LSL_List x) { return x.Length != 0; }
|
||||
public static string ListToString (LSL_List x) { return x.ToString (); }
|
||||
public static string ObjectToString (object x) { return (x == null) ? null : x.ToString (); }
|
||||
public static string RotationToString (LSL_Rotation x) { return x.ToString (); }
|
||||
public static string VectorToString (LSL_Vector x) { return x.ToString (); }
|
||||
public static LSL_List BoolToList (bool b) { return new LSL_List (new object[] { new LSL_Integer (b ? 1 : 0) }); }
|
||||
public static LSL_List CharToList (char c) { return new LSL_List (new object[] { new LSL_Integer (c) }); }
|
||||
public static LSL_List ExcToList (Exception e) { return new LSL_List (new object[] { e }); }
|
||||
public static LSL_List VectorToList (LSL_Vector v) { return new LSL_List (new object[] { v }); }
|
||||
public static LSL_List FloatToList (double f) { return new LSL_List (new object[] { new LSL_Float (f) }); }
|
||||
public static LSL_List IntegerToList (int i) { return new LSL_List (new object[] { new LSL_Integer (i) }); }
|
||||
public static LSL_List RotationToList (LSL_Rotation r) { return new LSL_List (new object[] { r }); }
|
||||
public static LSL_List StringToList (string s) { return new LSL_List (new object[] { new LSL_String (s) }); }
|
||||
|
||||
public static double ObjectToFloat (object x)
|
||||
{
|
||||
if (x is LSL_String) return double.Parse (((LSL_String)x).m_string);
|
||||
if (x is string) return double.Parse ((string)x);
|
||||
if (x is LSL_Float) return (double)(LSL_Float)x;
|
||||
if (x is LSL_Integer) return (double)(int)(LSL_Integer)x;
|
||||
if (x is int) return (double)(int)x;
|
||||
return (double)x;
|
||||
}
|
||||
|
||||
public static int ObjectToInteger (object x)
|
||||
{
|
||||
if (x is LSL_String) return int.Parse (((LSL_String)x).m_string);
|
||||
if (x is string) return int.Parse ((string)x);
|
||||
if (x is LSL_Integer) return (int)(LSL_Integer)x;
|
||||
return (int)x;
|
||||
}
|
||||
|
||||
public static LSL_List ObjectToList (object x)
|
||||
{
|
||||
return (LSL_List)x;
|
||||
}
|
||||
|
||||
public static LSL_Rotation ObjectToRotation (object x)
|
||||
{
|
||||
if (x is LSL_String) return new LSL_Rotation (((LSL_String)x).m_string);
|
||||
if (x is string) return new LSL_Rotation ((string)x);
|
||||
return (LSL_Rotation)x;
|
||||
}
|
||||
|
||||
public static LSL_Vector ObjectToVector (object x)
|
||||
{
|
||||
if (x is LSL_String) return new LSL_Vector (((LSL_String)x).m_string);
|
||||
if (x is string) return new LSL_Vector ((string)x);
|
||||
return (LSL_Vector)x;
|
||||
}
|
||||
|
||||
public static string ExceptionToString (Exception x, XMRInstAbstract inst)
|
||||
{
|
||||
return XMRInstAbstract.xmrExceptionTypeName (x) + ": " + XMRInstAbstract.xmrExceptionMessage (x) +
|
||||
"\n" + inst.xmrExceptionStackTrace (x);
|
||||
}
|
||||
|
||||
/*
|
||||
* These are used by event handler entrypoints to remove any LSL wrapping
|
||||
* from the argument list and return the unboxed/unwrapped value.
|
||||
*/
|
||||
public static double EHArgUnwrapFloat (object x)
|
||||
{
|
||||
if (x is LSL_Float) return (double)(LSL_Float)x;
|
||||
return (double)x;
|
||||
}
|
||||
|
||||
public static int EHArgUnwrapInteger (object x)
|
||||
{
|
||||
if (x is LSL_Integer) return (int)(LSL_Integer)x;
|
||||
return (int)x;
|
||||
}
|
||||
|
||||
public static LSL_Rotation EHArgUnwrapRotation (object x)
|
||||
{
|
||||
if (x is OpenMetaverse.Quaternion) {
|
||||
OpenMetaverse.Quaternion q = (OpenMetaverse.Quaternion)x;
|
||||
return new LSL_Rotation(q.X, q.Y, q.Z, q.W);
|
||||
}
|
||||
return (LSL_Rotation)x;
|
||||
}
|
||||
|
||||
public static string EHArgUnwrapString (object x)
|
||||
{
|
||||
if (x is LSL_Key) return (string)(LSL_Key)x;
|
||||
if (x is LSL_String) return (string)(LSL_String)x;
|
||||
return (string)x;
|
||||
}
|
||||
|
||||
public static LSL_Vector EHArgUnwrapVector (object x)
|
||||
{
|
||||
if (x is OpenMetaverse.Vector3) {
|
||||
OpenMetaverse.Vector3 v = (OpenMetaverse.Vector3)x;
|
||||
return new LSL_Vector(v.X, v.Y, v.Z);
|
||||
}
|
||||
return (LSL_Vector)x;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/**
|
||||
* @brief Collection of variable/function/method definitions
|
||||
*/
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
public class VarDict : IEnumerable {
|
||||
public VarDict outerVarDict; // next outer VarDict to search
|
||||
public TokenDeclSDTypeClass thisClass; // this VarDict is for members of thisClass
|
||||
|
||||
private struct ArgTypes {
|
||||
public TokenType[] argTypes;
|
||||
|
||||
public bool CanBeCalledBy (TokenType[] calledBy)
|
||||
{
|
||||
if ((argTypes == null) && (calledBy == null)) return true;
|
||||
if ((argTypes == null) || (calledBy == null)) return false;
|
||||
if (argTypes.Length != calledBy.Length) return false;
|
||||
for (int i = argTypes.Length; -- i >= 0;) {
|
||||
if (!TypeCast.IsAssignableFrom (argTypes[i], calledBy[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Equals (Object that)
|
||||
{
|
||||
if (that == null) return false;
|
||||
if (that.GetType () != typeof (ArgTypes)) return false;
|
||||
TokenType[] at = this.argTypes;
|
||||
TokenType[] bt = ((ArgTypes)that).argTypes;
|
||||
if ((at == null) && (bt == null)) return true;
|
||||
if ((at == null) || (bt == null)) return false;
|
||||
if (at.Length != bt.Length) return false;
|
||||
for (int i = at.Length; -- i >= 0;) {
|
||||
if (at[i].ToString () != bt[i].ToString ()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetHashCode ()
|
||||
{
|
||||
TokenType[] at = this.argTypes;
|
||||
if (at == null) return -1;
|
||||
int hc = 0;
|
||||
for (int i = at.Length; -- i >= 0;) {
|
||||
int c = (hc < 0) ? 1 : 0;
|
||||
hc = hc * 2 + c;
|
||||
hc ^= at[i].ToString ().GetHashCode ();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
}
|
||||
|
||||
private struct TDVEntry {
|
||||
public int count;
|
||||
public TokenDeclVar var;
|
||||
}
|
||||
|
||||
private bool isFrozen = false;
|
||||
private bool locals;
|
||||
private Dictionary<string, Dictionary<ArgTypes, TDVEntry>> master = new Dictionary<string, Dictionary<ArgTypes, TDVEntry>> ();
|
||||
private int count = 0;
|
||||
private VarDict frozenLocals = null;
|
||||
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* @param locals = false: cannot be frozen, allows forward references
|
||||
* true: can be frozen, thus forbidding forward references
|
||||
*/
|
||||
public VarDict (bool locals)
|
||||
{
|
||||
this.locals = locals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add new variable to the dictionary.
|
||||
*/
|
||||
public bool AddEntry (TokenDeclVar var)
|
||||
{
|
||||
if (isFrozen) {
|
||||
throw new Exception ("var dict is frozen");
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure we have a sub-dictionary based on the bare name (ie, no signature)
|
||||
*/
|
||||
Dictionary<ArgTypes, TDVEntry> typedic;
|
||||
if (!master.TryGetValue (var.name.val, out typedic)) {
|
||||
typedic = new Dictionary<ArgTypes, TDVEntry> ();
|
||||
master.Add (var.name.val, typedic);
|
||||
}
|
||||
|
||||
/*
|
||||
* See if there is an entry in the sub-dictionary that matches the argument signature.
|
||||
* Note that fields have null argument lists.
|
||||
* Methods always have a non-null argument list, even if only 0 entries long.
|
||||
*/
|
||||
ArgTypes types;
|
||||
types.argTypes = (var.argDecl == null) ? null : KeyTypesToStringTypes (var.argDecl.types);
|
||||
if (typedic.ContainsKey (types)) return false;
|
||||
|
||||
/*
|
||||
* It is unique, add to its name-specific sub-dictionary.
|
||||
*/
|
||||
TDVEntry entry;
|
||||
entry.count = ++ count;
|
||||
entry.var = var;
|
||||
typedic.Add (types, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Count { get { return count; } }
|
||||
|
||||
/**
|
||||
* @brief If this is not a local variable frame, just return the frame as is.
|
||||
* If this is a local variable frame, return a version that is frozen,
|
||||
* ie, one that does not contain any future additions.
|
||||
*/
|
||||
public VarDict FreezeLocals ()
|
||||
{
|
||||
/*
|
||||
* If not local var frame, return original frame as is.
|
||||
* This will allow forward references as the future additions
|
||||
* will be seen by lookups done in this dictionary.
|
||||
*/
|
||||
if (!locals) return this;
|
||||
|
||||
/*
|
||||
* If local var frame, return a copy frozen at this point.
|
||||
* This disallows forward referenes as those future additions
|
||||
* will not be seen by lookups done in the frozen dictionary.
|
||||
*/
|
||||
if ((frozenLocals == null) || (frozenLocals.count != this.count)) {
|
||||
|
||||
/*
|
||||
* Make a copy of the current var dictionary frame.
|
||||
* We copy a reference to the dictionary, and though it may
|
||||
* contain additions made after this point, those additions
|
||||
* will have a count .gt. frozen count and will be ignored.
|
||||
*/
|
||||
frozenLocals = new VarDict (true);
|
||||
|
||||
frozenLocals.outerVarDict = this.outerVarDict;
|
||||
frozenLocals.thisClass = this.thisClass;
|
||||
frozenLocals.master = this.master;
|
||||
frozenLocals.count = this.count;
|
||||
frozenLocals.frozenLocals = frozenLocals;
|
||||
|
||||
/*
|
||||
* Mark it as being frozen.
|
||||
* - assert fail if any attempt is made to add to it
|
||||
* - ignore any additions to the dictionary with greater count
|
||||
*/
|
||||
frozenLocals.isFrozen = true;
|
||||
}
|
||||
return frozenLocals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find all functions/variables that are callable
|
||||
* @param name = name of function/variable to look for
|
||||
* @param argTypes = the argument types the function is being called with
|
||||
* null to look for a variable
|
||||
* @returns null: no matching function/variable found
|
||||
* else: list of matching functions/variables
|
||||
* for variables, always of length 1
|
||||
*/
|
||||
private List<TokenDeclVar> found = new List<TokenDeclVar> ();
|
||||
public TokenDeclVar[] FindCallables (string name, TokenType[] argTypes)
|
||||
{
|
||||
argTypes = KeyTypesToStringTypes (argTypes);
|
||||
TokenDeclVar var = FindExact (name, argTypes);
|
||||
if (var != null) return new TokenDeclVar[] { var };
|
||||
|
||||
Dictionary<ArgTypes, TDVEntry> typedic;
|
||||
if (!master.TryGetValue (name, out typedic)) return null;
|
||||
|
||||
found.Clear ();
|
||||
foreach (KeyValuePair<ArgTypes, TDVEntry> kvp in typedic) {
|
||||
if ((kvp.Value.count <= this.count) && kvp.Key.CanBeCalledBy (argTypes)) {
|
||||
found.Add (kvp.Value.var);
|
||||
}
|
||||
}
|
||||
return (found.Count > 0) ? found.ToArray () : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find exact matching function/variable
|
||||
* @param name = name of function to look for
|
||||
* @param argTypes = argument types the function was declared with
|
||||
* null to look for a variable
|
||||
* @returns null: no matching function/variable found
|
||||
* else: the matching function/variable
|
||||
*/
|
||||
public TokenDeclVar FindExact (string name, TokenType[] argTypes)
|
||||
{
|
||||
/*
|
||||
* Look for list of stuff that matches the given name.
|
||||
*/
|
||||
Dictionary<ArgTypes, TDVEntry> typedic;
|
||||
if (!master.TryGetValue (name, out typedic)) return null;
|
||||
|
||||
/*
|
||||
* Loop through all fields/methods declared by that name, regardless of arg signature.
|
||||
*/
|
||||
foreach (TDVEntry entry in typedic.Values) {
|
||||
if (entry.count > this.count) continue;
|
||||
TokenDeclVar var = entry.var;
|
||||
|
||||
/*
|
||||
* Get argument types of declaration.
|
||||
* fields are always null
|
||||
* methods are always non-null, though may be zero-length
|
||||
*/
|
||||
TokenType[] declArgs = (var.argDecl == null) ? null : var.argDecl.types;
|
||||
|
||||
/*
|
||||
* Convert any key args to string args.
|
||||
*/
|
||||
declArgs = KeyTypesToStringTypes (declArgs);
|
||||
|
||||
/*
|
||||
* If both are null, they are signature-less (ie, both are fields), and so match.
|
||||
*/
|
||||
if ((declArgs == null) && (argTypes == null)) return var;
|
||||
|
||||
/*
|
||||
* If calling a delegate, it is a match, regardless of delegate arg types.
|
||||
* If it turns out the arg types do not match, the compiler will give an error
|
||||
* trying to cast the arguments to the delegate arg types.
|
||||
* We don't allow overloading same field name with different delegate types.
|
||||
*/
|
||||
if ((declArgs == null) && (argTypes != null)) {
|
||||
TokenType fieldType = var.type;
|
||||
if (fieldType is TokenTypeSDTypeDelegate) return var;
|
||||
}
|
||||
|
||||
/*
|
||||
* If not both null, no match, keep looking.
|
||||
*/
|
||||
if ((declArgs == null) || (argTypes == null)) continue;
|
||||
|
||||
/*
|
||||
* Both not null, match argument types to make sure we have correct overload.
|
||||
*/
|
||||
int i = declArgs.Length;
|
||||
if (i != argTypes.Length) continue;
|
||||
while (-- i >= 0) {
|
||||
string da = declArgs[i].ToString ();
|
||||
string ga = argTypes[i].ToString ();
|
||||
if (da == "key") da = "string";
|
||||
if (ga == "key") ga = "string";
|
||||
if (da != ga) break;
|
||||
}
|
||||
if (i < 0) return var;
|
||||
}
|
||||
|
||||
/*
|
||||
* No match.
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replace any TokenTypeKey elements with TokenTypeStr so that
|
||||
* it doesn't matter if functions are declared with key or string,
|
||||
* they will accept either.
|
||||
* @param argTypes = argument types as declared in source code
|
||||
* @returns argTypes with any key replaced by string
|
||||
*/
|
||||
private static TokenType[] KeyTypesToStringTypes (TokenType[] argTypes)
|
||||
{
|
||||
if (argTypes != null) {
|
||||
int i;
|
||||
int nats = argTypes.Length;
|
||||
for (i = nats; -- i >= 0;) {
|
||||
if (argTypes[i] is TokenTypeKey) break;
|
||||
}
|
||||
if (i >= 0) {
|
||||
TokenType[] at = new TokenType[nats];
|
||||
for (i = nats; -- i >= 0;) {
|
||||
at[i] = argTypes[i];
|
||||
if (argTypes[i] is TokenTypeKey) {
|
||||
at[i] = new TokenTypeStr (argTypes[i]);
|
||||
}
|
||||
}
|
||||
return at;
|
||||
}
|
||||
}
|
||||
return argTypes;
|
||||
}
|
||||
|
||||
// foreach goes through all the TokenDeclVars that were added
|
||||
|
||||
// IEnumerable
|
||||
public IEnumerator GetEnumerator ()
|
||||
{
|
||||
return new VarDictEnumerator (this.master, this.count);
|
||||
}
|
||||
|
||||
private class VarDictEnumerator : IEnumerator {
|
||||
private IEnumerator masterEnum;
|
||||
private IEnumerator typedicEnum;
|
||||
private int count;
|
||||
|
||||
public VarDictEnumerator (Dictionary<string, Dictionary<ArgTypes, TDVEntry>> master, int count)
|
||||
{
|
||||
masterEnum = master.Values.GetEnumerator ();
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
// IEnumerator
|
||||
public void Reset ()
|
||||
{
|
||||
masterEnum.Reset ();
|
||||
typedicEnum = null;
|
||||
}
|
||||
|
||||
// IEnumerator
|
||||
public bool MoveNext ()
|
||||
{
|
||||
while (true) {
|
||||
if (typedicEnum != null) {
|
||||
while (typedicEnum.MoveNext ()) {
|
||||
if (((TDVEntry)typedicEnum.Current).count <= this.count) return true;
|
||||
}
|
||||
typedicEnum = null;
|
||||
}
|
||||
if (!masterEnum.MoveNext ()) return false;
|
||||
Dictionary<ArgTypes, TDVEntry> ctd;
|
||||
ctd = (Dictionary<ArgTypes, TDVEntry>)masterEnum.Current;
|
||||
typedicEnum = ctd.Values.GetEnumerator ();
|
||||
}
|
||||
}
|
||||
|
||||
// IEnumerator
|
||||
public object Current { get { return ((TDVEntry)typedicEnum.Current).var; } }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Perform web request
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine {
|
||||
public class MMRWebRequest {
|
||||
public static bool allowFileURL = false;
|
||||
|
||||
public static Stream MakeRequest (string verb, string requestUrl, string obj, int timeoutms)
|
||||
{
|
||||
/*
|
||||
* Pick apart the given URL and make sure we support it.
|
||||
* For file:// URLs, just return a read-only stream of the file.
|
||||
*/
|
||||
Uri uri = new Uri (requestUrl);
|
||||
string supported = "http and https";
|
||||
if (allowFileURL && (verb == "GET")) {
|
||||
supported = "file, http and https";
|
||||
if (uri.Scheme == "file") {
|
||||
return File.OpenRead (requestUrl.Substring (7));
|
||||
}
|
||||
}
|
||||
bool https = uri.Scheme == "https";
|
||||
if (!https && (uri.Scheme != "http")) {
|
||||
throw new WebException ("only support " + supported + ", not " + uri.Scheme);
|
||||
}
|
||||
string host = uri.Host;
|
||||
int port = uri.Port;
|
||||
if (port < 0) port = https ? 443 : 80;
|
||||
string path = uri.AbsolutePath;
|
||||
|
||||
/*
|
||||
* Connect to the web server.
|
||||
*/
|
||||
System.Net.Sockets.TcpClient tcpconnection = new System.Net.Sockets.TcpClient (host, port);
|
||||
if (timeoutms > 0) {
|
||||
tcpconnection.SendTimeout = timeoutms;
|
||||
tcpconnection.ReceiveTimeout = timeoutms;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
/*
|
||||
* Get TCP stream to/from web server.
|
||||
* If HTTPS, wrap stream with SSL encryption.
|
||||
*/
|
||||
Stream tcpstream = tcpconnection.GetStream ();
|
||||
if (https) {
|
||||
System.Net.Security.SslStream sslstream = new System.Net.Security.SslStream (tcpstream, false);
|
||||
sslstream.AuthenticateAsClient (host);
|
||||
tcpstream = sslstream;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write request header to the web server.
|
||||
* There might be some POST data as well to write to web server.
|
||||
*/
|
||||
WriteStream (tcpstream, verb + " " + path + " HTTP/1.1\r\n");
|
||||
WriteStream (tcpstream, "Host: " + host + "\r\n");
|
||||
if (obj != null) {
|
||||
byte[] bytes = Encoding.UTF8.GetBytes (obj);
|
||||
|
||||
WriteStream (tcpstream, "Content-Length: " + bytes.Length + "\r\n");
|
||||
WriteStream (tcpstream, "Content-Type: application/x-www-form-urlencoded\r\n");
|
||||
WriteStream (tcpstream, "\r\n");
|
||||
tcpstream.Write (bytes, 0, bytes.Length);
|
||||
} else {
|
||||
WriteStream (tcpstream, "\r\n");
|
||||
}
|
||||
tcpstream.Flush ();
|
||||
|
||||
/*
|
||||
* Check for successful reply status line.
|
||||
*/
|
||||
string headerline = ReadStreamLine (tcpstream).Trim ();
|
||||
if (headerline != "HTTP/1.1 200 OK") throw new WebException ("status line " + headerline);
|
||||
|
||||
/*
|
||||
* Scan through header lines.
|
||||
* The only ones we care about are Content-Length and Transfer-Encoding.
|
||||
*/
|
||||
bool chunked = false;
|
||||
int contentlength = -1;
|
||||
while ((headerline = ReadStreamLine (tcpstream).Trim ().ToLowerInvariant ()) != "") {
|
||||
if (headerline.StartsWith ("content-length:")) {
|
||||
contentlength = int.Parse (headerline.Substring (15));
|
||||
}
|
||||
if (headerline.StartsWith ("transfer-encoding:") && (headerline.Substring (18).Trim () == "chunked")) {
|
||||
chunked = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read response byte array as a series of chunks.
|
||||
*/
|
||||
if (chunked) {
|
||||
return new ChunkedStreamReader (tcpstream);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read response byte array with the exact length given by Content-Length.
|
||||
*/
|
||||
if (contentlength >= 0) {
|
||||
return new LengthStreamReader (tcpstream, contentlength);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't know how it is being transferred.
|
||||
*/
|
||||
throw new WebException ("header missing content-length or transfer-encoding: chunked");
|
||||
} catch {
|
||||
tcpconnection.Close ();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write the string out as ASCII bytes.
|
||||
*/
|
||||
private static void WriteStream (Stream stream, string line)
|
||||
{
|
||||
byte[] bytes = Encoding.ASCII.GetBytes (line);
|
||||
stream.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the next text line from a stream.
|
||||
* @returns string with \r\n trimmed off
|
||||
*/
|
||||
private static string ReadStreamLine (Stream stream)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder ();
|
||||
while (true) {
|
||||
int b = stream.ReadByte ();
|
||||
if (b < 0) break;
|
||||
if (b == '\n') break;
|
||||
if (b == '\r') continue;
|
||||
sb.Append ((char)b);
|
||||
}
|
||||
return sb.ToString ();
|
||||
}
|
||||
|
||||
private class ChunkedStreamReader : Stream {
|
||||
private int chunklen;
|
||||
private Stream tcpstream;
|
||||
|
||||
public ChunkedStreamReader (Stream tcpstream)
|
||||
{
|
||||
this.tcpstream = tcpstream;
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return true; } }
|
||||
public override bool CanSeek { get { return false; } }
|
||||
public override bool CanTimeout { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override long Length { get { return 0; } }
|
||||
public override long Position { get { return 0; } set { } }
|
||||
public override void Flush () { }
|
||||
public override long Seek (long offset, SeekOrigin origin) { return 0; }
|
||||
public override void SetLength (long length) { }
|
||||
public override void Write (byte[] buffer, int offset, int length) { }
|
||||
|
||||
public override int Read (byte[] buffer, int offset, int length)
|
||||
{
|
||||
if (length <= 0) return 0;
|
||||
|
||||
if (chunklen == 0) {
|
||||
chunklen = int.Parse (ReadStreamLine (tcpstream), System.Globalization.NumberStyles.HexNumber);
|
||||
if (chunklen < 0) throw new WebException ("negative chunk length");
|
||||
if (chunklen == 0) chunklen = -1;
|
||||
}
|
||||
if (chunklen < 0) return 0;
|
||||
|
||||
int maxread = (length < chunklen) ? length : chunklen;
|
||||
int lenread = tcpstream.Read (buffer, offset, maxread);
|
||||
chunklen -= lenread;
|
||||
if (chunklen == 0) {
|
||||
int b = tcpstream.ReadByte ();
|
||||
if (b == '\r') b = tcpstream.ReadByte ();
|
||||
if (b != '\n') throw new WebException ("chunk not followed by \\r\\n");
|
||||
}
|
||||
return lenread;
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
chunklen = -1;
|
||||
if (tcpstream != null) {
|
||||
tcpstream.Close ();
|
||||
tcpstream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LengthStreamReader : Stream {
|
||||
private int contentlength;
|
||||
private Stream tcpstream;
|
||||
|
||||
public LengthStreamReader (Stream tcpstream, int contentlength)
|
||||
{
|
||||
this.tcpstream = tcpstream;
|
||||
this.contentlength = contentlength;
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return true; } }
|
||||
public override bool CanSeek { get { return false; } }
|
||||
public override bool CanTimeout { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override long Length { get { return 0; } }
|
||||
public override long Position { get { return 0; } set { } }
|
||||
public override void Flush () { }
|
||||
public override long Seek (long offset, SeekOrigin origin) { return 0; }
|
||||
public override void SetLength (long length) { }
|
||||
public override void Write (byte[] buffer, int offset, int length) { }
|
||||
|
||||
public override int Read (byte[] buffer, int offset, int length)
|
||||
{
|
||||
if (length <= 0) return 0;
|
||||
if (contentlength <= 0) return 0;
|
||||
|
||||
int maxread = (length < contentlength) ? length : contentlength;
|
||||
int lenread = tcpstream.Read (buffer, offset, maxread);
|
||||
contentlength -= lenread;
|
||||
return lenread;
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
contentlength = -1;
|
||||
if (tcpstream != null) {
|
||||
tcpstream.Close ();
|
||||
tcpstream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Used to build a dummy Mono.Tasklets.dll file when running on Windows
|
||||
// Will also work if running with mono, it will just not allow use of
|
||||
// the "con" and "mmr" thread models, only "sys" will work.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mono.Tasklets {
|
||||
public class Continuation : IDisposable
|
||||
{
|
||||
public Continuation ()
|
||||
{
|
||||
throw new NotSupportedException ("'con' thread model requires mono");
|
||||
}
|
||||
public void Dispose ()
|
||||
{ }
|
||||
|
||||
public void Mark ()
|
||||
{ }
|
||||
|
||||
public int Store (int state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Restore (int state)
|
||||
{ }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,534 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
|
||||
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
|
||||
using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
|
||||
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
|
||||
using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
|
||||
using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
|
||||
using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
|
||||
|
||||
// This class exists in the main app domain
|
||||
//
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
/**
|
||||
* @brief Array objects.
|
||||
*/
|
||||
public class XMR_Array {
|
||||
private const int EMPTYHEAP = 64;
|
||||
private const int ENTRYHEAP = 24;
|
||||
|
||||
private bool enumrValid; // true: enumr set to return array[arrayValid]
|
||||
// false: array[0..arrayValid-1] is all there is
|
||||
private SortedDictionary<object, object> dnary;
|
||||
private SortedDictionary<object, object>.Enumerator enumr;
|
||||
// enumerator used to fill 'array' past arrayValid to end of dictionary
|
||||
private int arrayValid; // number of elements in 'array' that have been filled in
|
||||
private KeyValuePair<object, object>[] array; // list of kvp's that have been returned by ForEach() since last modification
|
||||
private XMRInstAbstract inst; // script instance debited with heap use
|
||||
private int heapUse; // current heap use debit amount
|
||||
|
||||
public static TokenTypeSDTypeDelegate countDelegate = new TokenTypeSDTypeDelegate (new TokenTypeInt (null), new TokenType[0]);
|
||||
public static TokenTypeSDTypeDelegate clearDelegate = new TokenTypeSDTypeDelegate (new TokenTypeVoid (null), new TokenType[0]);
|
||||
public static TokenTypeSDTypeDelegate indexDelegate = new TokenTypeSDTypeDelegate (new TokenTypeObject (null), new TokenType[] { new TokenTypeInt (null) });
|
||||
public static TokenTypeSDTypeDelegate valueDelegate = new TokenTypeSDTypeDelegate (new TokenTypeObject (null), new TokenType[] { new TokenTypeInt (null) });
|
||||
|
||||
public XMR_Array (XMRInstAbstract inst)
|
||||
{
|
||||
this.inst = inst;
|
||||
dnary = new SortedDictionary<object, object> (XMRArrayKeyComparer.singleton);
|
||||
heapUse = inst.UpdateHeapUse (0, EMPTYHEAP);
|
||||
}
|
||||
|
||||
~XMR_Array ()
|
||||
{
|
||||
heapUse = inst.UpdateHeapUse (heapUse, 0);
|
||||
}
|
||||
|
||||
public static TokenType GetRValType (TokenName name)
|
||||
{
|
||||
if (name.val == "count") return new TokenTypeInt (name);
|
||||
if (name.val == "clear") return clearDelegate;
|
||||
if (name.val == "index") return indexDelegate;
|
||||
if (name.val == "value") return valueDelegate;
|
||||
return new TokenTypeVoid (name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle 'array[index]' syntax to get or set an element of the dictionary.
|
||||
* Get returns null if element not defined, script sees type 'undef'.
|
||||
* Setting an element to null removes it.
|
||||
*/
|
||||
public object GetByKey(object key)
|
||||
{
|
||||
object val;
|
||||
key = FixKey (key);
|
||||
if (!dnary.TryGetValue (key, out val)) val = null;
|
||||
return val;
|
||||
}
|
||||
|
||||
public void SetByKey(object key, object value)
|
||||
{
|
||||
key = FixKey (key);
|
||||
|
||||
/*
|
||||
* Update heap use throwing an exception on failure
|
||||
* before making any changes to the array.
|
||||
*/
|
||||
int keysize = HeapTrackerObject.Size (key);
|
||||
int newheapuse = heapUse;
|
||||
object oldval;
|
||||
if (dnary.TryGetValue (key, out oldval)) {
|
||||
newheapuse -= keysize + HeapTrackerObject.Size (oldval);
|
||||
}
|
||||
if (value != null) {
|
||||
newheapuse += keysize + HeapTrackerObject.Size (value);
|
||||
}
|
||||
heapUse = inst.UpdateHeapUse (heapUse, newheapuse);
|
||||
|
||||
/*
|
||||
* Save new value in array, replacing one of same key if there.
|
||||
* null means remove the value, ie, script did array[key] = undef.
|
||||
*/
|
||||
if (value != null) {
|
||||
dnary[key] = value;
|
||||
} else {
|
||||
dnary.Remove (key);
|
||||
|
||||
/*
|
||||
* Shrink the enumeration array, but always leave at least one element.
|
||||
*/
|
||||
if ((array != null) && (dnary.Count < array.Length / 2)) {
|
||||
Array.Resize<KeyValuePair<object, object>> (ref array, array.Length / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The enumeration array is invalid because the dictionary has been modified.
|
||||
* Next time a ForEach() call happens, it will repopulate 'array' as elements are retrieved.
|
||||
*/
|
||||
arrayValid = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts an 'object' type to array, key, list, string, but disallows null,
|
||||
* as our language doesn't allow types other than 'object' to be null.
|
||||
* Value types (float, rotation, etc) don't need explicit check for null as
|
||||
* the C# runtime can't convert a null to a value type, and throws an exception.
|
||||
* But for any reference type (array, key, etc) we must manually check for null.
|
||||
*/
|
||||
public static XMR_Array Obj2Array (object obj)
|
||||
{
|
||||
if (obj == null) throw new NullReferenceException ();
|
||||
return (XMR_Array)obj;
|
||||
}
|
||||
public static LSL_Key Obj2Key (object obj)
|
||||
{
|
||||
if (obj == null) throw new NullReferenceException ();
|
||||
return (LSL_Key)obj;
|
||||
}
|
||||
public static LSL_List Obj2List (object obj)
|
||||
{
|
||||
if (obj == null) throw new NullReferenceException ();
|
||||
return (LSL_List)obj;
|
||||
}
|
||||
public static LSL_String Obj2String (object obj)
|
||||
{
|
||||
if (obj == null) throw new NullReferenceException ();
|
||||
return obj.ToString ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief remove all elements from the array.
|
||||
* sets everything to its 'just constructed' state.
|
||||
*/
|
||||
public void __pub_clear ()
|
||||
{
|
||||
heapUse = inst.UpdateHeapUse (heapUse, EMPTYHEAP);
|
||||
dnary.Clear ();
|
||||
enumrValid = false;
|
||||
arrayValid = 0;
|
||||
array = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief return number of elements in the array.
|
||||
*/
|
||||
public int __pub_count ()
|
||||
{
|
||||
return dnary.Count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve index (key) of an arbitrary element.
|
||||
* @param number = number of the element (0 based)
|
||||
* @returns null: array doesn't have that many elements
|
||||
* else: index (key) for that element
|
||||
*/
|
||||
public object __pub_index (int number)
|
||||
{
|
||||
return ForEach (number) ? UnfixKey (array[number].Key) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve value of an arbitrary element.
|
||||
* @param number = number of the element (0 based)
|
||||
* @returns null: array doesn't have that many elements
|
||||
* else: value for that element
|
||||
*/
|
||||
public object __pub_value (int number)
|
||||
{
|
||||
return ForEach (number) ? array[number].Value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called in each iteration of a 'foreach' statement.
|
||||
* @param number = index of element to retrieve (0 = first one)
|
||||
* @returns false: element does not exist
|
||||
* true: element exists
|
||||
*/
|
||||
private bool ForEach (int number)
|
||||
{
|
||||
/*
|
||||
* If we don't have any array, we can't have ever done
|
||||
* any calls here before, so allocate an array big enough
|
||||
* and set everything else to the beginning.
|
||||
*/
|
||||
if (array == null) {
|
||||
array = new KeyValuePair<object, object>[dnary.Count];
|
||||
arrayValid = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If dictionary modified since last enumeration, get a new enumerator.
|
||||
*/
|
||||
if (arrayValid == 0) {
|
||||
enumr = dnary.GetEnumerator ();
|
||||
enumrValid = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure we have filled the array up enough for requested element.
|
||||
*/
|
||||
while ((arrayValid <= number) && enumrValid && enumr.MoveNext ()) {
|
||||
if (arrayValid >= array.Length) {
|
||||
Array.Resize<KeyValuePair<object, object>> (ref array, dnary.Count);
|
||||
}
|
||||
array[arrayValid++] = enumr.Current;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we don't have that many elements, return end-of-array status.
|
||||
*/
|
||||
return number < arrayValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transmit array out in such a way that it can be reconstructed,
|
||||
* including any in-progress ForEach() enumerations.
|
||||
*/
|
||||
public delegate void SendArrayObjDelegate (object graph);
|
||||
public void SendArrayObj (SendArrayObjDelegate sendObj)
|
||||
{
|
||||
/*
|
||||
* Set the count then the elements themselves.
|
||||
* UnfixKey() because sendObj doesn't handle XMRArrayListKeys.
|
||||
*/
|
||||
sendObj (dnary.Count);
|
||||
foreach (KeyValuePair<object, object> kvp in dnary) {
|
||||
sendObj (UnfixKey (kvp.Key));
|
||||
sendObj (kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receive array in. Any previous contents are erased.
|
||||
* Set up such that any enumeration in progress will resume
|
||||
* at the exact spot and in the exact same order as they
|
||||
* were in on the sending side.
|
||||
*/
|
||||
public delegate object RecvArrayObjDelegate ();
|
||||
public void RecvArrayObj (RecvArrayObjDelegate recvObj)
|
||||
{
|
||||
heapUse = inst.UpdateHeapUse (heapUse, EMPTYHEAP);
|
||||
|
||||
/*
|
||||
* Cause any enumeration to refill the array from the sorted dictionary.
|
||||
* Since it is a sorted dictionary, any enumerations will be in the same
|
||||
* order as on the sending side.
|
||||
*/
|
||||
arrayValid = 0;
|
||||
enumrValid = false;
|
||||
|
||||
/*
|
||||
* Fill dictionary.
|
||||
*/
|
||||
dnary.Clear ();
|
||||
int count = (int)recvObj ();
|
||||
while (-- count >= 0) {
|
||||
object key = FixKey (recvObj ());
|
||||
object val = recvObj ();
|
||||
int htuse = HeapTrackerObject.Size (key) + HeapTrackerObject.Size (val);
|
||||
heapUse = inst.UpdateHeapUse (heapUse, heapUse + htuse);
|
||||
dnary.Add (key, val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We want our index values to be of consistent type, otherwise we get things like (LSL_Integer)1 != (int)1.
|
||||
* So strip off any LSL-ness from the types.
|
||||
* We also deep-strip any given lists used as keys (multi-dimensional arrays).
|
||||
*/
|
||||
public static object FixKey (object key)
|
||||
{
|
||||
if (key is LSL_Integer) return (int)(LSL_Integer)key;
|
||||
if (key is LSL_Float) return (double)(LSL_Float)key;
|
||||
if (key is LSL_Key) return (string)(LSL_Key)key;
|
||||
if (key is LSL_String) return (string)(LSL_String)key;
|
||||
if (key is LSL_List) {
|
||||
object[] data = ((LSL_List)key).Data;
|
||||
if (data.Length == 1) return FixKey (data[0]);
|
||||
return new XMRArrayListKey ((LSL_List)key);
|
||||
}
|
||||
return key; // int, double, string, LSL_Vector, LSL_Rotation, etc are ok as is
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief When returning a key, such as for array.index(), we want to return the original
|
||||
* LSL_List, not the sanitized one, as the script compiler expects an LSL_List.
|
||||
* Any other sanitized types can remain as is (int, string, etc).
|
||||
*/
|
||||
private static object UnfixKey (object key)
|
||||
{
|
||||
if (key is XMRArrayListKey) key = ((XMRArrayListKey)key).GetOriginal ();
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
public class XMRArrayKeyComparer : IComparer<object> {
|
||||
|
||||
public static XMRArrayKeyComparer singleton = new XMRArrayKeyComparer ();
|
||||
|
||||
/**
|
||||
* @brief Compare two keys
|
||||
*/
|
||||
public int Compare (object x, object y) // IComparer<object>
|
||||
{
|
||||
/*
|
||||
* Use short type name (eg, String, Int32, XMRArrayListKey) as most significant part of key.
|
||||
*/
|
||||
string xtn = x.GetType ().Name;
|
||||
string ytn = y.GetType ().Name;
|
||||
int ctn = String.CompareOrdinal (xtn, ytn);
|
||||
if (ctn != 0) return ctn;
|
||||
|
||||
ComparerDelegate cd;
|
||||
if (!comparers.TryGetValue (xtn, out cd)) {
|
||||
throw new Exception ("unsupported key type " + xtn);
|
||||
}
|
||||
return cd (x, y);
|
||||
}
|
||||
|
||||
private delegate int ComparerDelegate (object a, object b);
|
||||
|
||||
private static Dictionary<string, ComparerDelegate> comparers = BuildComparers ();
|
||||
|
||||
private static Dictionary<string, ComparerDelegate> BuildComparers ()
|
||||
{
|
||||
Dictionary<string, ComparerDelegate> cmps = new Dictionary<string, ComparerDelegate> ();
|
||||
cmps.Add (typeof (double).Name, MyFloatComparer);
|
||||
cmps.Add (typeof (int).Name, MyIntComparer);
|
||||
cmps.Add (typeof (XMRArrayListKey).Name, MyListKeyComparer);
|
||||
cmps.Add (typeof (LSL_Rotation).Name, MyRotationComparer);
|
||||
cmps.Add (typeof (string).Name, MyStringComparer);
|
||||
cmps.Add (typeof (LSL_Vector).Name, MyVectorComparer);
|
||||
return cmps;
|
||||
}
|
||||
|
||||
private static int MyFloatComparer (object a, object b)
|
||||
{
|
||||
double af = (double)a;
|
||||
double bf = (double)b;
|
||||
if (af < bf) return -1;
|
||||
if (af > bf) return 1;
|
||||
return 0;
|
||||
}
|
||||
private static int MyIntComparer (object a, object b)
|
||||
{
|
||||
return (int)a - (int)b;
|
||||
}
|
||||
private static int MyListKeyComparer (object a, object b)
|
||||
{
|
||||
XMRArrayListKey alk = (XMRArrayListKey)a;
|
||||
XMRArrayListKey blk = (XMRArrayListKey)b;
|
||||
return XMRArrayListKey.Compare (alk, blk);
|
||||
}
|
||||
private static int MyRotationComparer (object a, object b)
|
||||
{
|
||||
LSL_Rotation ar = (LSL_Rotation)a;
|
||||
LSL_Rotation br = (LSL_Rotation)b;
|
||||
if (ar.x < br.x) return -1;
|
||||
if (ar.x > br.x) return 1;
|
||||
if (ar.y < br.y) return -1;
|
||||
if (ar.y > br.y) return 1;
|
||||
if (ar.z < br.z) return -1;
|
||||
if (ar.z > br.z) return 1;
|
||||
if (ar.s < br.s) return -1;
|
||||
if (ar.s > br.s) return 1;
|
||||
return 0;
|
||||
}
|
||||
private static int MyStringComparer (object a, object b)
|
||||
{
|
||||
return String.CompareOrdinal ((string)a, (string)b);
|
||||
}
|
||||
private static int MyVectorComparer (object a, object b)
|
||||
{
|
||||
LSL_Vector av = (LSL_Vector)a;
|
||||
LSL_Vector bv = (LSL_Vector)b;
|
||||
if (av.x < bv.x) return -1;
|
||||
if (av.x > bv.x) return 1;
|
||||
if (av.y < bv.y) return -1;
|
||||
if (av.y > bv.y) return 1;
|
||||
if (av.z < bv.z) return -1;
|
||||
if (av.z > bv.z) return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Lists used as keys must be sanitized first.
|
||||
* List gets converted to an object[] and each element is converted from LSL_ types to system types where possible.
|
||||
* And we also need an equality operator that compares the values of all elements of the list, not just the lengths.
|
||||
* Note that just like LSL_Lists, we consider these objects to be immutable, so they can be directly used as keys in
|
||||
* the dictionary as they don't ever change.
|
||||
*/
|
||||
public class XMRArrayListKey {
|
||||
private LSL_List original;
|
||||
private object[] cleaned;
|
||||
private int length;
|
||||
private int hashCode;
|
||||
|
||||
/**
|
||||
* @brief Construct a sanitized object[] from a list.
|
||||
* Also save the original list in case we need it later.
|
||||
*/
|
||||
public XMRArrayListKey (LSL_List key)
|
||||
{
|
||||
original = key;
|
||||
object[] given = key.Data;
|
||||
int len = given.Length;
|
||||
length = len;
|
||||
cleaned = new object[len];
|
||||
int hc = len;
|
||||
for (int i = 0; i < len; i ++) {
|
||||
object v = XMR_Array.FixKey (given[i]);
|
||||
hc += hc + ((hc < 0) ? 1 : 0);
|
||||
hc ^= v.GetHashCode ();
|
||||
cleaned[i] = v;
|
||||
}
|
||||
hashCode = hc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get heap tracking size.
|
||||
*/
|
||||
public int Size {
|
||||
get {
|
||||
return original.Size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief See if the given object is an XMRArrayListKey and every value is equal to our own.
|
||||
*/
|
||||
public override bool Equals (object o)
|
||||
{
|
||||
if (!(o is XMRArrayListKey)) return false;
|
||||
XMRArrayListKey a = (XMRArrayListKey)o;
|
||||
int len = a.length;
|
||||
if (len != length) return false;
|
||||
if (a.hashCode != hashCode) return false;
|
||||
for (int i = 0; i < len; i ++) {
|
||||
if (!cleaned[i].Equals (a.cleaned[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get an hash code.
|
||||
*/
|
||||
public override int GetHashCode ()
|
||||
{
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare for key sorting.
|
||||
*/
|
||||
public static int Compare (XMRArrayListKey x, XMRArrayListKey y)
|
||||
{
|
||||
int j = x.length - y.length;
|
||||
if (j == 0) {
|
||||
for (int i = 0; i < x.length; i ++) {
|
||||
object xo = x.cleaned[i];
|
||||
object yo = y.cleaned[i];
|
||||
j = XMRArrayKeyComparer.singleton.Compare (xo, yo);
|
||||
if (j != 0) break;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the original LSL_List we were built from.
|
||||
*/
|
||||
public LSL_List GetOriginal ()
|
||||
{
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Debugging
|
||||
*/
|
||||
public override string ToString ()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder ();
|
||||
for (int i = 0; i < length; i ++) {
|
||||
if (i > 0) sb.Append (',');
|
||||
sb.Append (cleaned[i].ToString ());
|
||||
}
|
||||
return sb.ToString ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* 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 log4net;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
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 partial class XMREngine {
|
||||
|
||||
private void XmrTestLs (string[] args, int indx)
|
||||
{
|
||||
bool flagFull = false;
|
||||
bool flagQueues = false;
|
||||
bool flagTopCPU = false;
|
||||
int maxScripts = 0x7FFFFFFF;
|
||||
int numScripts = 0;
|
||||
string outName = null;
|
||||
XMRInstance[] instances;
|
||||
|
||||
/*
|
||||
* Decode command line options.
|
||||
*/
|
||||
for (int i = indx; i < args.Length; i ++) {
|
||||
if (args[i] == "-full") {
|
||||
flagFull = true;
|
||||
continue;
|
||||
}
|
||||
if (args[i] == "-help") {
|
||||
m_log.Info ("[XMREngine]: xmr ls -full -max=<number> -out=<filename> -queues -topcpu");
|
||||
return;
|
||||
}
|
||||
if (args[i].StartsWith("-max=")) {
|
||||
try {
|
||||
maxScripts = Convert.ToInt32(args[i].Substring(5));
|
||||
} catch (Exception e) {
|
||||
m_log.Error("[XMREngine]: bad max " + args[i].Substring(5) + ": " + e.Message);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (args[i].StartsWith("-out=")) {
|
||||
outName = args[i].Substring(5);
|
||||
continue;
|
||||
}
|
||||
if (args[i] == "-queues") {
|
||||
flagQueues = true;
|
||||
continue;
|
||||
}
|
||||
if (args[i] == "-topcpu") {
|
||||
flagTopCPU = true;
|
||||
continue;
|
||||
}
|
||||
if (args[i][0] == '-') {
|
||||
m_log.Error("[XMREngine]: unknown option " + args[i] + ", try 'xmr ls -help'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TextWriter outFile = null;
|
||||
if (outName != null) {
|
||||
try {
|
||||
outFile = File.CreateText(outName);
|
||||
} catch (Exception e) {
|
||||
m_log.Error("[XMREngine]: error creating " + outName + ": " + e.Message);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
outFile = new LogInfoTextWriter(m_log);
|
||||
}
|
||||
|
||||
try {
|
||||
for (int i = 0; i < numThreadScriptWorkers; i ++) {
|
||||
XMRScriptThread th = m_ScriptThreads[i];
|
||||
outFile.WriteLine("Script thread ID: " + th.m_ScriptThreadTID);
|
||||
long execTime = th.m_ScriptExecTime;
|
||||
if (execTime < 0) {
|
||||
execTime += (long)(DateTime.UtcNow - DateTime.MinValue).TotalMilliseconds;
|
||||
}
|
||||
outFile.WriteLine(" execution time: " + execTime + " mS");
|
||||
outFile.WriteLine(" last ran at: " + th.m_LastRanAt.ToString());
|
||||
XMRInstance rins = th.m_RunInstance;
|
||||
if (rins != null) {
|
||||
outFile.WriteLine(" running: " + rins.ItemID.ToString() + " " + rins.m_DescName);
|
||||
if (flagFull) {
|
||||
outFile.WriteLine (rins.RunTestLs (true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan instance list to find those that match selection criteria.
|
||||
*/
|
||||
if (!Monitor.TryEnter(m_InstancesDict, 100)) {
|
||||
m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
instances = new XMRInstance[m_InstancesDict.Count];
|
||||
foreach (XMRInstance ins in m_InstancesDict.Values)
|
||||
{
|
||||
if (InstanceMatchesArgs(ins, args, indx)) {
|
||||
instances[numScripts++] = ins;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Monitor.Exit(m_InstancesDict);
|
||||
}
|
||||
|
||||
/*
|
||||
* Maybe sort by descending CPU time.
|
||||
*/
|
||||
if (flagTopCPU) {
|
||||
Array.Sort<XMRInstance>(instances, CompareInstancesByCPUTime);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the entries.
|
||||
*/
|
||||
if (!flagFull) {
|
||||
outFile.WriteLine(" ItemID" +
|
||||
" CPU(ms)" +
|
||||
" NumEvents" +
|
||||
" Status " +
|
||||
" World Position " +
|
||||
" <Part>:<Item>");
|
||||
}
|
||||
for (int i = 0; (i < numScripts) && (i < maxScripts); i ++) {
|
||||
outFile.WriteLine(instances[i].RunTestLs(flagFull));
|
||||
}
|
||||
|
||||
/*
|
||||
* Print number of scripts that match selection criteria,
|
||||
* even if we were told to print fewer.
|
||||
*/
|
||||
outFile.WriteLine("total of {0} script(s)", numScripts);
|
||||
|
||||
/*
|
||||
* If -queues given, print out queue contents too.
|
||||
*/
|
||||
if (flagQueues) {
|
||||
LsQueue(outFile, "start", m_StartQueue, args, indx);
|
||||
LsQueue(outFile, "sleep", m_SleepQueue, args, indx);
|
||||
LsQueue(outFile, "yield", m_YieldQueue, args, indx);
|
||||
}
|
||||
} finally {
|
||||
outFile.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void XmrTestPev (string[] args, int indx)
|
||||
{
|
||||
bool flagAll = false;
|
||||
int numScripts = 0;
|
||||
XMRInstance[] instances;
|
||||
|
||||
/*
|
||||
* Decode command line options.
|
||||
*/
|
||||
int i, j;
|
||||
List<string> selargs = new List<string> (args.Length);
|
||||
MethodInfo[] eventmethods = typeof (IEventHandlers).GetMethods ();
|
||||
MethodInfo eventmethod;
|
||||
for (i = indx; i < args.Length; i ++) {
|
||||
string arg = args[i];
|
||||
if (arg == "-all") {
|
||||
flagAll = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-help") {
|
||||
m_log.Info ("[XMREngine]: xmr pev -all | <part-of-script-name> <event-name> <params...>");
|
||||
return;
|
||||
}
|
||||
if (arg[0] == '-') {
|
||||
m_log.Error ("[XMREngine]: unknown option " + arg + ", try 'xmr pev -help'");
|
||||
return;
|
||||
}
|
||||
for (j = 0; j < eventmethods.Length; j ++) {
|
||||
eventmethod = eventmethods[j];
|
||||
if (eventmethod.Name == arg) goto gotevent;
|
||||
}
|
||||
selargs.Add (arg);
|
||||
}
|
||||
m_log.Error ("[XMREngine]: missing <event-name> <params...>, try 'xmr pev -help'");
|
||||
return;
|
||||
gotevent:
|
||||
string eventname = eventmethod.Name;
|
||||
StringBuilder sourcesb = new StringBuilder ();
|
||||
while (++ i < args.Length) {
|
||||
sourcesb.Append (' ');
|
||||
sourcesb.Append (args[i]);
|
||||
}
|
||||
string sourcest = sourcesb.ToString ();
|
||||
string sourcehash;
|
||||
youveanerror = false;
|
||||
Token t = TokenBegin.Construct ("", null, ErrorMsg, sourcest, out sourcehash);
|
||||
if (youveanerror) return;
|
||||
ParameterInfo[] paraminfos = eventmethod.GetParameters ();
|
||||
object[] paramvalues = new object[paraminfos.Length];
|
||||
i = 0;
|
||||
while (!((t = t.nextToken) is TokenEnd)) {
|
||||
if (i >= paramvalues.Length) {
|
||||
ErrorMsg (t, "extra parameter(s)");
|
||||
return;
|
||||
}
|
||||
paramvalues[i] = ParseParamValue (ref t);
|
||||
if (paramvalues[i] == null) return;
|
||||
i ++;
|
||||
}
|
||||
OpenSim.Region.ScriptEngine.Shared.EventParams eps =
|
||||
new OpenSim.Region.ScriptEngine.Shared.EventParams (eventname, paramvalues, zeroDetectParams);
|
||||
|
||||
/*
|
||||
* Scan instance list to find those that match selection criteria.
|
||||
*/
|
||||
if (!Monitor.TryEnter(m_InstancesDict, 100)) {
|
||||
m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
instances = new XMRInstance[m_InstancesDict.Count];
|
||||
foreach (XMRInstance ins in m_InstancesDict.Values) {
|
||||
if (flagAll || InstanceMatchesArgs (ins, selargs.ToArray (), 0)) {
|
||||
instances[numScripts++] = ins;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Monitor.Exit(m_InstancesDict);
|
||||
}
|
||||
|
||||
/*
|
||||
* Post event to the matching instances.
|
||||
*/
|
||||
for (i = 0; i < numScripts; i ++) {
|
||||
XMRInstance inst = instances[i];
|
||||
m_log.Info ("[XMREngine]: post " + eventname + " to " + inst.m_DescName);
|
||||
inst.PostEvent (eps);
|
||||
}
|
||||
}
|
||||
|
||||
private object ParseParamValue (ref Token token)
|
||||
{
|
||||
if (token is TokenFloat) {
|
||||
return new LSL_Float (((TokenFloat)token).val);
|
||||
}
|
||||
if (token is TokenInt) {
|
||||
return new LSL_Integer (((TokenInt)token).val);
|
||||
}
|
||||
if (token is TokenStr) {
|
||||
return new LSL_String (((TokenStr)token).val);
|
||||
}
|
||||
if (token is TokenKwCmpLT) {
|
||||
List<double> valuelist = new List<double> ();
|
||||
while (!((token = token.nextToken) is TokenKwCmpGT)) {
|
||||
if (!(token is TokenKwComma)) {
|
||||
object value = ParseParamValue (ref token);
|
||||
if (value == null) return null;
|
||||
if (value is int) value = (double)(int)value;
|
||||
if (!(value is double)) {
|
||||
ErrorMsg (token, "must be float or integer constant");
|
||||
return null;
|
||||
}
|
||||
valuelist.Add ((double)value);
|
||||
} else if (token.prevToken is TokenKwComma) {
|
||||
ErrorMsg (token, "missing constant");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
double[] values = valuelist.ToArray ();
|
||||
switch (values.Length) {
|
||||
case 3: {
|
||||
return new LSL_Vector (values[0], values[1], values[2]);
|
||||
}
|
||||
case 4: {
|
||||
return new LSL_Rotation (values[0], values[1], values[2], values[3]);
|
||||
}
|
||||
default: {
|
||||
ErrorMsg (token, "not rotation or vector");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (token is TokenKwBrkOpen) {
|
||||
List<object> valuelist = new List<object> ();
|
||||
while (!((token = token.nextToken) is TokenKwBrkClose)) {
|
||||
if (!(token is TokenKwComma)) {
|
||||
object value = ParseParamValue (ref token);
|
||||
if (value == null) return null;
|
||||
valuelist.Add (value);
|
||||
} else if (token.prevToken is TokenKwComma) {
|
||||
ErrorMsg (token, "missing constant");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new LSL_List (valuelist.ToArray ());
|
||||
}
|
||||
if (token is TokenName) {
|
||||
FieldInfo field = typeof (OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass).GetField (((TokenName)token).val);
|
||||
if ((field != null) && field.IsPublic && (field.IsLiteral || (field.IsStatic && field.IsInitOnly))) {
|
||||
return field.GetValue (null);
|
||||
}
|
||||
}
|
||||
ErrorMsg (token, "invalid constant");
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool youveanerror;
|
||||
private void ErrorMsg (Token token, string message)
|
||||
{
|
||||
youveanerror = true;
|
||||
m_log.Info ("[XMREngine]: " + token.posn + " " + message);
|
||||
}
|
||||
|
||||
private void XmrTestReset (string[] args, int indx)
|
||||
{
|
||||
bool flagAll = false;
|
||||
int numScripts = 0;
|
||||
XMRInstance[] instances;
|
||||
|
||||
if (args.Length <= indx) {
|
||||
m_log.Error("[XMREngine]: must specify part of script name or -all for all scripts");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode command line options.
|
||||
*/
|
||||
for (int i = indx; i < args.Length; i ++) {
|
||||
if (args[i] == "-all") {
|
||||
flagAll = true;
|
||||
continue;
|
||||
}
|
||||
if (args[i] == "-help") {
|
||||
m_log.Info ("[XMREngine]: xmr reset -all | <part-of-script-name>");
|
||||
return;
|
||||
}
|
||||
if (args[i][0] == '-') {
|
||||
m_log.Error ("[XMREngine]: unknown option " + args[i] + ", try 'xmr reset -help'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan instance list to find those that match selection criteria.
|
||||
*/
|
||||
if (!Monitor.TryEnter(m_InstancesDict, 100)) {
|
||||
m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
instances = new XMRInstance[m_InstancesDict.Count];
|
||||
foreach (XMRInstance ins in m_InstancesDict.Values) {
|
||||
if (flagAll || InstanceMatchesArgs (ins, args, indx)) {
|
||||
instances[numScripts++] = ins;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Monitor.Exit(m_InstancesDict);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the instances as if someone clicked their "Reset" button.
|
||||
*/
|
||||
for (int i = 0; i < numScripts; i ++) {
|
||||
XMRInstance inst = instances[i];
|
||||
m_log.Info ("[XMREngine]: resetting " + inst.m_DescName);
|
||||
inst.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
private static int CompareInstancesByCPUTime(XMRInstance a, XMRInstance b)
|
||||
{
|
||||
if (a == null) {
|
||||
return (b == null) ? 0 : 1;
|
||||
}
|
||||
if (b == null) {
|
||||
return -1;
|
||||
}
|
||||
if (b.m_CPUTime < a.m_CPUTime) return -1;
|
||||
if (b.m_CPUTime > a.m_CPUTime) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void LsQueue(TextWriter outFile, string name, XMRInstQueue queue, string[] args, int indx)
|
||||
{
|
||||
outFile.WriteLine("Queue " + name + ":");
|
||||
lock (queue) {
|
||||
for (XMRInstance inst = queue.PeekHead(); inst != null; inst = inst.m_NextInst) {
|
||||
try {
|
||||
|
||||
/*
|
||||
* Try to print instance name.
|
||||
*/
|
||||
if (InstanceMatchesArgs(inst, args, indx)) {
|
||||
outFile.WriteLine(" " + inst.ItemID.ToString() + " " + inst.m_DescName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
/*
|
||||
* Sometimes there are instances in the queue that are disposed.
|
||||
*/
|
||||
outFile.WriteLine(" " + inst.ItemID.ToString() + " " + inst.m_DescName + ": " + e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool InstanceMatchesArgs(XMRInstance ins, string[] args, int indx)
|
||||
{
|
||||
bool hadSomethingToCompare = false;
|
||||
|
||||
for (int i = indx; i < args.Length; i ++)
|
||||
{
|
||||
if (args[i][0] != '-') {
|
||||
hadSomethingToCompare = true;
|
||||
if (ins.m_DescName.Contains(args[i])) return true;
|
||||
if (ins.ItemID.ToString().Contains(args[i])) return true;
|
||||
if (ins.AssetID.ToString().Contains(args[i])) return true;
|
||||
}
|
||||
}
|
||||
return !hadSomethingToCompare;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make m_log.Info look like a text writer.
|
||||
*/
|
||||
public class LogInfoTextWriter : TextWriter {
|
||||
private StringBuilder sb = new StringBuilder();
|
||||
private ILog m_log;
|
||||
public LogInfoTextWriter (ILog m_log)
|
||||
{
|
||||
this.m_log = m_log;
|
||||
}
|
||||
public override void Write (char c)
|
||||
{
|
||||
if (c == '\n') {
|
||||
m_log.Info("[XMREngine]: " + sb.ToString());
|
||||
sb.Remove(0, sb.Length);
|
||||
} else {
|
||||
sb.Append(c);
|
||||
}
|
||||
}
|
||||
public override void Close () { }
|
||||
public override Encoding Encoding {
|
||||
get {
|
||||
return Encoding.UTF8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.ScriptEngine.Shared;
|
||||
using OpenSim.Region.ScriptEngine.Interfaces;
|
||||
using log4net;
|
||||
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Prepares events so they can be directly executed upon a script by EventQueueManager, then queues it.
|
||||
/// </summary>
|
||||
public partial class XMREngine
|
||||
{
|
||||
public static readonly object[] zeroObjectArray = new object[0];
|
||||
public static readonly object[] oneObjectArrayOne = new object[1] { 1 };
|
||||
|
||||
private void InitEvents()
|
||||
{
|
||||
m_log.Info("[XMREngine] Hooking up to server events");
|
||||
this.World.EventManager.OnAttach += attach;
|
||||
this.World.EventManager.OnObjectGrab += touch_start;
|
||||
this.World.EventManager.OnObjectGrabbing += touch;
|
||||
this.World.EventManager.OnObjectDeGrab += touch_end;
|
||||
this.World.EventManager.OnScriptChangedEvent += changed;
|
||||
this.World.EventManager.OnScriptAtTargetEvent += at_target;
|
||||
this.World.EventManager.OnScriptNotAtTargetEvent += not_at_target;
|
||||
this.World.EventManager.OnScriptAtRotTargetEvent += at_rot_target;
|
||||
this.World.EventManager.OnScriptNotAtRotTargetEvent += not_at_rot_target;
|
||||
this.World.EventManager.OnScriptMovingStartEvent += moving_start;
|
||||
this.World.EventManager.OnScriptMovingEndEvent += moving_end;
|
||||
this.World.EventManager.OnScriptControlEvent += control;
|
||||
this.World.EventManager.OnScriptColliderStart += collision_start;
|
||||
this.World.EventManager.OnScriptColliding += collision;
|
||||
this.World.EventManager.OnScriptCollidingEnd += collision_end;
|
||||
this.World.EventManager.OnScriptLandColliderStart += land_collision_start;
|
||||
this.World.EventManager.OnScriptLandColliding += land_collision;
|
||||
this.World.EventManager.OnScriptLandColliderEnd += land_collision_end;
|
||||
IMoneyModule money=this.World.RequestModuleInterface<IMoneyModule>();
|
||||
if (money != null)
|
||||
{
|
||||
money.OnObjectPaid+=HandleObjectPaid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When an object gets paid by an avatar and generates the paid event,
|
||||
/// this will pipe it to the script engine
|
||||
/// </summary>
|
||||
/// <param name="objectID">Object ID that got paid</param>
|
||||
/// <param name="agentID">Agent Id that did the paying</param>
|
||||
/// <param name="amount">Amount paid</param>
|
||||
private void HandleObjectPaid(UUID objectID, UUID agentID,
|
||||
int amount)
|
||||
{
|
||||
// Add to queue for all scripts in ObjectID object
|
||||
DetectParams[] det = new DetectParams[1];
|
||||
det[0] = new DetectParams();
|
||||
det[0].Key = agentID;
|
||||
det[0].Populate(this.World);
|
||||
|
||||
// Since this is an event from a shared module, all scenes will
|
||||
// get it. But only one has the object in question. The others
|
||||
// just ignore it.
|
||||
//
|
||||
SceneObjectPart part =
|
||||
this.World.GetSceneObjectPart(objectID);
|
||||
|
||||
if (part == null)
|
||||
return;
|
||||
|
||||
if ((part.ScriptEvents & scriptEvents.money) == 0)
|
||||
part = part.ParentGroup.RootPart;
|
||||
|
||||
Verbose ("Paid: " + objectID + " from " + agentID + ", amount " + amount);
|
||||
|
||||
if (part != null)
|
||||
{
|
||||
money(part.LocalId, agentID, amount, det);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles piping the proper stuff to The script engine for touching
|
||||
/// Including DetectedParams
|
||||
/// </summary>
|
||||
/// <param name="localID"></param>
|
||||
/// <param name="originalID"></param>
|
||||
/// <param name="offsetPos"></param>
|
||||
/// <param name="remoteClient"></param>
|
||||
/// <param name="surfaceArgs"></param>
|
||||
public void touch_start(uint localID, uint originalID, Vector3 offsetPos,
|
||||
IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs)
|
||||
{
|
||||
touches(localID, originalID, offsetPos, remoteClient, surfaceArgs, "touch_start");
|
||||
}
|
||||
|
||||
public void touch(uint localID, uint originalID, Vector3 offsetPos,
|
||||
IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs)
|
||||
{
|
||||
touches(localID, originalID, offsetPos, remoteClient, surfaceArgs, "touch");
|
||||
}
|
||||
|
||||
private static Vector3 zeroVec3 = new Vector3(0,0,0);
|
||||
public void touch_end(uint localID, uint originalID, IClientAPI remoteClient,
|
||||
SurfaceTouchEventArgs surfaceArgs)
|
||||
{
|
||||
touches(localID, originalID, zeroVec3, remoteClient, surfaceArgs, "touch_end");
|
||||
}
|
||||
|
||||
private void touches(uint localID, uint originalID, Vector3 offsetPos,
|
||||
IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs, string eventname)
|
||||
{
|
||||
SceneObjectPart part;
|
||||
if (originalID == 0) {
|
||||
part = this.World.GetSceneObjectPart(localID);
|
||||
if (part == null) return;
|
||||
} else {
|
||||
part = this.World.GetSceneObjectPart(originalID);
|
||||
}
|
||||
|
||||
DetectParams det = new DetectParams();
|
||||
det.Key = remoteClient.AgentId;
|
||||
det.Populate(this.World);
|
||||
det.OffsetPos = new LSL_Vector(offsetPos.X,
|
||||
offsetPos.Y,
|
||||
offsetPos.Z);
|
||||
det.LinkNum = part.LinkNum;
|
||||
|
||||
if (surfaceArgs != null) {
|
||||
det.SurfaceTouchArgs = surfaceArgs;
|
||||
}
|
||||
|
||||
// Add to queue for all scripts in ObjectID object
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
eventname, oneObjectArrayOne,
|
||||
new DetectParams[] { det }));
|
||||
}
|
||||
|
||||
public void changed(uint localID, uint change)
|
||||
{
|
||||
int ch = (int)change;
|
||||
// Add to queue for all scripts in localID, Object pass change.
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
"changed",new object[] { ch },
|
||||
zeroDetectParams));
|
||||
}
|
||||
|
||||
// state_entry: not processed here
|
||||
// state_exit: not processed here
|
||||
|
||||
public void money(uint localID, UUID agentID, int amount, DetectParams[] det)
|
||||
{
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
"money", new object[] {
|
||||
agentID.ToString(),
|
||||
amount },
|
||||
det));
|
||||
}
|
||||
|
||||
public void collision_start(uint localID, ColliderArgs col)
|
||||
{
|
||||
collisions(localID, col, "collision_start");
|
||||
}
|
||||
|
||||
public void collision(uint localID, ColliderArgs col)
|
||||
{
|
||||
collisions(localID, col, "collision");
|
||||
}
|
||||
|
||||
public void collision_end(uint localID, ColliderArgs col)
|
||||
{
|
||||
collisions(localID, col, "collision_end");
|
||||
}
|
||||
|
||||
private void collisions(uint localID, ColliderArgs col, string eventname)
|
||||
{
|
||||
int dc = col.Colliders.Count;
|
||||
if (dc > 0) {
|
||||
DetectParams[] det = new DetectParams[dc];
|
||||
int i = 0;
|
||||
foreach (DetectedObject detobj in col.Colliders) {
|
||||
DetectParams d = new DetectParams();
|
||||
det[i++] = d;
|
||||
|
||||
d.Key = detobj.keyUUID;
|
||||
d.Populate (this.World);
|
||||
|
||||
/* not done by XEngine...
|
||||
d.Position = detobj.posVector;
|
||||
d.Rotation = detobj.rotQuat;
|
||||
d.Velocity = detobj.velVector;
|
||||
... */
|
||||
}
|
||||
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
eventname,
|
||||
new Object[] { dc },
|
||||
det));
|
||||
}
|
||||
}
|
||||
|
||||
public void land_collision_start(uint localID, ColliderArgs col)
|
||||
{
|
||||
land_collisions(localID, col, "land_collision_start");
|
||||
}
|
||||
|
||||
public void land_collision(uint localID, ColliderArgs col)
|
||||
{
|
||||
land_collisions(localID, col, "land_collision");
|
||||
}
|
||||
|
||||
public void land_collision_end(uint localID, ColliderArgs col)
|
||||
{
|
||||
land_collisions(localID, col, "land_collision_end");
|
||||
}
|
||||
|
||||
private void land_collisions(uint localID, ColliderArgs col, string eventname)
|
||||
{
|
||||
foreach (DetectedObject detobj in col.Colliders) {
|
||||
LSL_Vector vec = new LSL_Vector(detobj.posVector.X,
|
||||
detobj.posVector.Y,
|
||||
detobj.posVector.Z);
|
||||
EventParams eps = new EventParams(eventname,
|
||||
new Object[] { vec },
|
||||
zeroDetectParams);
|
||||
this.PostObjectEvent(localID, eps);
|
||||
}
|
||||
}
|
||||
|
||||
// timer: not handled here
|
||||
// listen: not handled here
|
||||
|
||||
public void control(UUID itemID, UUID agentID, uint held, uint change)
|
||||
{
|
||||
this.PostScriptEvent(itemID, new EventParams(
|
||||
"control",new object[] {
|
||||
agentID.ToString(),
|
||||
(int)held,
|
||||
(int)change},
|
||||
zeroDetectParams));
|
||||
}
|
||||
|
||||
public void email(uint localID, UUID itemID, string timeSent,
|
||||
string address, string subject, string message, int numLeft)
|
||||
{
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
"email",new object[] {
|
||||
timeSent,
|
||||
address,
|
||||
subject,
|
||||
message,
|
||||
numLeft},
|
||||
zeroDetectParams));
|
||||
}
|
||||
|
||||
public void at_target(uint localID, uint handle, Vector3 targetpos,
|
||||
Vector3 atpos)
|
||||
{
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
"at_target", new object[] {
|
||||
(int)handle,
|
||||
new LSL_Vector(targetpos.X,targetpos.Y,targetpos.Z),
|
||||
new LSL_Vector(atpos.X,atpos.Y,atpos.Z) },
|
||||
zeroDetectParams));
|
||||
}
|
||||
|
||||
public void not_at_target(uint localID)
|
||||
{
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
"not_at_target",zeroObjectArray,
|
||||
zeroDetectParams));
|
||||
}
|
||||
|
||||
public void at_rot_target(uint localID, uint handle, OpenMetaverse.Quaternion targetrot, OpenMetaverse.Quaternion atrot)
|
||||
{
|
||||
this.PostObjectEvent(
|
||||
localID,
|
||||
new EventParams(
|
||||
"at_rot_target",
|
||||
new object[] {
|
||||
new LSL_Integer(handle),
|
||||
new LSL_Rotation(targetrot.X, targetrot.Y, targetrot.Z, targetrot.W),
|
||||
new LSL_Rotation(atrot.X, atrot.Y, atrot.Z, atrot.W)
|
||||
},
|
||||
zeroDetectParams
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public void not_at_rot_target(uint localID)
|
||||
{
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
"not_at_rot_target",zeroObjectArray,
|
||||
zeroDetectParams));
|
||||
}
|
||||
|
||||
// run_time_permissions: not handled here
|
||||
|
||||
public void attach(uint localID, UUID itemID, UUID avatar)
|
||||
{
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
"attach",new object[] {
|
||||
avatar.ToString() },
|
||||
zeroDetectParams));
|
||||
}
|
||||
|
||||
// dataserver: not handled here
|
||||
// link_message: not handled here
|
||||
|
||||
public void moving_start(uint localID)
|
||||
{
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
"moving_start",zeroObjectArray,
|
||||
zeroDetectParams));
|
||||
}
|
||||
|
||||
public void moving_end(uint localID)
|
||||
{
|
||||
this.PostObjectEvent(localID, new EventParams(
|
||||
"moving_end",zeroObjectArray,
|
||||
zeroDetectParams));
|
||||
}
|
||||
|
||||
// object_rez: not handled here
|
||||
// remote_data: not handled here
|
||||
// http_response: not handled here
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* 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 OpenSim.Region.ScriptEngine.XMREngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
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 HeapTrackerBase {
|
||||
private int usage;
|
||||
private XMRInstAbstract instance;
|
||||
|
||||
public HeapTrackerBase (XMRInstAbstract inst)
|
||||
{
|
||||
if (inst == null) throw new ArgumentNullException ("inst");
|
||||
instance = inst;
|
||||
}
|
||||
|
||||
~HeapTrackerBase ()
|
||||
{
|
||||
usage = instance.UpdateHeapUse (usage, 0);
|
||||
}
|
||||
|
||||
protected void NewUse (int newuse)
|
||||
{
|
||||
usage = instance.UpdateHeapUse (usage, newuse);
|
||||
}
|
||||
}
|
||||
|
||||
public class HeapTrackerList : HeapTrackerBase {
|
||||
private LSL_List value;
|
||||
|
||||
public HeapTrackerList (XMRInstAbstract inst) : base (inst) { }
|
||||
|
||||
public void Pop (LSL_List lis)
|
||||
{
|
||||
NewUse (Size (lis));
|
||||
value = lis;
|
||||
}
|
||||
|
||||
public LSL_List Push ()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int Size (LSL_List lis)
|
||||
{
|
||||
return (!typeof (LSL_List).IsValueType && (lis == null)) ? 0 : lis.Size;
|
||||
}
|
||||
}
|
||||
|
||||
public class HeapTrackerObject : HeapTrackerBase {
|
||||
public const int HT_CHAR = 2;
|
||||
public const int HT_DELE = 8;
|
||||
public const int HT_DOUB = 8;
|
||||
public const int HT_SING = 4;
|
||||
public const int HT_SFLT = 4;
|
||||
public const int HT_INT = 4;
|
||||
public const int HT_VEC = HT_DOUB * 3;
|
||||
public const int HT_ROT = HT_DOUB * 4;
|
||||
|
||||
private object value;
|
||||
|
||||
public HeapTrackerObject (XMRInstAbstract inst) : base (inst) { }
|
||||
|
||||
public void Pop (object obj)
|
||||
{
|
||||
NewUse (Size (obj));
|
||||
value = obj;
|
||||
}
|
||||
|
||||
public object Push ()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int Size (object obj)
|
||||
{
|
||||
if (obj == null) return 0;
|
||||
|
||||
if (obj is char) return HT_CHAR;
|
||||
if (obj is Delegate) return HT_DELE;
|
||||
if (obj is double) return HT_DOUB;
|
||||
if (obj is float) return HT_SING;
|
||||
if (obj is int) return HT_INT;
|
||||
if (obj is LSL_Float) return HT_SFLT;
|
||||
if (obj is LSL_Integer) return HT_INT;
|
||||
if (obj is LSL_List) return ((LSL_List)obj).Size;
|
||||
if (obj is LSL_Rotation) return HT_ROT;
|
||||
if (obj is LSL_String) return ((LSL_String)obj).m_string.Length * HT_CHAR;
|
||||
if (obj is LSL_Vector) return HT_VEC;
|
||||
if (obj is string) return ((string)obj).Length * HT_CHAR;
|
||||
if (obj is XMR_Array) return 0;
|
||||
if (obj is XMRArrayListKey) return ((XMRArrayListKey)obj).Size;
|
||||
if (obj is XMRSDTypeClObj) return 0;
|
||||
|
||||
if (obj is Array) {
|
||||
Array ar = (Array)obj;
|
||||
int len = ar.Length;
|
||||
if (len == 0) return 0;
|
||||
Type et = ar.GetType ().GetElementType ();
|
||||
if (et.IsValueType) return Size (ar.GetValue (0)) * len;
|
||||
int size = 0;
|
||||
for (int i = 0; i < len; i ++) {
|
||||
size += Size (ar.GetValue (i));
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
throw new Exception ("unknown size of type " + obj.GetType ().Name);
|
||||
}
|
||||
}
|
||||
|
||||
public class HeapTrackerString : HeapTrackerBase {
|
||||
private string value;
|
||||
|
||||
public HeapTrackerString (XMRInstAbstract inst) : base (inst) { }
|
||||
|
||||
public void Pop (string str)
|
||||
{
|
||||
NewUse (Size (str));
|
||||
value = str;
|
||||
}
|
||||
|
||||
public string Push ()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int Size (string str)
|
||||
{
|
||||
return (str == null) ? 0 : str.Length * HeapTrackerObject.HT_CHAR;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,644 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Remoting.Lifetime;
|
||||
using System.Security.Policy;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.ScriptEngine.Interfaces;
|
||||
using OpenSim.Region.ScriptEngine.Shared;
|
||||
using OpenSim.Region.ScriptEngine.Shared.Api;
|
||||
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
|
||||
using OpenSim.Region.ScriptEngine.XMREngine;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.Framework.Scenes.Scripting;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using log4net;
|
||||
|
||||
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
|
||||
{
|
||||
/****************************************************\
|
||||
* This file contains routines called by scripts. *
|
||||
\****************************************************/
|
||||
|
||||
public class XMRLSL_Api : LSL_Api
|
||||
{
|
||||
public AsyncCommandManager acm;
|
||||
private XMRInstance inst;
|
||||
|
||||
public void InitXMRLSLApi(XMRInstance i)
|
||||
{
|
||||
acm = AsyncCommands;
|
||||
inst = i;
|
||||
}
|
||||
|
||||
protected override void ScriptSleep(int ms)
|
||||
{
|
||||
inst.Sleep(ms);
|
||||
}
|
||||
|
||||
public override void llSleep(double sec)
|
||||
{
|
||||
inst.Sleep((int)(sec * 1000.0));
|
||||
}
|
||||
|
||||
public override void llDie()
|
||||
{
|
||||
inst.Die();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Seat avatar on prim.
|
||||
* @param owner = true: owner of prim script is running in
|
||||
* false: avatar that has given ANIMATION permission on the prim
|
||||
* @returns 0: successful
|
||||
* -1: no permission to animate
|
||||
* -2: no av granted perms
|
||||
* -3: av not in region
|
||||
*/
|
||||
public int xmrSeatAvatar (bool owner)
|
||||
{
|
||||
// Get avatar to be seated and make sure they have given us ANIMATION permission
|
||||
|
||||
UUID avuuid;
|
||||
if (owner) {
|
||||
avuuid = inst.m_Part.OwnerID;
|
||||
} else {
|
||||
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION) == 0) {
|
||||
return -1;
|
||||
}
|
||||
avuuid = m_item.PermsGranter;
|
||||
}
|
||||
if (avuuid == UUID.Zero) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
ScenePresence presence = World.GetScenePresence (avuuid);
|
||||
if (presence == null) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
// remoteClient = not used by ScenePresence.HandleAgentRequestSit()
|
||||
// agentID = not used by ScenePresence.HandleAgentRequestSit()
|
||||
// targetID = UUID of prim to sit on
|
||||
// offset = offset of sitting position
|
||||
|
||||
presence.HandleAgentRequestSit (null, UUID.Zero, m_host.UUID, OpenMetaverse.Vector3.Zero);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief llTeleportAgent() is broken in that if you pass it a landmark,
|
||||
* it still subjects the position to spawn points, as it always
|
||||
* calls RequestTeleportLocation() with TeleportFlags.ViaLocation.
|
||||
* See llTeleportAgent() and CheckAndAdjustTelehub().
|
||||
*
|
||||
* @param agent = what agent to teleport
|
||||
* @param landmark = inventory name or UUID of a landmark object
|
||||
* @param lookat = looking direction after teleport
|
||||
*/
|
||||
public void xmrTeleportAgent2Landmark (string agent, string landmark, LSL_Vector lookat)
|
||||
{
|
||||
// find out about agent to be teleported
|
||||
UUID agentId;
|
||||
if (!UUID.TryParse (agent, out agentId)) throw new ApplicationException ("bad agent uuid");
|
||||
|
||||
ScenePresence presence = World.GetScenePresence (agentId);
|
||||
if (presence == null) throw new ApplicationException ("agent not present in scene");
|
||||
if (presence.IsNPC) throw new ApplicationException ("agent is an NPC");
|
||||
if (presence.IsGod) throw new ApplicationException ("agent is a god");
|
||||
|
||||
// prim must be owned by land owner or prim must be attached to agent
|
||||
if (m_host.ParentGroup.AttachmentPoint == 0) {
|
||||
if (m_host.OwnerID != World.LandChannel.GetLandObject (presence.AbsolutePosition).LandData.OwnerID) {
|
||||
throw new ApplicationException ("prim not owned by land's owner");
|
||||
}
|
||||
} else {
|
||||
if (m_host.OwnerID != presence.UUID) throw new ApplicationException ("prim not attached to agent");
|
||||
}
|
||||
|
||||
// find landmark in inventory or by UUID
|
||||
UUID assetID = ScriptUtils.GetAssetIdFromKeyOrItemName (m_host, landmark);
|
||||
if (assetID == UUID.Zero) throw new ApplicationException ("no such landmark");
|
||||
|
||||
// read it in and make sure it is a landmark
|
||||
AssetBase lma = World.AssetService.Get (assetID.ToString ());
|
||||
if ((lma == null) || (lma.Type != (sbyte)AssetType.Landmark)) throw new ApplicationException ("not a landmark");
|
||||
|
||||
// parse the record
|
||||
AssetLandmark lm = new AssetLandmark (lma);
|
||||
|
||||
// the regionhandle (based on region's world X,Y) might be out of date
|
||||
// re-read the handle so we can pass it to RequestTeleportLocation()
|
||||
var region = World.GridService.GetRegionByUUID (World.RegionInfo.ScopeID, lm.RegionID);
|
||||
if (region == null) throw new ApplicationException ("no such region");
|
||||
|
||||
// finally ready to teleport
|
||||
World.RequestTeleportLocation (presence.ControllingClient,
|
||||
region.RegionHandle,
|
||||
lm.Position,
|
||||
lookat,
|
||||
(uint)TeleportFlags.ViaLandmark);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allow any member of group given by config SetParcelMusicURLGroup to set music URL.
|
||||
* Code modelled after llSetParcelMusicURL().
|
||||
* @param newurl = new URL to set (or "" to leave it alone)
|
||||
* @returns previous URL string
|
||||
*/
|
||||
public string xmrSetParcelMusicURLGroup (string newurl)
|
||||
{
|
||||
string groupname = m_ScriptEngine.Config.GetString ("SetParcelMusicURLGroup", "");
|
||||
if (groupname == "") throw new ApplicationException ("no SetParcelMusicURLGroup config param set");
|
||||
|
||||
IGroupsModule igm = World.RequestModuleInterface<IGroupsModule> ();
|
||||
if (igm == null) throw new ApplicationException ("no GroupsModule loaded");
|
||||
|
||||
GroupRecord grouprec = igm.GetGroupRecord (groupname);
|
||||
if (grouprec == null) throw new ApplicationException ("no such group " + groupname);
|
||||
|
||||
GroupMembershipData gmd = igm.GetMembershipData (grouprec.GroupID, m_host.OwnerID);
|
||||
if (gmd == null) throw new ApplicationException ("not a member of group " + groupname);
|
||||
|
||||
ILandObject land = World.LandChannel.GetLandObject (m_host.AbsolutePosition);
|
||||
if (land == null) throw new ApplicationException ("no land at " + m_host.AbsolutePosition.ToString ());
|
||||
string oldurl = land.GetMusicUrl ();
|
||||
if (oldurl == null) oldurl = "";
|
||||
if ((newurl != null) && (newurl != "")) land.SetMusicUrl (newurl);
|
||||
return oldurl;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class XMRInstance
|
||||
{
|
||||
/**
|
||||
* @brief The script is calling llReset().
|
||||
* We throw an exception to unwind the script out to its main
|
||||
* causing all the finally's to execute and it will also set
|
||||
* eventCode = None to indicate event handler has completed.
|
||||
*/
|
||||
public void ApiReset()
|
||||
{
|
||||
ClearQueueExceptLinkMessages();
|
||||
throw new ScriptResetException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The script is calling one of the llDetected...(int number)
|
||||
* functions. Return corresponding DetectParams pointer.
|
||||
*/
|
||||
public DetectParams GetDetectParams(int number)
|
||||
{
|
||||
DetectParams dp = null;
|
||||
if ((number >= 0) && (m_DetectParams != null) && (number < m_DetectParams.Length)) {
|
||||
dp = m_DetectParams[number];
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Script is calling llDie, so flag the run loop to delete script
|
||||
* once we are off the microthread stack, and throw an exception
|
||||
* to unwind the stack asap.
|
||||
*/
|
||||
public void Die()
|
||||
{
|
||||
// llDie doesn't work in attachments!
|
||||
if (m_Part.ParentGroup.IsAttachment || m_DetachQuantum > 0)
|
||||
return;
|
||||
|
||||
throw new ScriptDieException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called by script to sleep for the given number of milliseconds.
|
||||
*/
|
||||
public void Sleep(int ms)
|
||||
{
|
||||
lock (m_QueueLock) {
|
||||
|
||||
/*
|
||||
* Say how long to sleep.
|
||||
*/
|
||||
m_SleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds(ms);
|
||||
|
||||
/*
|
||||
* Don't wake on any events.
|
||||
*/
|
||||
m_SleepEventMask1 = 0;
|
||||
m_SleepEventMask2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The compiler follows all calls to llSleep() with a call to CheckRun().
|
||||
* So tell CheckRun() to suspend the microthread.
|
||||
*/
|
||||
suspendOnCheckRunTemp = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block script execution until an event is queued or a timeout is reached.
|
||||
* @param timeout = maximum number of seconds to wait
|
||||
* @param returnMask = if event is queued that matches these mask bits,
|
||||
* the script is woken, that event is dequeued and
|
||||
* returned to the caller. The event handler is not
|
||||
* executed.
|
||||
* @param backgroundMask = if any of these events are queued while waiting,
|
||||
* execute their event handlers. When any such event
|
||||
* handler exits, continue waiting for events or the
|
||||
* timeout.
|
||||
* @returns empty list: no event was queued that matched returnMask and the timeout was reached
|
||||
* or a background event handler changed state (eg, via 'state' statement)
|
||||
* else: list giving parameters of the event:
|
||||
* [0] = event code (integer)
|
||||
* [1..n] = call parameters to the event, if any
|
||||
* Notes:
|
||||
* 1) Scrips should use XMREVENTMASKn_<eventname> symbols for the mask arguments,
|
||||
* where n is 1 or 2 for mask1 or mask2 arguments.
|
||||
* The list[0] return argument can be decoded by using XMREVENTCODE_<eventname> symbols.
|
||||
* 2) If all masks are zero, the call ends up acting like llSleep.
|
||||
* 3) If an event is enabled in both returnMask and backgroundMask, the returnMask bit
|
||||
* action takes precedence, ie, the event is returned. This allows a simple specification
|
||||
* of -1 for both backgroundMask arguments to indicate that all events not listed in
|
||||
* the returnMask argumetns should be handled in the background.
|
||||
* 4) Any events not listed in either returnMask or backgroundMask arguments will be
|
||||
* queued for later processing (subject to normal queue limits).
|
||||
* 5) Background event handlers execute as calls from within xmrEventDequeue, they do
|
||||
* not execute as separate threads. Thus any background event handlers must return
|
||||
* before the call to xmrEventDequeue will return.
|
||||
* 6) If a background event handler changes state (eg, via 'state' statement), the state
|
||||
* is immediately changed and the script-level xmrEventDequeue call does not return.
|
||||
* 7) For returned events, the detect parameters are overwritten by the returned event.
|
||||
* For background events, the detect parameters are saved and restored.
|
||||
* 8) Scripts must contain dummy event handler definitions for any event types that may
|
||||
* be returned by xmrEventDequeue, to let the runtime know that the script is capable
|
||||
* of processing that event type. Otherwise, the event may not be queued to the script.
|
||||
*/
|
||||
private static LSL_List emptyList = new LSL_List (new object[0]);
|
||||
|
||||
public override LSL_List xmrEventDequeue (double timeout, int returnMask1, int returnMask2,
|
||||
int backgroundMask1, int backgroundMask2)
|
||||
{
|
||||
DateTime sleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds (timeout * 1000.0);
|
||||
EventParams evt = null;
|
||||
int callNo, evc2;
|
||||
int evc1 = 0;
|
||||
int mask1 = returnMask1 | backgroundMask1; // codes 00..31
|
||||
int mask2 = returnMask2 | backgroundMask2; // codes 32..63
|
||||
LinkedListNode<EventParams> lln = null;
|
||||
object[] sv;
|
||||
ScriptEventCode evc = ScriptEventCode.None;
|
||||
|
||||
callNo = -1;
|
||||
try {
|
||||
if (callMode == CallMode_NORMAL) goto findevent;
|
||||
|
||||
/*
|
||||
* Stack frame is being restored as saved via CheckRun...().
|
||||
* Restore necessary values then jump to __call<n> label to resume processing.
|
||||
*/
|
||||
sv = RestoreStackFrame ("xmrEventDequeue", out callNo);
|
||||
sleepUntil = DateTime.Parse ((string)sv[0]);
|
||||
returnMask1 = (int)sv[1];
|
||||
returnMask2 = (int)sv[2];
|
||||
mask1 = (int)sv[3];
|
||||
mask2 = (int)sv[4];
|
||||
switch (callNo) {
|
||||
case 0: goto __call0;
|
||||
case 1: {
|
||||
evc1 = (int)sv[5];
|
||||
evc = (ScriptEventCode)(int)sv[6];
|
||||
DetectParams[] detprms = ObjArrToDetPrms ((object[])sv[7]);
|
||||
object[] ehargs = (object[])sv[8];
|
||||
evt = new EventParams (evc.ToString (), ehargs, detprms);
|
||||
goto __call1;
|
||||
}
|
||||
}
|
||||
throw new ScriptBadCallNoException (callNo);
|
||||
|
||||
/*
|
||||
* Find first event that matches either the return or background masks.
|
||||
*/
|
||||
findevent:
|
||||
Monitor.Enter (m_QueueLock);
|
||||
for (lln = m_EventQueue.First; lln != null; lln = lln.Next) {
|
||||
evt = lln.Value;
|
||||
evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), evt.EventName);
|
||||
evc1 = (int)evc;
|
||||
evc2 = evc1 - 32;
|
||||
if ((((uint)evc1 < (uint)32) && (((mask1 >> evc1) & 1) != 0)) ||
|
||||
(((uint)evc2 < (uint)32) && (((mask2 >> evc2) & 1) != 0))) goto remfromq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nothing found, sleep while one comes in.
|
||||
*/
|
||||
m_SleepUntil = sleepUntil;
|
||||
m_SleepEventMask1 = mask1;
|
||||
m_SleepEventMask2 = mask2;
|
||||
Monitor.Exit (m_QueueLock);
|
||||
suspendOnCheckRunTemp = true;
|
||||
callNo = 0;
|
||||
__call0:
|
||||
CheckRunQuick ();
|
||||
goto checktmo;
|
||||
|
||||
/*
|
||||
* Found one, remove it from queue.
|
||||
*/
|
||||
remfromq:
|
||||
m_EventQueue.Remove (lln);
|
||||
if ((uint)evc1 < (uint)m_EventCounts.Length) {
|
||||
m_EventCounts[evc1] --;
|
||||
}
|
||||
Monitor.Exit (m_QueueLock);
|
||||
m_InstEHEvent ++;
|
||||
|
||||
/*
|
||||
* See if returnable or background event.
|
||||
*/
|
||||
if ((((uint)evc1 < (uint)32) && (((returnMask1 >> evc1) & 1) != 0)) ||
|
||||
(((uint)evc2 < (uint)32) && (((returnMask2 >> evc2) & 1) != 0))) {
|
||||
|
||||
/*
|
||||
* Returnable event, return its parameters in a list.
|
||||
* Also set the detect parameters to what the event has.
|
||||
*/
|
||||
int plen = evt.Params.Length;
|
||||
object[] plist = new object[plen+1];
|
||||
plist[0] = (LSL_Integer)evc1;
|
||||
for (int i = 0; i < plen;) {
|
||||
object ob = evt.Params[i];
|
||||
if (ob is int) ob = (LSL_Integer)(int)ob;
|
||||
else if (ob is double) ob = (LSL_Float)(double)ob;
|
||||
else if (ob is string) ob = (LSL_String)(string)ob;
|
||||
plist[++i] = ob;
|
||||
}
|
||||
m_DetectParams = evt.DetectParams;
|
||||
return new LSL_List (plist);
|
||||
}
|
||||
|
||||
/*
|
||||
* It is a background event, simply call its event handler,
|
||||
* then check event queue again.
|
||||
*/
|
||||
callNo = 1;
|
||||
__call1:
|
||||
ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode,evc1];
|
||||
if (seh == null) goto checktmo;
|
||||
|
||||
DetectParams[] saveDetParams = this.m_DetectParams;
|
||||
object[] saveEHArgs = this.ehArgs;
|
||||
ScriptEventCode saveEventCode = this.eventCode;
|
||||
|
||||
this.m_DetectParams = evt.DetectParams;
|
||||
this.ehArgs = evt.Params;
|
||||
this.eventCode = evc;
|
||||
|
||||
try {
|
||||
seh (this);
|
||||
} finally {
|
||||
this.m_DetectParams = saveDetParams;
|
||||
this.ehArgs = saveEHArgs;
|
||||
this.eventCode = saveEventCode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep waiting until we find a returnable event or timeout.
|
||||
*/
|
||||
checktmo:
|
||||
if (DateTime.UtcNow < sleepUntil) goto findevent;
|
||||
|
||||
/*
|
||||
* We timed out, return an empty list.
|
||||
*/
|
||||
return emptyList;
|
||||
} finally {
|
||||
if (callMode != CallMode_NORMAL) {
|
||||
|
||||
/*
|
||||
* Stack frame is being saved by CheckRun...().
|
||||
* Save everything we need at the __call<n> labels so we can restore it
|
||||
* when we need to.
|
||||
*/
|
||||
sv = CaptureStackFrame ("xmrEventDequeue", callNo, 9);
|
||||
sv[0] = sleepUntil.ToString (); // needed at __call0,__call1
|
||||
sv[1] = returnMask1; // needed at __call0,__call1
|
||||
sv[2] = returnMask2; // needed at __call0,__call1
|
||||
sv[3] = mask1; // needed at __call0,__call1
|
||||
sv[4] = mask2; // needed at __call0,__call1
|
||||
if (callNo == 1) {
|
||||
sv[5] = evc1; // needed at __call1
|
||||
sv[6] = (int)evc; // needed at __call1
|
||||
sv[7] = DetPrmsToObjArr (evt.DetectParams); // needed at __call1
|
||||
sv[8] = evt.Params; // needed at __call1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enqueue an event
|
||||
* @param ev = as returned by xmrEventDequeue saying which event type to queue
|
||||
* 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 override void xmrEventEnqueue (LSL_List ev)
|
||||
{
|
||||
object[] data = ev.Data;
|
||||
ScriptEventCode evc = (ScriptEventCode)ListInt (data[0]);
|
||||
|
||||
int nargs = data.Length - 1;
|
||||
object[] args = new object[nargs];
|
||||
Array.Copy (data, 1, args, 0, nargs);
|
||||
|
||||
PostEvent (new EventParams (evc.ToString (), args, m_DetectParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save current detect params into a list
|
||||
* @returns a list containing current detect param values
|
||||
*/
|
||||
private const int saveDPVer = 1;
|
||||
|
||||
public override LSL_List xmrEventSaveDets ()
|
||||
{
|
||||
object[] obs = DetPrmsToObjArr (m_DetectParams);
|
||||
return new LSL_List (obs);
|
||||
}
|
||||
|
||||
private static object[] DetPrmsToObjArr (DetectParams[] dps)
|
||||
{
|
||||
int len = dps.Length;
|
||||
object[] obs = new object[len*16+1];
|
||||
int j = 0;
|
||||
obs[j++] = (LSL_Integer)saveDPVer;
|
||||
for (int i = 0; i < len; i ++) {
|
||||
DetectParams dp = dps[i];
|
||||
obs[j++] = (LSL_String)dp.Key.ToString(); // UUID
|
||||
obs[j++] = dp.OffsetPos; // vector
|
||||
obs[j++] = (LSL_Integer)dp.LinkNum; // integer
|
||||
obs[j++] = (LSL_String)dp.Group.ToString(); // UUID
|
||||
obs[j++] = (LSL_String)dp.Name; // string
|
||||
obs[j++] = (LSL_String)dp.Owner.ToString(); // UUID
|
||||
obs[j++] = dp.Position; // vector
|
||||
obs[j++] = dp.Rotation; // rotation
|
||||
obs[j++] = (LSL_Integer)dp.Type; // integer
|
||||
obs[j++] = dp.Velocity; // vector
|
||||
obs[j++] = dp.TouchST; // vector
|
||||
obs[j++] = dp.TouchNormal; // vector
|
||||
obs[j++] = dp.TouchBinormal; // vector
|
||||
obs[j++] = dp.TouchPos; // vector
|
||||
obs[j++] = dp.TouchUV; // vector
|
||||
obs[j++] = (LSL_Integer)dp.TouchFace; // integer
|
||||
}
|
||||
return obs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Load current detect params from a list
|
||||
* @param dpList = as returned by xmrEventSaveDets()
|
||||
*/
|
||||
public override void xmrEventLoadDets (LSL_List dpList)
|
||||
{
|
||||
m_DetectParams = ObjArrToDetPrms (dpList.Data);
|
||||
}
|
||||
|
||||
private static DetectParams[] ObjArrToDetPrms (object[] objs)
|
||||
{
|
||||
int j = 0;
|
||||
if ((objs.Length % 16 != 1) || (ListInt (objs[j++]) != saveDPVer)) {
|
||||
throw new Exception ("invalid detect param format");
|
||||
}
|
||||
|
||||
int len = objs.Length / 16;
|
||||
DetectParams[] dps = new DetectParams[len];
|
||||
|
||||
for (int i = 0; i < len; i ++) {
|
||||
DetectParams dp = new DetectParams ();
|
||||
|
||||
dp.Key = new UUID (ListStr (objs[j++]));
|
||||
dp.OffsetPos = (LSL_Vector)objs[j++];
|
||||
dp.LinkNum = ListInt (objs[j++]);
|
||||
dp.Group = new UUID (ListStr (objs[j++]));
|
||||
dp.Name = ListStr (objs[j++]);
|
||||
dp.Owner = new UUID (ListStr (objs[j++]));
|
||||
dp.Position = (LSL_Vector)objs[j++];
|
||||
dp.Rotation = (LSL_Rotation)objs[j++];
|
||||
dp.Type = ListInt (objs[j++]);
|
||||
dp.Velocity = (LSL_Vector)objs[j++];
|
||||
|
||||
SurfaceTouchEventArgs stea = new SurfaceTouchEventArgs ();
|
||||
|
||||
stea.STCoord = LSLVec2OMVec ((LSL_Vector)objs[j++]);
|
||||
stea.Normal = LSLVec2OMVec ((LSL_Vector)objs[j++]);
|
||||
stea.Binormal = LSLVec2OMVec ((LSL_Vector)objs[j++]);
|
||||
stea.Position = LSLVec2OMVec ((LSL_Vector)objs[j++]);
|
||||
stea.UVCoord = LSLVec2OMVec ((LSL_Vector)objs[j++]);
|
||||
stea.FaceIndex = ListInt (objs[j++]);
|
||||
|
||||
dp.SurfaceTouchArgs = stea;
|
||||
|
||||
dps[i] = dp;
|
||||
}
|
||||
|
||||
return dps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The script is executing a 'state <newState>;' command.
|
||||
* Tell outer layers to cancel any event triggers, like llListen(),
|
||||
* then tell outer layers which events the new state has handlers for.
|
||||
* We also clear the event queue as per http://wiki.secondlife.com/wiki/State
|
||||
*/
|
||||
public override void StateChange()
|
||||
{
|
||||
/*
|
||||
* Cancel any llListen()s etc.
|
||||
* But llSetTimerEvent() should persist.
|
||||
*/
|
||||
object[] timers = m_XMRLSLApi.acm.TimerPlugin.GetSerializationData(m_ItemID);
|
||||
AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
|
||||
m_XMRLSLApi.acm.TimerPlugin.CreateFromData(m_LocalID, m_ItemID, UUID.Zero, timers);
|
||||
|
||||
/*
|
||||
* Tell whoever cares which event handlers the new state has.
|
||||
*/
|
||||
m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(stateCode));
|
||||
|
||||
/*
|
||||
* Clear out any old events from the queue.
|
||||
*/
|
||||
lock (m_QueueLock) {
|
||||
m_EventQueue.Clear();
|
||||
for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Script is calling xmrStackLeft().
|
||||
*/
|
||||
public override int xmrStackLeft ()
|
||||
{
|
||||
return microthread.StackLeft ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Thrown by things like llResetScript() to unconditionally
|
||||
* unwind as script and reset it to the default state_entry
|
||||
* handler. We don't want script-level try/catch to intercept
|
||||
* these so scripts can't interfere with the behavior.
|
||||
*/
|
||||
public class ScriptResetException : Exception, IXMRUncatchable { }
|
||||
|
||||
/**
|
||||
* @brief Thrown by things like llDie() to unconditionally unwind as
|
||||
* script. We don't want script-level try/catch to intercept
|
||||
* these so scripts can't interfere with the behavior.
|
||||
*/
|
||||
public class ScriptDieException : Exception, IXMRUncatchable { }
|
||||
}
|
|
@ -0,0 +1,436 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Remoting.Lifetime;
|
||||
using System.Security.Policy;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.ScriptEngine.Interfaces;
|
||||
using OpenSim.Region.ScriptEngine.Shared;
|
||||
using OpenSim.Region.ScriptEngine.Shared.Api;
|
||||
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
|
||||
using OpenSim.Region.ScriptEngine.XMREngine;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using log4net;
|
||||
|
||||
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 partial class XMRInstance
|
||||
{
|
||||
/********************************************************************************\
|
||||
* The only method of interest to outside this module is GetExecutionState() *
|
||||
* which captures the current state of the script into an XML document. *
|
||||
* *
|
||||
* The rest of this module contains support routines for GetExecutionState(). *
|
||||
\********************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Create an XML element that gives the current state of the script.
|
||||
* <ScriptState Engine="XMREngine" SourceHash=m_ObjCode.sourceHash Asset=m_Item.AssetID>
|
||||
* <Snapshot>globalsandstackdump</Snapshot>
|
||||
* <Running>m_Running</Running>
|
||||
* <DetectArray ...
|
||||
* <EventQueue ...
|
||||
* <Permissions ...
|
||||
* <Plugins />
|
||||
* </ScriptState>
|
||||
* Updates the .state file while we're at it.
|
||||
*/
|
||||
public XmlElement GetExecutionState(XmlDocument doc)
|
||||
{
|
||||
/*
|
||||
* When we're detaching an attachment, we need to wait here.
|
||||
*/
|
||||
|
||||
// Change this to a 5 second timeout. If things do mess up,
|
||||
// we don't want to be stuck forever.
|
||||
//
|
||||
m_DetachReady.WaitOne (5000, false);
|
||||
|
||||
XmlElement scriptStateN = doc.CreateElement("", "ScriptState", "");
|
||||
scriptStateN.SetAttribute("Engine", m_Engine.ScriptEngineName);
|
||||
scriptStateN.SetAttribute("Asset", m_Item.AssetID.ToString());
|
||||
scriptStateN.SetAttribute ("SourceHash", m_ObjCode.sourceHash);
|
||||
|
||||
/*
|
||||
* Make sure we aren't executing part of the script so it stays
|
||||
* stable. Setting suspendOnCheckRun tells CheckRun() to suspend
|
||||
* and return out so RunOne() will release the lock asap.
|
||||
*/
|
||||
suspendOnCheckRunHold = true;
|
||||
lock (m_RunLock)
|
||||
{
|
||||
m_RunOnePhase = "GetExecutionState enter";
|
||||
CheckRunLockInvariants(true);
|
||||
|
||||
/*
|
||||
* Get copy of script globals and stack in relocateable form.
|
||||
*/
|
||||
MemoryStream snapshotStream = new MemoryStream();
|
||||
MigrateOutEventHandler(snapshotStream);
|
||||
Byte[] snapshotBytes = snapshotStream.ToArray();
|
||||
snapshotStream.Close();
|
||||
string snapshotString = Convert.ToBase64String(snapshotBytes);
|
||||
XmlElement snapshotN = doc.CreateElement("", "Snapshot", "");
|
||||
snapshotN.AppendChild(doc.CreateTextNode(snapshotString));
|
||||
scriptStateN.AppendChild(snapshotN);
|
||||
m_RunOnePhase = "GetExecutionState B"; CheckRunLockInvariants(true);
|
||||
|
||||
/*
|
||||
* "Running" says whether or not we are accepting new events.
|
||||
*/
|
||||
XmlElement runningN = doc.CreateElement("", "Running", "");
|
||||
runningN.AppendChild(doc.CreateTextNode(m_Running.ToString()));
|
||||
scriptStateN.AppendChild(runningN);
|
||||
m_RunOnePhase = "GetExecutionState C"; CheckRunLockInvariants(true);
|
||||
|
||||
/*
|
||||
* "DoGblInit" says whether or not default:state_entry() will init global vars.
|
||||
*/
|
||||
XmlElement doGblInitN = doc.CreateElement("", "DoGblInit", "");
|
||||
doGblInitN.AppendChild(doc.CreateTextNode(doGblInit.ToString()));
|
||||
scriptStateN.AppendChild(doGblInitN);
|
||||
m_RunOnePhase = "GetExecutionState D"; CheckRunLockInvariants(true);
|
||||
|
||||
/*
|
||||
* More misc data.
|
||||
*/
|
||||
XmlNode permissionsN = doc.CreateElement("", "Permissions", "");
|
||||
scriptStateN.AppendChild(permissionsN);
|
||||
|
||||
XmlAttribute granterA = doc.CreateAttribute("", "granter", "");
|
||||
granterA.Value = m_Item.PermsGranter.ToString();
|
||||
permissionsN.Attributes.Append(granterA);
|
||||
|
||||
XmlAttribute maskA = doc.CreateAttribute("", "mask", "");
|
||||
maskA.Value = m_Item.PermsMask.ToString();
|
||||
permissionsN.Attributes.Append(maskA);
|
||||
m_RunOnePhase = "GetExecutionState E"; CheckRunLockInvariants(true);
|
||||
|
||||
/*
|
||||
* "DetectParams" are returned by llDetected...() script functions
|
||||
* for the currently active event, if any.
|
||||
*/
|
||||
if (m_DetectParams != null)
|
||||
{
|
||||
XmlElement detParArrayN = doc.CreateElement("", "DetectArray", "");
|
||||
AppendXMLDetectArray(doc, detParArrayN, m_DetectParams);
|
||||
scriptStateN.AppendChild(detParArrayN);
|
||||
}
|
||||
m_RunOnePhase = "GetExecutionState F"; CheckRunLockInvariants(true);
|
||||
|
||||
/*
|
||||
* Save any events we have in the queue.
|
||||
* <EventQueue>
|
||||
* <Event Name="...">
|
||||
* <param>...</param> ...
|
||||
* <DetectParams>...</DetectParams> ...
|
||||
* </Event>
|
||||
* ...
|
||||
* </EventQueue>
|
||||
*/
|
||||
XmlElement queuedEventsN = doc.CreateElement("", "EventQueue", "");
|
||||
lock (m_QueueLock)
|
||||
{
|
||||
foreach (EventParams evt in m_EventQueue)
|
||||
{
|
||||
XmlElement singleEventN = doc.CreateElement("", "Event", "");
|
||||
singleEventN.SetAttribute("Name", evt.EventName);
|
||||
AppendXMLObjectArray(doc, singleEventN, evt.Params, "param");
|
||||
AppendXMLDetectArray(doc, singleEventN, evt.DetectParams);
|
||||
queuedEventsN.AppendChild(singleEventN);
|
||||
}
|
||||
}
|
||||
scriptStateN.AppendChild(queuedEventsN);
|
||||
m_RunOnePhase = "GetExecutionState G"; CheckRunLockInvariants(true);
|
||||
|
||||
/*
|
||||
* "Plugins" indicate enabled timers and listens, etc.
|
||||
*/
|
||||
Object[] pluginData =
|
||||
AsyncCommandManager.GetSerializationData(m_Engine,
|
||||
m_ItemID);
|
||||
|
||||
XmlNode plugins = doc.CreateElement("", "Plugins", "");
|
||||
AppendXMLObjectArray(doc, plugins, pluginData, "plugin");
|
||||
scriptStateN.AppendChild(plugins);
|
||||
m_RunOnePhase = "GetExecutionState H"; CheckRunLockInvariants(true);
|
||||
|
||||
/*
|
||||
* Let script run again.
|
||||
*/
|
||||
suspendOnCheckRunHold = false;
|
||||
|
||||
m_RunOnePhase = "GetExecutionState leave";
|
||||
CheckRunLockInvariants(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* scriptStateN represents the contents of the .state file so
|
||||
* write the .state file while we are here.
|
||||
*/
|
||||
FileStream fs = File.Create(m_StateFileName);
|
||||
StreamWriter sw = new StreamWriter(fs);
|
||||
sw.Write(scriptStateN.OuterXml);
|
||||
sw.Close();
|
||||
fs.Close();
|
||||
|
||||
return scriptStateN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write script state to output stream.
|
||||
* The script microthread is at same state on return,
|
||||
* ie, either inactive or suspended inside CheckRun().
|
||||
*
|
||||
* Input:
|
||||
* stream = stream to write event handler state information to
|
||||
*/
|
||||
private void MigrateOutEventHandler (Stream stream)
|
||||
{
|
||||
moehexcep = null;
|
||||
|
||||
// do all the work in the MigrateOutEventHandlerThread() method below
|
||||
moehstream = stream;
|
||||
|
||||
XMRScriptThread cst = XMRScriptThread.CurrentScriptThread ();
|
||||
if (cst != null) {
|
||||
|
||||
// we might be getting called inside some LSL Api function
|
||||
// so we are already in script thread and thus must do
|
||||
// migration directly
|
||||
MigrateOutEventHandlerThread ();
|
||||
} else {
|
||||
|
||||
// some other thread, do migration via a script thread
|
||||
lock (XMRScriptThread.m_WakeUpLock) {
|
||||
m_Engine.m_ThunkQueue.Enqueue (this.MigrateOutEventHandlerThread);
|
||||
}
|
||||
XMRScriptThread.WakeUpOne ();
|
||||
|
||||
// wait for it to complete
|
||||
lock (moehdone) {
|
||||
while (moehstream != null) {
|
||||
Monitor.Wait (moehdone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// maybe it threw up
|
||||
if (moehexcep != null) throw moehexcep;
|
||||
}
|
||||
private Exception moehexcep;
|
||||
private object moehdone = new object ();
|
||||
private Stream moehstream;
|
||||
private void MigrateOutEventHandlerThread ()
|
||||
{
|
||||
Exception except;
|
||||
|
||||
try {
|
||||
|
||||
/*
|
||||
* Resume the microthread and it will throw a StackCaptureException()
|
||||
* with the stack frames saved to this.stackFrames.
|
||||
* Then write the saved stack frames to the output stream.
|
||||
*
|
||||
* There is a stack only if the event code is not None.
|
||||
*/
|
||||
if (this.eventCode != ScriptEventCode.None) {
|
||||
|
||||
// tell microthread to continue
|
||||
// it should see captureStackFrames and throw StackCaptureException()
|
||||
// ...generating XMRStackFrames as it unwinds
|
||||
this.captureStackFrames = true;
|
||||
except = this.microthread.ResumeEx ();
|
||||
this.captureStackFrames = false;
|
||||
if (except == null) {
|
||||
throw new Exception ("stack save did not complete");
|
||||
}
|
||||
if (!(except is StackCaptureException)) {
|
||||
throw except;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write script state out, frames and all, to the stream.
|
||||
* Does not change script state.
|
||||
*/
|
||||
moehstream.WriteByte (migrationVersion);
|
||||
moehstream.WriteByte ((byte)16);
|
||||
this.MigrateOut (new BinaryWriter (moehstream));
|
||||
|
||||
/*
|
||||
* Now restore script stack.
|
||||
* Microthread will suspend inside CheckRun() when restore is complete.
|
||||
*/
|
||||
if (this.eventCode != ScriptEventCode.None) {
|
||||
this.stackFramesRestored = false;
|
||||
except = this.microthread.StartEx ();
|
||||
if (except != null) {
|
||||
throw except;
|
||||
}
|
||||
if (!this.stackFramesRestored) {
|
||||
throw new Exception ("restore after save did not complete");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
moehexcep = e;
|
||||
} finally {
|
||||
|
||||
// make sure CheckRunLockInvariants() won't puque
|
||||
if (this.microthread.Active () == 0) {
|
||||
this.eventCode = ScriptEventCode.None;
|
||||
}
|
||||
|
||||
// wake the MigrateOutEventHandler() method above
|
||||
lock (moehdone) {
|
||||
moehstream = null;
|
||||
Monitor.Pulse (moehdone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert an DetectParams[] to corresponding XML.
|
||||
* DetectParams[] holds the values retrievable by llDetected...() for
|
||||
* a given event.
|
||||
*/
|
||||
private static void AppendXMLDetectArray(XmlDocument doc, XmlElement parent, DetectParams[] detect)
|
||||
{
|
||||
foreach (DetectParams d in detect)
|
||||
{
|
||||
XmlElement detectParamsN = GetXMLDetect(doc, d);
|
||||
parent.AppendChild(detectParamsN);
|
||||
}
|
||||
}
|
||||
|
||||
private static XmlElement GetXMLDetect(XmlDocument doc, DetectParams d)
|
||||
{
|
||||
XmlElement detectParamsN = doc.CreateElement("", "DetectParams", "");
|
||||
|
||||
XmlAttribute d_key = doc.CreateAttribute("", "key", "");
|
||||
d_key.Value = d.Key.ToString();
|
||||
detectParamsN.Attributes.Append(d_key);
|
||||
|
||||
XmlAttribute pos = doc.CreateAttribute("", "pos", "");
|
||||
pos.Value = d.OffsetPos.ToString();
|
||||
detectParamsN.Attributes.Append(pos);
|
||||
|
||||
XmlAttribute d_linkNum = doc.CreateAttribute("", "linkNum", "");
|
||||
d_linkNum.Value = d.LinkNum.ToString();
|
||||
detectParamsN.Attributes.Append(d_linkNum);
|
||||
|
||||
XmlAttribute d_group = doc.CreateAttribute("", "group", "");
|
||||
d_group.Value = d.Group.ToString();
|
||||
detectParamsN.Attributes.Append(d_group);
|
||||
|
||||
XmlAttribute d_name = doc.CreateAttribute("", "name", "");
|
||||
d_name.Value = d.Name.ToString();
|
||||
detectParamsN.Attributes.Append(d_name);
|
||||
|
||||
XmlAttribute d_owner = doc.CreateAttribute("", "owner", "");
|
||||
d_owner.Value = d.Owner.ToString();
|
||||
detectParamsN.Attributes.Append(d_owner);
|
||||
|
||||
XmlAttribute d_position = doc.CreateAttribute("", "position", "");
|
||||
d_position.Value = d.Position.ToString();
|
||||
detectParamsN.Attributes.Append(d_position);
|
||||
|
||||
XmlAttribute d_rotation = doc.CreateAttribute("", "rotation", "");
|
||||
d_rotation.Value = d.Rotation.ToString();
|
||||
detectParamsN.Attributes.Append(d_rotation);
|
||||
|
||||
XmlAttribute d_type = doc.CreateAttribute("", "type", "");
|
||||
d_type.Value = d.Type.ToString();
|
||||
detectParamsN.Attributes.Append(d_type);
|
||||
|
||||
XmlAttribute d_velocity = doc.CreateAttribute("", "velocity", "");
|
||||
d_velocity.Value = d.Velocity.ToString();
|
||||
detectParamsN.Attributes.Append(d_velocity);
|
||||
|
||||
return detectParamsN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Append elements of an array of objects to an XML parent.
|
||||
* @param doc = document the parent is part of
|
||||
* @param parent = parent to append the items to
|
||||
* @param array = array of objects
|
||||
* @param tag = <tag ..>...</tag> for each element
|
||||
*/
|
||||
private static void AppendXMLObjectArray(XmlDocument doc, XmlNode parent, object[] array, string tag)
|
||||
{
|
||||
foreach (object o in array)
|
||||
{
|
||||
XmlElement element = GetXMLObject(doc, o, tag);
|
||||
parent.AppendChild(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get and XML representation of an object.
|
||||
* @param doc = document the tag will be put in
|
||||
* @param o = object to be represented
|
||||
* @param tag = <tag ...>...</tag>
|
||||
*/
|
||||
private static XmlElement GetXMLObject(XmlDocument doc, object o, string tag)
|
||||
{
|
||||
XmlAttribute typ = doc.CreateAttribute("", "type", "");
|
||||
XmlElement n = doc.CreateElement("", tag, "");
|
||||
|
||||
if (o is LSL_List)
|
||||
{
|
||||
typ.Value = "list";
|
||||
n.Attributes.Append(typ);
|
||||
AppendXMLObjectArray(doc, n, ((LSL_List)o).Data, "item");
|
||||
}
|
||||
else
|
||||
{
|
||||
typ.Value = o.GetType().ToString();
|
||||
n.Attributes.Append(typ);
|
||||
n.AppendChild(doc.CreateTextNode(o.ToString()));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,878 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Remoting.Lifetime;
|
||||
using System.Security.Policy;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.ScriptEngine.Interfaces;
|
||||
using OpenSim.Region.ScriptEngine.Shared;
|
||||
using OpenSim.Region.ScriptEngine.Shared.Api;
|
||||
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
|
||||
using OpenSim.Region.ScriptEngine.XMREngine;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using log4net;
|
||||
|
||||
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 partial class XMRInstance
|
||||
{
|
||||
/****************************************************************************\
|
||||
* The only method of interest to outside this module is the Initializer. *
|
||||
* *
|
||||
* The rest of this module contains support routines for the Initializer. *
|
||||
\****************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Initializer, loads script in memory and all ready for running.
|
||||
* @param engine = XMREngine instance this is part of
|
||||
* @param scriptBasePath = directory name where files are
|
||||
* @param stackSize = number of bytes to allocate for stacks
|
||||
* @param errors = return compiler errors in this array
|
||||
* @param forceRecomp = force recompile
|
||||
* Throws exception if any error, so it was successful if it returns.
|
||||
*/
|
||||
public void Initialize(XMREngine engine, string scriptBasePath,
|
||||
int stackSize, int heapSize, ArrayList errors)
|
||||
{
|
||||
if (stackSize < 16384) stackSize = 16384;
|
||||
if (heapSize < 16384) heapSize = 16384;
|
||||
|
||||
/*
|
||||
* Save all call parameters in instance vars for easy access.
|
||||
*/
|
||||
m_Engine = engine;
|
||||
m_ScriptBasePath = scriptBasePath;
|
||||
m_StackSize = stackSize;
|
||||
m_HeapSize = heapSize;
|
||||
m_CompilerErrors = errors;
|
||||
m_StateFileName = GetStateFileName(scriptBasePath, m_ItemID);
|
||||
|
||||
/*
|
||||
* Not in any XMRInstQueue.
|
||||
*/
|
||||
m_NextInst = this;
|
||||
m_PrevInst = this;
|
||||
|
||||
/*
|
||||
* Set up list of API calls it has available.
|
||||
* This also gets the API modules ready to accept setup data, such as
|
||||
* active listeners being restored.
|
||||
*/
|
||||
IScriptApi scriptApi;
|
||||
ApiManager am = new ApiManager();
|
||||
foreach (string api in am.GetApis())
|
||||
{
|
||||
/*
|
||||
* Instantiate the API for this script instance.
|
||||
*/
|
||||
if (api != "LSL") {
|
||||
scriptApi = am.CreateApi(api);
|
||||
} else {
|
||||
scriptApi = m_XMRLSLApi = new XMRLSL_Api();
|
||||
}
|
||||
|
||||
/*
|
||||
* Connect it up to the instance.
|
||||
*/
|
||||
InitScriptApi (engine, api, scriptApi);
|
||||
}
|
||||
|
||||
m_XMRLSLApi.InitXMRLSLApi(this);
|
||||
|
||||
/*
|
||||
* Get object loaded, compiling script and reading .state file as
|
||||
* necessary to restore the state.
|
||||
*/
|
||||
suspendOnCheckRunHold = true;
|
||||
InstantiateScript();
|
||||
m_SourceCode = null;
|
||||
if (m_ObjCode == null) throw new ArgumentNullException ("m_ObjCode");
|
||||
if (m_ObjCode.scriptEventHandlerTable == null) {
|
||||
throw new ArgumentNullException ("m_ObjCode.scriptEventHandlerTable");
|
||||
}
|
||||
|
||||
suspendOnCheckRunHold = false;
|
||||
suspendOnCheckRunTemp = false;
|
||||
|
||||
/*
|
||||
* Declare which events the script's current state can handle.
|
||||
*/
|
||||
int eventMask = GetStateEventFlags(stateCode);
|
||||
m_Part.SetScriptEvents(m_ItemID, eventMask);
|
||||
}
|
||||
|
||||
private void InitScriptApi (XMREngine engine, string api, IScriptApi scriptApi)
|
||||
{
|
||||
/*
|
||||
* Set up m_ApiManager_<APINAME> = instance pointer.
|
||||
*/
|
||||
engine.m_XMRInstanceApiCtxFieldInfos[api].SetValue (this, scriptApi);
|
||||
|
||||
/*
|
||||
* Initialize the API instance.
|
||||
*/
|
||||
scriptApi.Initialize(m_Engine, m_Part, m_Item);
|
||||
this.InitApi (api, scriptApi);
|
||||
}
|
||||
|
||||
|
||||
// Get script object code loaded in memory and all ready to run,
|
||||
// ready to resume it from where the .state file says it was last
|
||||
private void InstantiateScript()
|
||||
{
|
||||
bool compiledIt = false;
|
||||
ScriptObjCode objCode;
|
||||
|
||||
/*
|
||||
* If source code string is empty, use the asset ID as the object file name.
|
||||
* Allow lines of // comments at the beginning (for such as engine selection).
|
||||
*/
|
||||
int i, j, len;
|
||||
if (m_SourceCode == null) m_SourceCode = String.Empty;
|
||||
for (len = m_SourceCode.Length; len > 0; -- len) {
|
||||
if (m_SourceCode[len-1] > ' ') break;
|
||||
}
|
||||
for (i = 0; i < len; i ++) {
|
||||
char c = m_SourceCode[i];
|
||||
if (c <= ' ') continue;
|
||||
if (c != '/') break;
|
||||
if ((i + 1 >= len) || (m_SourceCode[i+1] != '/')) break;
|
||||
i = m_SourceCode.IndexOf ('\n', i);
|
||||
if (i < 0) i = len - 1;
|
||||
}
|
||||
if ((i >= len) || !m_Engine.m_UseSourceHashCode) {
|
||||
|
||||
/*
|
||||
* Source consists of nothing but // comments and whitespace,
|
||||
* or we are being forced to use the asset-id as the key, to
|
||||
* open an already existing object code file.
|
||||
*/
|
||||
m_ScriptObjCodeKey = m_Item.AssetID.ToString ();
|
||||
if (i >= len) m_SourceCode = "";
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Make up dictionary key for the object code.
|
||||
* Use the same object code for identical source code
|
||||
* regardless of asset ID, so we don't care if they
|
||||
* copy scripts or not.
|
||||
*/
|
||||
byte[] scbytes = System.Text.Encoding.UTF8.GetBytes (m_SourceCode);
|
||||
StringBuilder sb = new StringBuilder ((256 + 5) / 6);
|
||||
ByteArrayToSixbitStr (sb, System.Security.Cryptography.SHA256.Create ().ComputeHash (scbytes));
|
||||
m_ScriptObjCodeKey = sb.ToString ();
|
||||
|
||||
/*
|
||||
* But source code can be just a sixbit string itself
|
||||
* that identifies an already existing object code file.
|
||||
*/
|
||||
if (len - i == m_ScriptObjCodeKey.Length) {
|
||||
for (j = len; -- j >= i;) {
|
||||
if (sixbit.IndexOf (m_SourceCode[j]) < 0) break;
|
||||
}
|
||||
if (j < i) {
|
||||
m_ScriptObjCodeKey = m_SourceCode.Substring (i, len - i);
|
||||
m_SourceCode = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* There may already be an ScriptObjCode struct in memory that
|
||||
* we can use. If not, try to compile it.
|
||||
*/
|
||||
lock (m_CompileLock) {
|
||||
if (!m_CompiledScriptObjCode.TryGetValue (m_ScriptObjCodeKey, out objCode) || m_ForceRecomp) {
|
||||
objCode = TryToCompile ();
|
||||
compiledIt = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loaded successfully, increment reference count.
|
||||
*
|
||||
* If we just compiled it though, reset count to 0 first as
|
||||
* this is the one-and-only existance of this objCode struct,
|
||||
* and we want any old ones for this source code to be garbage
|
||||
* collected.
|
||||
*/
|
||||
if (compiledIt) {
|
||||
m_CompiledScriptObjCode[m_ScriptObjCodeKey] = objCode;
|
||||
objCode.refCount = 0;
|
||||
}
|
||||
objCode.refCount ++;
|
||||
|
||||
/*
|
||||
* Now set up to decrement ref count on dispose.
|
||||
*/
|
||||
m_ObjCode = objCode;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
/*
|
||||
* Fill in script instance from object code
|
||||
* Script instance is put in a "never-ever-has-run-before" state.
|
||||
*/
|
||||
LoadObjCode();
|
||||
|
||||
/*
|
||||
* Fill in script intial state
|
||||
* - either as loaded from a .state file
|
||||
* - or initial default state_entry() event
|
||||
*/
|
||||
LoadInitialState();
|
||||
} catch {
|
||||
|
||||
/*
|
||||
* If any error loading, decrement object code reference count.
|
||||
*/
|
||||
DecObjCodeRefCount ();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private const string sixbit = "0123456789_abcdefghijklmnopqrstuvwxyz@ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static void ByteArrayToSixbitStr (StringBuilder sb, byte[] bytes)
|
||||
{
|
||||
int bit = 0;
|
||||
int val = 0;
|
||||
foreach (byte b in bytes) {
|
||||
val |= (int)((uint)b << bit);
|
||||
bit += 8;
|
||||
while (bit >= 6) {
|
||||
sb.Append (sixbit[val&63]);
|
||||
val >>= 6;
|
||||
bit -= 6;
|
||||
}
|
||||
}
|
||||
if (bit > 0) {
|
||||
sb.Append (sixbit[val&63]);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to create object code from source code
|
||||
// If error, just throw exception
|
||||
private ScriptObjCode TryToCompile ()
|
||||
{
|
||||
m_CompilerErrors.Clear();
|
||||
|
||||
/*
|
||||
* If object file exists, create ScriptObjCode directly from that.
|
||||
* Otherwise, compile the source to create object file then create
|
||||
* ScriptObjCode from that.
|
||||
*/
|
||||
string assetID = m_Item.AssetID.ToString();
|
||||
m_CameFrom = "asset://" + assetID;
|
||||
ScriptObjCode objCode = Compile ();
|
||||
if (m_CompilerErrors.Count != 0)
|
||||
{
|
||||
throw new Exception ("compilation errors");
|
||||
}
|
||||
if (objCode == null)
|
||||
{
|
||||
throw new Exception ("compilation failed");
|
||||
}
|
||||
|
||||
return objCode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve source from asset server.
|
||||
*/
|
||||
private string FetchSource (string cameFrom)
|
||||
{
|
||||
m_log.Debug ("[XMREngine]: fetching source " + cameFrom);
|
||||
if (!cameFrom.StartsWith ("asset://")) {
|
||||
throw new Exception ("unable to retrieve source from " + cameFrom);
|
||||
}
|
||||
string assetID = cameFrom.Substring (8);
|
||||
AssetBase asset = m_Engine.World.AssetService.Get(assetID);
|
||||
if (asset == null) {
|
||||
throw new Exception ("source not found " + cameFrom);
|
||||
}
|
||||
string source = Encoding.UTF8.GetString (asset.Data);
|
||||
if (EmptySource (source)) {
|
||||
throw new Exception ("fetched source empty " + cameFrom);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in script object initial contents.
|
||||
* Set the initial state to "default".
|
||||
*/
|
||||
private void LoadObjCode ()
|
||||
{
|
||||
/*
|
||||
* Script must leave this much stack remaining on calls to CheckRun().
|
||||
*/
|
||||
this.stackLimit = m_StackSize / 2;
|
||||
|
||||
/*
|
||||
* This is how many total heap bytes script is allowed to use.
|
||||
*/
|
||||
this.heapLimit = m_HeapSize;
|
||||
|
||||
/*
|
||||
* Allocate global variable arrays.
|
||||
*/
|
||||
this.glblVars.AllocVarArrays (m_ObjCode.glblSizes);
|
||||
|
||||
/*
|
||||
* Script can handle these event codes.
|
||||
*/
|
||||
m_HaveEventHandlers = new bool[m_ObjCode.scriptEventHandlerTable.GetLength(1)];
|
||||
for (int i = m_ObjCode.scriptEventHandlerTable.GetLength(0); -- i >= 0;) {
|
||||
for (int j = m_ObjCode.scriptEventHandlerTable.GetLength(1); -- j >= 0;) {
|
||||
if (m_ObjCode.scriptEventHandlerTable[i,j] != null) {
|
||||
m_HaveEventHandlers[j] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up microthread object which actually calls the script event handler functions.
|
||||
*/
|
||||
this.microthread = (IScriptUThread)m_Engine.uThreadCtor.Invoke (new object[] { this });
|
||||
}
|
||||
|
||||
// LoadInitialState()
|
||||
// if no state XML file exists for the asset,
|
||||
// post initial default state events
|
||||
// else
|
||||
// try to restore from .state file
|
||||
// If any error, throw exception
|
||||
//
|
||||
private void LoadInitialState()
|
||||
{
|
||||
/*
|
||||
* If no .state file exists, start from default state
|
||||
* Otherwise, read initial state from the .state file
|
||||
*/
|
||||
if (!File.Exists(m_StateFileName)) {
|
||||
m_Running = true; // event processing is enabled
|
||||
eventCode = ScriptEventCode.None; // not processing any event
|
||||
|
||||
// default state_entry() must initialize global variables
|
||||
doGblInit = true;
|
||||
stateCode = 0;
|
||||
|
||||
PostEvent(new EventParams("state_entry",
|
||||
zeroObjectArray,
|
||||
zeroDetectParams));
|
||||
} else {
|
||||
FileStream fs = File.Open(m_StateFileName,
|
||||
FileMode.Open,
|
||||
FileAccess.Read);
|
||||
StreamReader ss = new StreamReader(fs);
|
||||
string xml = ss.ReadToEnd();
|
||||
ss.Close();
|
||||
fs.Close();
|
||||
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.LoadXml(xml);
|
||||
LoadScriptState(doc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Post event(s) saying what caused the script to start.
|
||||
*/
|
||||
if (m_PostOnRez) {
|
||||
PostEvent(new EventParams("on_rez",
|
||||
new Object[] { m_StartParam },
|
||||
zeroDetectParams));
|
||||
}
|
||||
|
||||
switch (m_StateSource) {
|
||||
case StateSource.AttachedRez: {
|
||||
// PostEvent(new EventParams("attach",
|
||||
// new object[] { m_Part.ParentGroup.AttachedAvatar.ToString() },
|
||||
// zeroDetectParams));
|
||||
break;
|
||||
}
|
||||
|
||||
case StateSource.PrimCrossing: {
|
||||
PostEvent(new EventParams("changed",
|
||||
sbcCR,
|
||||
zeroDetectParams));
|
||||
break;
|
||||
}
|
||||
|
||||
case StateSource.Teleporting: {
|
||||
PostEvent(new EventParams("changed",
|
||||
sbcCR,
|
||||
zeroDetectParams));
|
||||
PostEvent(new EventParams("changed",
|
||||
sbcCT,
|
||||
zeroDetectParams));
|
||||
break;
|
||||
}
|
||||
|
||||
case StateSource.RegionStart: {
|
||||
PostEvent(new EventParams("changed",
|
||||
sbcCRS,
|
||||
zeroDetectParams));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Object[] sbcCRS = new Object[] { ScriptBaseClass.CHANGED_REGION_START };
|
||||
private static Object[] sbcCR = new Object[] { ScriptBaseClass.CHANGED_REGION };
|
||||
private static Object[] sbcCT = new Object[] { ScriptBaseClass.CHANGED_TELEPORT };
|
||||
|
||||
/**
|
||||
* @brief Save compilation error messages for later retrieval
|
||||
* via GetScriptErrors().
|
||||
*/
|
||||
private void ErrorHandler(Token token, string message)
|
||||
{
|
||||
if (token != null) {
|
||||
string srcloc = token.SrcLoc;
|
||||
if (srcloc.StartsWith (m_CameFrom)) {
|
||||
srcloc = srcloc.Substring (m_CameFrom.Length);
|
||||
}
|
||||
m_CompilerErrors.Add(srcloc + " Error: " + message);
|
||||
} else if (message != null) {
|
||||
m_CompilerErrors.Add("(0,0) Error: " + message);
|
||||
} else {
|
||||
m_CompilerErrors.Add("(0,0) Error compiling, see exception in log");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load script state from the given XML doc into the script memory
|
||||
* <ScriptState Engine="XMREngine" Asset=...>
|
||||
* <Running>...</Running>
|
||||
* <DoGblInit>...</DoGblInit>
|
||||
* <Permissions granted=... mask=... />
|
||||
* RestoreDetectParams()
|
||||
* <Plugins>
|
||||
* ExtractXMLObjectArray("plugin")
|
||||
* </Plugins>
|
||||
* <Snapshot>
|
||||
* MigrateInEventHandler()
|
||||
* </Snapshot>
|
||||
* </ScriptState>
|
||||
*/
|
||||
private void LoadScriptState(XmlDocument doc)
|
||||
{
|
||||
DetectParams[] detParams;
|
||||
LinkedList<EventParams> eventQueue;
|
||||
|
||||
// Everything we know is enclosed in <ScriptState>...</ScriptState>
|
||||
XmlElement scriptStateN = (XmlElement)doc.SelectSingleNode("ScriptState");
|
||||
if (scriptStateN == null) {
|
||||
throw new Exception("no <ScriptState> tag");
|
||||
}
|
||||
string sen = scriptStateN.GetAttribute("Engine");
|
||||
if ((sen == null) || (sen != m_Engine.ScriptEngineName)) {
|
||||
throw new Exception("<ScriptState> missing Engine=\"XMREngine\" attribute");
|
||||
}
|
||||
|
||||
// AssetID is unique for the script source text so make sure the
|
||||
// state file was written for that source file
|
||||
string assetID = scriptStateN.GetAttribute("Asset");
|
||||
if (assetID != m_Item.AssetID.ToString())
|
||||
{
|
||||
throw new Exception("<ScriptState> assetID mismatch");
|
||||
}
|
||||
|
||||
// Also match the sourceHash in case script was
|
||||
// loaded via 'xmroption fetchsource' and has changed
|
||||
string sourceHash = scriptStateN.GetAttribute ("SourceHash");
|
||||
if ((sourceHash == null) || (sourceHash != m_ObjCode.sourceHash)) {
|
||||
throw new Exception ("<ScriptState> SourceHash mismatch");
|
||||
}
|
||||
|
||||
// Get various attributes
|
||||
XmlElement runningN = (XmlElement)scriptStateN.SelectSingleNode("Running");
|
||||
m_Running = bool.Parse(runningN.InnerText);
|
||||
|
||||
XmlElement doGblInitN = (XmlElement)scriptStateN.SelectSingleNode("DoGblInit");
|
||||
doGblInit = bool.Parse(doGblInitN.InnerText);
|
||||
|
||||
XmlElement permissionsN = (XmlElement)scriptStateN.SelectSingleNode("Permissions");
|
||||
m_Item.PermsGranter = new UUID(permissionsN.GetAttribute("granter"));
|
||||
m_Item.PermsMask = Convert.ToInt32(permissionsN.GetAttribute("mask"));
|
||||
m_Part.Inventory.UpdateInventoryItem(m_Item, false, false);
|
||||
|
||||
// get values used by stuff like llDetectedGrab, etc.
|
||||
detParams = RestoreDetectParams(scriptStateN.SelectSingleNode("DetectArray"));
|
||||
|
||||
// Restore queued events
|
||||
eventQueue = RestoreEventQueue(scriptStateN.SelectSingleNode("EventQueue"));
|
||||
|
||||
// Restore timers and listeners
|
||||
XmlElement pluginN = (XmlElement)scriptStateN.SelectSingleNode("Plugins");
|
||||
Object[] pluginData = ExtractXMLObjectArray(pluginN, "plugin");
|
||||
|
||||
// Script's global variables and stack contents
|
||||
XmlElement snapshotN =
|
||||
(XmlElement)scriptStateN.SelectSingleNode("Snapshot");
|
||||
|
||||
Byte[] data = Convert.FromBase64String(snapshotN.InnerText);
|
||||
MemoryStream ms = new MemoryStream();
|
||||
ms.Write(data, 0, data.Length);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
MigrateInEventHandler(ms);
|
||||
ms.Close();
|
||||
|
||||
// Restore event queues, preserving any events that queued
|
||||
// whilst we were restoring the state
|
||||
lock (m_QueueLock) {
|
||||
m_DetectParams = detParams;
|
||||
foreach (EventParams evt in m_EventQueue) {
|
||||
eventQueue.AddLast (evt);
|
||||
}
|
||||
m_EventQueue = eventQueue;
|
||||
for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
|
||||
foreach (EventParams evt in m_EventQueue)
|
||||
{
|
||||
ScriptEventCode eventCode = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
|
||||
evt.EventName);
|
||||
m_EventCounts[(int)eventCode] ++;
|
||||
}
|
||||
}
|
||||
|
||||
// Requeue timer and listeners (possibly queuing new events)
|
||||
AsyncCommandManager.CreateFromData(m_Engine,
|
||||
m_LocalID, m_ItemID, m_Part.UUID,
|
||||
pluginData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read llDetectedGrab, etc, values from XML
|
||||
* <EventQueue>
|
||||
* <DetectParams>...</DetectParams>
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* </EventQueue>
|
||||
*/
|
||||
private LinkedList<EventParams> RestoreEventQueue(XmlNode eventsN)
|
||||
{
|
||||
LinkedList<EventParams> eventQueue = new LinkedList<EventParams>();
|
||||
if (eventsN != null) {
|
||||
XmlNodeList eventL = eventsN.SelectNodes("Event");
|
||||
foreach (XmlNode evnt in eventL)
|
||||
{
|
||||
string name = ((XmlElement)evnt).GetAttribute("Name");
|
||||
object[] parms = ExtractXMLObjectArray(evnt, "param");
|
||||
DetectParams[] detects = RestoreDetectParams(evnt);
|
||||
|
||||
if (parms == null) parms = zeroObjectArray;
|
||||
if (detects == null) detects = zeroDetectParams;
|
||||
|
||||
EventParams evt = new EventParams(name, parms, detects);
|
||||
eventQueue.AddLast(evt);
|
||||
}
|
||||
}
|
||||
return eventQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read llDetectedGrab, etc, values from XML
|
||||
* <DetectArray>
|
||||
* <DetectParams>...</DetectParams>
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* </DetectArray>
|
||||
*/
|
||||
private DetectParams[] RestoreDetectParams(XmlNode detectedN)
|
||||
{
|
||||
if (detectedN == null) return null;
|
||||
|
||||
List<DetectParams> detected = new List<DetectParams>();
|
||||
XmlNodeList detectL = detectedN.SelectNodes("DetectParams");
|
||||
|
||||
DetectParams detprm = new DetectParams();
|
||||
foreach (XmlNode detxml in detectL) {
|
||||
try {
|
||||
detprm.Group = new UUID(detxml.Attributes.GetNamedItem("group").Value);
|
||||
detprm.Key = new UUID(detxml.Attributes.GetNamedItem("key").Value);
|
||||
detprm.Owner = new UUID(detxml.Attributes.GetNamedItem("owner").Value);
|
||||
|
||||
detprm.LinkNum = Int32.Parse(detxml.Attributes.GetNamedItem("linkNum").Value);
|
||||
detprm.Type = Int32.Parse(detxml.Attributes.GetNamedItem("type").Value);
|
||||
|
||||
detprm.Name = detxml.Attributes.GetNamedItem("name").Value;
|
||||
|
||||
detprm.OffsetPos = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("pos").Value);
|
||||
detprm.Position = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("position").Value);
|
||||
detprm.Velocity = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("velocity").Value);
|
||||
|
||||
detprm.Rotation = new LSL_Types.Quaternion(detxml.Attributes.GetNamedItem("rotation").Value);
|
||||
|
||||
detected.Add(detprm);
|
||||
detprm = new DetectParams();
|
||||
} catch (Exception e) {
|
||||
m_log.Warn("[XMREngine]: RestoreDetectParams bad XML: " + detxml.ToString());
|
||||
m_log.Warn("[XMREngine]: ... " + e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return detected.ToArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract elements of an array of objects from an XML parent.
|
||||
* Each element is of form <tag ...>...</tag>
|
||||
* @param parent = XML parent to extract them from
|
||||
* @param tag = what the value's tag is
|
||||
* @returns object array of the values
|
||||
*/
|
||||
private static object[] ExtractXMLObjectArray(XmlNode parent, string tag)
|
||||
{
|
||||
List<Object> olist = new List<Object>();
|
||||
|
||||
XmlNodeList itemL = parent.SelectNodes(tag);
|
||||
foreach (XmlNode item in itemL)
|
||||
{
|
||||
olist.Add(ExtractXMLObjectValue(item));
|
||||
}
|
||||
|
||||
return olist.ToArray();
|
||||
}
|
||||
|
||||
private static object ExtractXMLObjectValue(XmlNode item)
|
||||
{
|
||||
string itemType = item.Attributes.GetNamedItem("type").Value;
|
||||
|
||||
if (itemType == "list")
|
||||
{
|
||||
return new LSL_List(ExtractXMLObjectArray(item, "item"));
|
||||
}
|
||||
|
||||
if (itemType == "OpenMetaverse.UUID")
|
||||
{
|
||||
UUID val = new UUID();
|
||||
UUID.TryParse(item.InnerText, out val);
|
||||
return val;
|
||||
}
|
||||
|
||||
Type itemT = Type.GetType(itemType);
|
||||
if (itemT == null)
|
||||
{
|
||||
Object[] args = new Object[] { item.InnerText };
|
||||
|
||||
string assembly = itemType + ", OpenSim.Region.ScriptEngine.Shared";
|
||||
itemT = Type.GetType(assembly);
|
||||
if (itemT == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Activator.CreateInstance(itemT, args);
|
||||
}
|
||||
|
||||
return Convert.ChangeType(item.InnerText, itemT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Migrate an event handler in from a stream.
|
||||
*
|
||||
* Input:
|
||||
* stream = as generated by MigrateOutEventHandler()
|
||||
*/
|
||||
private void MigrateInEventHandler (Stream stream)
|
||||
{
|
||||
miehexcep = null;
|
||||
|
||||
// do all the work in the MigrateInEventHandlerThread() method below
|
||||
miehstream = stream;
|
||||
|
||||
XMRScriptThread cst = XMRScriptThread.CurrentScriptThread ();
|
||||
if (cst != null) {
|
||||
|
||||
// in case we are getting called inside some LSL Api function
|
||||
MigrateInEventHandlerThread ();
|
||||
} else {
|
||||
|
||||
// some other thread, do migration via a script thread
|
||||
lock (XMRScriptThread.m_WakeUpLock) {
|
||||
m_Engine.m_ThunkQueue.Enqueue (this.MigrateInEventHandlerThread);
|
||||
}
|
||||
XMRScriptThread.WakeUpOne ();
|
||||
|
||||
// wait for it to complete
|
||||
lock (miehdone) {
|
||||
while (miehstream != null) {
|
||||
Monitor.Wait (miehdone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// maybe it threw up
|
||||
if (miehexcep != null) throw miehexcep;
|
||||
}
|
||||
private Exception miehexcep;
|
||||
private object miehdone = new object ();
|
||||
private Stream miehstream;
|
||||
private void MigrateInEventHandlerThread ()
|
||||
{
|
||||
try {
|
||||
int mv = miehstream.ReadByte ();
|
||||
if (mv != migrationVersion) {
|
||||
throw new Exception ("incoming migration version " + mv + " but accept only " + migrationVersion);
|
||||
}
|
||||
miehstream.ReadByte (); // ignored
|
||||
|
||||
/*
|
||||
* Restore script variables and stack and other state from stream.
|
||||
* And it also marks us busy (by setting this.eventCode) so we can't be
|
||||
* started again and this event lost.
|
||||
*/
|
||||
BinaryReader br = new BinaryReader (miehstream);
|
||||
this.MigrateIn (br);
|
||||
|
||||
/*
|
||||
* If eventCode is None, it means the script was idle when migrated.
|
||||
*/
|
||||
if (this.eventCode != ScriptEventCode.None) {
|
||||
|
||||
/*
|
||||
* So microthread.Start() calls XMRScriptUThread.Main() which calls the
|
||||
* event handler function. The event handler function sees the stack
|
||||
* frames in this.stackFrames and restores its args and locals, then calls
|
||||
* whatever it was calling when the snapshot was taken. That function also
|
||||
* sees this.stackFrames and restores its args and locals, and so on...
|
||||
* Eventually it gets to the point of calling CheckRun() which sees we are
|
||||
* doing a restore and it suspends, returning here with the microthread
|
||||
* stack all restored. It shouldn't ever throw an exception.
|
||||
*/
|
||||
this.stackFramesRestored = false;
|
||||
Exception te = microthread.StartEx ();
|
||||
if (te != null) throw te;
|
||||
if (!this.stackFramesRestored) throw new Exception ("migrate in did not complete");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
miehexcep = e;
|
||||
} finally {
|
||||
|
||||
/*
|
||||
* Wake the MigrateInEventHandler() method above.
|
||||
*/
|
||||
lock (miehdone) {
|
||||
miehstream = null;
|
||||
Monitor.Pulse (miehdone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See if permitted by configuration file.
|
||||
* See OSSL_Api.CheckThreatLevelTest().
|
||||
*/
|
||||
public string CheckFetchbinaryAllowed ()
|
||||
{
|
||||
string ownerPerm = m_Engine.Config.GetString ("Allow_fetchbinary", "");
|
||||
UUID ownerID = m_Item.OwnerID;
|
||||
string[] ids = ownerPerm.Split (new char[] { ',' });
|
||||
foreach (string id in ids) {
|
||||
string curuc = id.Trim ().ToUpperInvariant ();
|
||||
|
||||
switch (curuc) {
|
||||
case "ESTATE_MANAGER": {
|
||||
if (m_Engine.m_Scene.RegionInfo.EstateSettings.IsEstateManagerOrOwner (ownerID) &&
|
||||
(m_Engine.m_Scene.RegionInfo.EstateSettings.EstateOwner != ownerID)) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "ESTATE_OWNER": {
|
||||
if (m_Engine.m_Scene.RegionInfo.EstateSettings.EstateOwner == ownerID) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "PARCEL_GROUP_MEMBER": {
|
||||
ILandObject land = m_Engine.m_Scene.LandChannel.GetLandObject (m_Part.AbsolutePosition);
|
||||
if (land.LandData.GroupID == m_Item.GroupID && land.LandData.GroupID != UUID.Zero) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "PARCEL_OWNER": {
|
||||
ILandObject land = m_Engine.m_Scene.LandChannel.GetLandObject (m_Part.AbsolutePosition);
|
||||
if (land.LandData.OwnerID == ownerID) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "TRUE": {
|
||||
return null;
|
||||
}
|
||||
|
||||
default: {
|
||||
UUID uuid;
|
||||
if (UUID.TryParse (curuc, out uuid)) {
|
||||
if (uuid == ownerID) return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string creatorPerm = m_Engine.Config.GetString ("Creators_fetchbinary", "");
|
||||
UUID creatorID = m_Item.CreatorID;
|
||||
ids = creatorPerm.Split (new char[] { ',' });
|
||||
foreach (string id in ids) {
|
||||
string current = id.Trim ();
|
||||
UUID uuid;
|
||||
if (UUID.TryParse (current, out uuid)) {
|
||||
if (uuid != UUID.Zero) {
|
||||
if (creatorID == uuid) return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "fetchbinary not enabled for owner " + ownerID + " creator " + creatorID;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Remoting.Lifetime;
|
||||
using System.Security.Policy;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.ScriptEngine.Interfaces;
|
||||
using OpenSim.Region.ScriptEngine.Shared;
|
||||
using OpenSim.Region.ScriptEngine.Shared.Api;
|
||||
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
|
||||
using OpenSim.Region.ScriptEngine.XMREngine;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using log4net;
|
||||
|
||||
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;
|
||||
|
||||
// This class exists in the main app domain
|
||||
//
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
/**
|
||||
* @brief Which queue it is in as far as running is concerned,
|
||||
* ie, m_StartQueue, m_YieldQueue, m_SleepQueue, etc.
|
||||
* Allowed transitions:
|
||||
* Starts in CONSTRUCT when constructed
|
||||
* CONSTRUCT->ONSTARTQ : only by thread that constructed and compiled it
|
||||
* IDLE->ONSTARTQ,RESETTING : by any thread but must have m_QueueLock when transitioning
|
||||
* ONSTARTQ->RUNNING,RESETTING : only by thread that removed it from m_StartQueue
|
||||
* ONYIELDQ->RUNNING,RESETTING : only by thread that removed it from m_YieldQueue
|
||||
* ONSLEEPQ->REMDFROMSLPQ : by any thread but must have m_SleepQueue when transitioning
|
||||
* REMDFROMSLPQ->ONYIELDQ,RESETTING : only by thread that removed it from m_SleepQueue
|
||||
* RUNNING->whatever1 : only by thread that transitioned it to RUNNING
|
||||
* whatever1 = IDLE,ONSLEEPQ,ONYIELDQ,ONSTARTQ,SUSPENDED,FINISHED
|
||||
* FINSHED->whatever2 : only by thread that transitioned it to FINISHED
|
||||
* whatever2 = IDLE,ONSTARTQ,DISPOSED
|
||||
* SUSPENDED->ONSTARTQ : by any thread (NOT YET IMPLEMENTED, should be under some kind of lock?)
|
||||
* RESETTING->ONSTARTQ : only by the thread that transitioned it to RESETTING
|
||||
*/
|
||||
public enum XMRInstState {
|
||||
CONSTRUCT, // it is being constructed
|
||||
IDLE, // nothing happening (finished last event and m_EventQueue is empty)
|
||||
ONSTARTQ, // inserted on m_Engine.m_StartQueue
|
||||
RUNNING, // currently being executed by RunOne()
|
||||
ONSLEEPQ, // inserted on m_Engine.m_SleepQueue
|
||||
REMDFROMSLPQ, // removed from m_SleepQueue but not yet on m_YieldQueue
|
||||
ONYIELDQ, // inserted on m_Engine.m_YieldQueue
|
||||
FINISHED, // just finished handling an event
|
||||
SUSPENDED, // m_SuspendCount > 0
|
||||
RESETTING, // being reset via external call
|
||||
DISPOSED // has been disposed
|
||||
}
|
||||
|
||||
public partial class XMRInstance : XMRInstAbstract, IDisposable
|
||||
{
|
||||
/******************************************************************\
|
||||
* This module contains the instance variables for XMRInstance. *
|
||||
\******************************************************************/
|
||||
|
||||
public const int MAXEVENTQUEUE = 64;
|
||||
|
||||
public static readonly DetectParams[] zeroDetectParams = new DetectParams[0];
|
||||
public static readonly object[] zeroObjectArray = new object[0];
|
||||
|
||||
public static readonly ILog m_log =
|
||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
// For a given m_Item.AssetID, do we have the compiled object code and where
|
||||
// is it?
|
||||
public static object m_CompileLock = new object();
|
||||
private static Dictionary<string,ScriptObjCode> m_CompiledScriptObjCode = new Dictionary<string,ScriptObjCode>();
|
||||
|
||||
public XMRInstance m_NextInst; // used by XMRInstQueue
|
||||
public XMRInstance m_PrevInst;
|
||||
public XMRInstState m_IState;
|
||||
|
||||
public bool m_ForceRecomp = false;
|
||||
public SceneObjectPart m_Part = null;
|
||||
public uint m_LocalID = 0;
|
||||
public TaskInventoryItem m_Item = null;
|
||||
public UUID m_ItemID;
|
||||
public UUID m_PartUUID;
|
||||
private string m_CameFrom;
|
||||
private string m_ScriptObjCodeKey;
|
||||
|
||||
private XMREngine m_Engine = null;
|
||||
private string m_ScriptBasePath;
|
||||
private string m_StateFileName;
|
||||
public string m_SourceCode;
|
||||
public bool m_PostOnRez;
|
||||
private DetectParams[] m_DetectParams = null;
|
||||
public int m_StartParam = 0;
|
||||
public StateSource m_StateSource;
|
||||
public string m_DescName;
|
||||
private bool[] m_HaveEventHandlers;
|
||||
public int m_StackSize;
|
||||
public int m_HeapSize;
|
||||
private ArrayList m_CompilerErrors;
|
||||
private DateTime m_LastRanAt = DateTime.MinValue;
|
||||
private string m_RunOnePhase = "hasn't run";
|
||||
private string m_CheckRunPhase = "hasn't checked";
|
||||
public int m_InstEHEvent = 0; // number of events dequeued (StartEventHandler called)
|
||||
public int m_InstEHSlice = 0; // number of times handler timesliced (ResumeEx called)
|
||||
public double m_CPUTime = 0; // accumulated CPU time (milliseconds)
|
||||
|
||||
// If code needs to have both m_QueueLock and m_RunLock,
|
||||
// be sure to lock m_RunLock first then m_QueueLock, as
|
||||
// that is the order used in RunOne().
|
||||
// These locks are currently separated to allow the script
|
||||
// to call API routines that queue events back to the script.
|
||||
// If we just had one lock, then the queuing would deadlock.
|
||||
|
||||
// guards m_DetachQuantum, m_EventQueue, m_EventCounts, m_Running, m_Suspended
|
||||
public Object m_QueueLock = new Object();
|
||||
|
||||
// true iff allowed to accept new events
|
||||
public bool m_Running = true;
|
||||
|
||||
// queue of events that haven't been acted upon yet
|
||||
public LinkedList<EventParams> m_EventQueue = new LinkedList<EventParams> ();
|
||||
|
||||
// number of events of each code currently in m_EventQueue.
|
||||
private int[] m_EventCounts = new int[(int)ScriptEventCode.Size];
|
||||
|
||||
// locked whilst running on the microthread stack (or about to run on it or just ran on it)
|
||||
private Object m_RunLock = new Object();
|
||||
|
||||
// script won't step while > 0. bus-atomic updates only.
|
||||
private int m_SuspendCount = 0;
|
||||
|
||||
// don't run any of script until this time
|
||||
// or until one of these events are queued
|
||||
public DateTime m_SleepUntil = DateTime.MinValue;
|
||||
public int m_SleepEventMask1 = 0;
|
||||
public int m_SleepEventMask2 = 0;
|
||||
|
||||
private XMRLSL_Api m_XMRLSLApi;
|
||||
|
||||
/*
|
||||
* We will use this microthread to run the scripts event handlers.
|
||||
*/
|
||||
private IScriptUThread microthread;
|
||||
|
||||
/*
|
||||
* Set to perform migration.
|
||||
*/
|
||||
public bool stackFramesRestored; // set true by CheckRun() when stack has been
|
||||
// restored and is about to suspend the microthread
|
||||
public bool captureStackFrames; // set true to tell CheckRun() to throw a
|
||||
// StackCaptureException() causing it to capture a
|
||||
// snapshot of the script's stack
|
||||
|
||||
/*
|
||||
* Makes sure migration data version is same on both ends.
|
||||
*/
|
||||
public static byte migrationVersion = 10;
|
||||
|
||||
// Incremented each time script gets reset.
|
||||
public int m_ResetCount = 0;
|
||||
|
||||
// Scripts start suspended now. This means that event queues will
|
||||
// accept events, but will not actually run them until the core
|
||||
// tells it it's OK. This is needed to prevent loss of link messages
|
||||
// in complex objects, where no event can be allowed to run until
|
||||
// all possible link message receivers' queues are established.
|
||||
// Guarded by m_QueueLock.
|
||||
public bool m_Suspended = true;
|
||||
|
||||
// We really don't want to save state for a script that hasn't had
|
||||
// a chance to run, because it's state will be blank. That would
|
||||
// cause attachment state loss.
|
||||
public bool m_HasRun = false;
|
||||
|
||||
// When llDie is executed within the attach(NULL_KEY) event of
|
||||
// a script being detached to inventory, the DeleteSceneObject call
|
||||
// it causes will delete the script instances before their state can
|
||||
// be saved. Therefore, the instance needs to know that it's being
|
||||
// detached to inventory, rather than to ground.
|
||||
// Also, the attach(NULL_KEY) event needs to run with priority, and
|
||||
// it also needs to have a limited quantum.
|
||||
// If this is nonzero, we're detaching to inventory.
|
||||
// Guarded by m_QueueLock.
|
||||
private int m_DetachQuantum = 0;
|
||||
|
||||
// Finally, we need to wait until the quantum is done, or the script
|
||||
// suspends itself. This should be efficient, so we use an event
|
||||
// for it instead of spinning busy.
|
||||
// It's born ready, but will be reset when the detach is posted.
|
||||
// It will then be set again on suspend/completion
|
||||
private ManualResetEvent m_DetachReady = new ManualResetEvent(true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.Remoting.Lifetime;
|
||||
using System.Security.Policy;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.ScriptEngine.Interfaces;
|
||||
using OpenSim.Region.ScriptEngine.Shared;
|
||||
using OpenSim.Region.ScriptEngine.Shared.Api;
|
||||
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
|
||||
using OpenSim.Region.ScriptEngine.XMREngine;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using log4net;
|
||||
|
||||
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;
|
||||
|
||||
// This class exists in the main app domain
|
||||
//
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
public partial class XMRInstance
|
||||
{
|
||||
|
||||
// In case Dispose() doesn't get called, we want to be sure to clean
|
||||
// up. This makes sure we decrement m_CompiledScriptRefCount.
|
||||
~XMRInstance()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clean up stuff.
|
||||
* We specifically leave m_DescName intact for 'xmr ls' command.
|
||||
*/
|
||||
public void Dispose()
|
||||
{
|
||||
/*
|
||||
* Tell script stop executing next time it calls CheckRun().
|
||||
*/
|
||||
suspendOnCheckRunHold = true;
|
||||
|
||||
/*
|
||||
* Wait for it to stop executing and prevent it from starting again
|
||||
* as it can't run without a microthread.
|
||||
*/
|
||||
lock (m_RunLock)
|
||||
{
|
||||
if (microthread != null)
|
||||
{
|
||||
m_RunOnePhase = "disposing";
|
||||
CheckRunLockInvariants(true);
|
||||
microthread.Dispose ();
|
||||
microthread = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't send us any more events.
|
||||
*/
|
||||
if (m_Part != null)
|
||||
{
|
||||
xmrTrapRegionCrossing (0);
|
||||
m_Part.RemoveScriptEvents(m_ItemID);
|
||||
AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
|
||||
m_Part = null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let script methods get garbage collected if no one else is using
|
||||
* them.
|
||||
*/
|
||||
DecObjCodeRefCount ();
|
||||
}
|
||||
|
||||
private void DecObjCodeRefCount ()
|
||||
{
|
||||
if (m_ObjCode != null) {
|
||||
lock (m_CompileLock) {
|
||||
ScriptObjCode objCode;
|
||||
|
||||
if (m_CompiledScriptObjCode.TryGetValue (m_ScriptObjCodeKey, out objCode) &&
|
||||
(objCode == m_ObjCode) &&
|
||||
(-- objCode.refCount == 0)) {
|
||||
m_CompiledScriptObjCode.Remove (m_ScriptObjCodeKey);
|
||||
}
|
||||
}
|
||||
m_ObjCode = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Verbose (string format, params object[] args)
|
||||
{
|
||||
if (m_Engine.m_Verbose) m_log.DebugFormat (format, args);
|
||||
}
|
||||
|
||||
// Called by 'xmr top' console command
|
||||
// to dump this script's state to console
|
||||
// Sacha
|
||||
public void RunTestTop()
|
||||
{
|
||||
if (m_InstEHSlice > 0){
|
||||
Console.WriteLine(m_DescName);
|
||||
Console.WriteLine(" m_LocalID = " + m_LocalID);
|
||||
Console.WriteLine(" m_ItemID = " + m_ItemID);
|
||||
Console.WriteLine(" m_Item.AssetID = " + m_Item.AssetID);
|
||||
Console.WriteLine(" m_StartParam = " + m_StartParam);
|
||||
Console.WriteLine(" m_PostOnRez = " + m_PostOnRez);
|
||||
Console.WriteLine(" m_StateSource = " + m_StateSource);
|
||||
Console.WriteLine(" m_SuspendCount = " + m_SuspendCount);
|
||||
Console.WriteLine(" m_SleepUntil = " + m_SleepUntil);
|
||||
Console.WriteLine(" m_IState = " + m_IState.ToString());
|
||||
Console.WriteLine(" m_StateCode = " + GetStateName(stateCode));
|
||||
Console.WriteLine(" eventCode = " + eventCode.ToString());
|
||||
Console.WriteLine(" m_LastRanAt = " + m_LastRanAt.ToString());
|
||||
Console.WriteLine(" heapUsed/Limit = " + xmrHeapUsed () + "/" + heapLimit);
|
||||
Console.WriteLine(" m_InstEHEvent = " + m_InstEHEvent.ToString());
|
||||
Console.WriteLine(" m_InstEHSlice = " + m_InstEHSlice.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Called by 'xmr ls' console command
|
||||
// to dump this script's state to console
|
||||
public string RunTestLs(bool flagFull)
|
||||
{
|
||||
if (flagFull) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine(m_DescName);
|
||||
sb.AppendLine(" m_LocalID = " + m_LocalID);
|
||||
sb.AppendLine(" m_ItemID = " + m_ItemID + " (.state file)");
|
||||
sb.AppendLine(" m_Item.AssetID = " + m_Item.AssetID);
|
||||
sb.AppendLine(" m_Part.WorldPosition = " + m_Part.GetWorldPosition ());
|
||||
sb.AppendLine(" m_ScriptObjCodeKey = " + m_ScriptObjCodeKey + " (source text)");
|
||||
sb.AppendLine(" m_StartParam = " + m_StartParam);
|
||||
sb.AppendLine(" m_PostOnRez = " + m_PostOnRez);
|
||||
sb.AppendLine(" m_StateSource = " + m_StateSource);
|
||||
sb.AppendLine(" m_SuspendCount = " + m_SuspendCount);
|
||||
sb.AppendLine(" m_SleepUntil = " + m_SleepUntil);
|
||||
sb.AppendLine(" m_SleepEvMask1 = 0x" + m_SleepEventMask1.ToString("X"));
|
||||
sb.AppendLine(" m_SleepEvMask2 = 0x" + m_SleepEventMask2.ToString("X"));
|
||||
sb.AppendLine(" m_IState = " + m_IState.ToString());
|
||||
sb.AppendLine(" m_StateCode = " + GetStateName(stateCode));
|
||||
sb.AppendLine(" eventCode = " + eventCode.ToString());
|
||||
sb.AppendLine(" m_LastRanAt = " + m_LastRanAt.ToString());
|
||||
sb.AppendLine(" m_RunOnePhase = " + m_RunOnePhase);
|
||||
sb.AppendLine(" suspOnCkRunHold = " + suspendOnCheckRunHold);
|
||||
sb.AppendLine(" suspOnCkRunTemp = " + suspendOnCheckRunTemp);
|
||||
sb.AppendLine(" m_CheckRunPhase = " + m_CheckRunPhase);
|
||||
sb.AppendLine(" heapUsed/Limit = " + xmrHeapUsed () + "/" + heapLimit);
|
||||
sb.AppendLine(" m_InstEHEvent = " + m_InstEHEvent.ToString());
|
||||
sb.AppendLine(" m_InstEHSlice = " + m_InstEHSlice.ToString());
|
||||
sb.AppendLine(" m_CPUTime = " + m_CPUTime);
|
||||
sb.AppendLine(" callMode = " + callMode);
|
||||
sb.AppendLine(" captureStackFrames = " + captureStackFrames);
|
||||
sb.AppendLine(" stackFramesRestored = " + stackFramesRestored);
|
||||
lock (m_QueueLock)
|
||||
{
|
||||
sb.AppendLine(" m_Running = " + m_Running);
|
||||
foreach (EventParams evt in m_EventQueue)
|
||||
{
|
||||
sb.AppendLine(" evt.EventName = " + evt.EventName);
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
} else {
|
||||
return String.Format("{0} {1} {2} {3} {4} {5}",
|
||||
m_ItemID,
|
||||
m_CPUTime.ToString("F3").PadLeft(9),
|
||||
m_InstEHEvent.ToString().PadLeft(9),
|
||||
m_IState.ToString().PadRight(10),
|
||||
m_Part.GetWorldPosition().ToString().PadRight(32),
|
||||
m_DescName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given stateCode, get a mask of the low 32 event codes
|
||||
* that the state has handlers defined for.
|
||||
*/
|
||||
public int GetStateEventFlags(int stateCode)
|
||||
{
|
||||
if ((stateCode < 0) ||
|
||||
(stateCode >= m_ObjCode.scriptEventHandlerTable.GetLength(0)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int code = 0;
|
||||
for (int i = 0 ; i < 32; i ++)
|
||||
{
|
||||
if (m_ObjCode.scriptEventHandlerTable[stateCode, i] != null)
|
||||
{
|
||||
code |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the .state file name.
|
||||
*/
|
||||
public static string GetStateFileName (string scriptBasePath, UUID itemID)
|
||||
{
|
||||
return GetScriptFileName (scriptBasePath, itemID.ToString() + ".state");
|
||||
}
|
||||
|
||||
public string GetScriptFileName (string filename)
|
||||
{
|
||||
return GetScriptFileName (m_ScriptBasePath, filename);
|
||||
}
|
||||
|
||||
public static string GetScriptFileName (string scriptBasePath, string filename)
|
||||
{
|
||||
/*
|
||||
* Get old path, ie, all files lumped in a single huge directory.
|
||||
*/
|
||||
string oldPath = Path.Combine (scriptBasePath, filename);
|
||||
|
||||
/*
|
||||
* Get new path, ie, files split up based on first 2 chars of name.
|
||||
*/
|
||||
string subdir = filename.Substring (0, 2);
|
||||
filename = filename.Substring (2);
|
||||
scriptBasePath = Path.Combine (scriptBasePath, subdir);
|
||||
Directory.CreateDirectory (scriptBasePath);
|
||||
string newPath = Path.Combine (scriptBasePath, filename);
|
||||
|
||||
/*
|
||||
* If file exists only in old location, move to new location.
|
||||
* If file exists in both locations, delete old location.
|
||||
*/
|
||||
if (File.Exists (oldPath)) {
|
||||
if (File.Exists (newPath)) {
|
||||
File.Delete (oldPath);
|
||||
} else {
|
||||
File.Move (oldPath, newPath);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Always return new location.
|
||||
*/
|
||||
return newPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode state code (int) to state name (string).
|
||||
*/
|
||||
public string GetStateName (int stateCode)
|
||||
{
|
||||
try {
|
||||
return m_ObjCode.stateNames[stateCode];
|
||||
} catch {
|
||||
return stateCode.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief various gets & sets.
|
||||
*/
|
||||
public int StartParam
|
||||
{
|
||||
get { return m_StartParam; }
|
||||
set { m_StartParam = value; }
|
||||
}
|
||||
|
||||
public SceneObjectPart SceneObject
|
||||
{
|
||||
get { return m_Part; }
|
||||
}
|
||||
|
||||
public DetectParams[] DetectParams
|
||||
{
|
||||
get { return m_DetectParams; }
|
||||
set { m_DetectParams = value; }
|
||||
}
|
||||
|
||||
public UUID ItemID
|
||||
{
|
||||
get { return m_ItemID; }
|
||||
}
|
||||
|
||||
public UUID AssetID
|
||||
{
|
||||
get { return m_Item.AssetID; }
|
||||
}
|
||||
|
||||
public bool Running
|
||||
{
|
||||
get { return m_Running; }
|
||||
set
|
||||
{
|
||||
lock (m_QueueLock)
|
||||
{
|
||||
m_Running = value;
|
||||
if (!value)
|
||||
{
|
||||
EmptyEventQueues ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Empty out the event queues.
|
||||
* Assumes caller has the m_QueueLock locked.
|
||||
*/
|
||||
public void EmptyEventQueues ()
|
||||
{
|
||||
m_EventQueue.Clear();
|
||||
for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert an LSL vector to an Openmetaverse vector.
|
||||
*/
|
||||
public static OpenMetaverse.Vector3 LSLVec2OMVec (LSL_Vector lslVec)
|
||||
{
|
||||
return new OpenMetaverse.Vector3 ((float)lslVec.x, (float)lslVec.y, (float)lslVec.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract an integer from an element of an LSL_List.
|
||||
*/
|
||||
public static int ListInt (object element)
|
||||
{
|
||||
if (element is LSL_Integer) {
|
||||
return (int)(LSL_Integer)element;
|
||||
}
|
||||
return (int)element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract a string from an element of an LSL_List.
|
||||
*/
|
||||
public static string ListStr (object element)
|
||||
{
|
||||
if (element is LSL_String) {
|
||||
return (string)(LSL_String)element;
|
||||
}
|
||||
return (string)element;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* 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 System;
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
/**
|
||||
* @brief Implements a queue of XMRInstance's.
|
||||
* Do our own queue to avoid shitty little mallocs.
|
||||
*
|
||||
* Note: looping inst.m_NextInst and m_PrevInst back to itself
|
||||
* when inst is removed from a queue is purely for debug.
|
||||
*/
|
||||
public class XMRInstQueue
|
||||
{
|
||||
private XMRInstance m_Head = null;
|
||||
private XMRInstance m_Tail = null;
|
||||
|
||||
/**
|
||||
* @brief Insert instance at head of queue (in front of all others)
|
||||
* @param inst = instance to insert
|
||||
*/
|
||||
public void InsertHead(XMRInstance inst)
|
||||
{
|
||||
if ((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) {
|
||||
throw new Exception("already in list");
|
||||
}
|
||||
inst.m_PrevInst = null;
|
||||
if ((inst.m_NextInst = m_Head) == null) {
|
||||
m_Tail = inst;
|
||||
} else {
|
||||
m_Head.m_PrevInst = inst;
|
||||
}
|
||||
m_Head = inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert instance at tail of queue (behind all others)
|
||||
* @param inst = instance to insert
|
||||
*/
|
||||
public void InsertTail(XMRInstance inst)
|
||||
{
|
||||
if ((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) {
|
||||
throw new Exception("already in list");
|
||||
}
|
||||
inst.m_NextInst = null;
|
||||
if ((inst.m_PrevInst = m_Tail) == null) {
|
||||
m_Head = inst;
|
||||
} else {
|
||||
m_Tail.m_NextInst = inst;
|
||||
}
|
||||
m_Tail = inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert instance before another element in queue
|
||||
* @param inst = instance to insert
|
||||
* @param after = element that is to come after one being inserted
|
||||
*/
|
||||
public void InsertBefore(XMRInstance inst, XMRInstance after)
|
||||
{
|
||||
if ((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) {
|
||||
throw new Exception("already in list");
|
||||
}
|
||||
if (after == null) {
|
||||
InsertTail(inst);
|
||||
} else {
|
||||
inst.m_NextInst = after;
|
||||
inst.m_PrevInst = after.m_PrevInst;
|
||||
if (inst.m_PrevInst == null) {
|
||||
m_Head = inst;
|
||||
} else {
|
||||
inst.m_PrevInst.m_NextInst = inst;
|
||||
}
|
||||
after.m_PrevInst = inst;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Peek to see if anything in queue
|
||||
* @returns first XMRInstance in queue but doesn't remove it
|
||||
* null if queue is empty
|
||||
*/
|
||||
public XMRInstance PeekHead()
|
||||
{
|
||||
return m_Head;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove first element from queue, if any
|
||||
* @returns null if queue is empty
|
||||
* else returns first element in queue and removes it
|
||||
*/
|
||||
public XMRInstance RemoveHead()
|
||||
{
|
||||
XMRInstance inst = m_Head;
|
||||
if (inst != null) {
|
||||
if ((m_Head = inst.m_NextInst) == null) {
|
||||
m_Tail = null;
|
||||
} else {
|
||||
m_Head.m_PrevInst = null;
|
||||
}
|
||||
inst.m_NextInst = inst;
|
||||
inst.m_PrevInst = inst;
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove last element from queue, if any
|
||||
* @returns null if queue is empty
|
||||
* else returns last element in queue and removes it
|
||||
*/
|
||||
public XMRInstance RemoveTail()
|
||||
{
|
||||
XMRInstance inst = m_Tail;
|
||||
if (inst != null) {
|
||||
if ((m_Tail = inst.m_PrevInst) == null) {
|
||||
m_Head = null;
|
||||
} else {
|
||||
m_Tail.m_NextInst = null;
|
||||
}
|
||||
inst.m_NextInst = inst;
|
||||
inst.m_PrevInst = inst;
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove arbitrary element from queue, if any
|
||||
* @param inst = element to remove (assumed to be in the queue)
|
||||
* @returns with element removed
|
||||
*/
|
||||
public void Remove(XMRInstance inst)
|
||||
{
|
||||
XMRInstance next = inst.m_NextInst;
|
||||
XMRInstance prev = inst.m_PrevInst;
|
||||
if ((prev == inst) || (next == inst)) {
|
||||
throw new Exception("not in a list");
|
||||
}
|
||||
if (next == null) {
|
||||
if (m_Tail != inst) {
|
||||
throw new Exception("not in this list");
|
||||
}
|
||||
m_Tail = prev;
|
||||
} else {
|
||||
next.m_PrevInst = prev;
|
||||
}
|
||||
if (prev == null) {
|
||||
if (m_Head != inst) {
|
||||
throw new Exception("not in this list");
|
||||
}
|
||||
m_Head = next;
|
||||
} else {
|
||||
prev.m_NextInst = next;
|
||||
}
|
||||
inst.m_NextInst = inst;
|
||||
inst.m_PrevInst = inst;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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 OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.ScriptEngine.Shared;
|
||||
using System;
|
||||
|
||||
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 partial class XMRInstance
|
||||
{
|
||||
/**
|
||||
* @brief If RegionCrossing trapping is enabled, any attempt to move the object
|
||||
* outside its current region will cause the event to fire and the object
|
||||
* will remain in its current region.
|
||||
*/
|
||||
public override void xmrTrapRegionCrossing (int en)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Move object to new position and rotation asynchronously.
|
||||
* Can move object across region boundary.
|
||||
* @param pos = new position within current region (same coords as llGetPos())
|
||||
* @param rot = new rotation within current region (same coords as llGetRot())
|
||||
* @param options = not used
|
||||
* @param evcode = not used
|
||||
* @param evargs = arguments to pass to event handler
|
||||
* @returns false: completed synchronously, no event will be queued
|
||||
*/
|
||||
public const double Sorpra_MIN_CROSS = 1.0 / 512.0; // ie, ~2mm
|
||||
public const int Sorpra_TIMEOUT_MS = 30000; // ie, 30sec
|
||||
public override bool xmrSetObjRegPosRotAsync (LSL_Vector pos, LSL_Rotation rot, int options, int evcode, LSL_List evargs)
|
||||
{
|
||||
// do the move
|
||||
SceneObjectGroup sog = m_Part.ParentGroup;
|
||||
sog.UpdateGroupRotationPR (pos, rot);
|
||||
|
||||
// it is always synchronous
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* 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 System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
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 XMRSDTypeClObj
|
||||
{
|
||||
/*
|
||||
* Which script instance we are part of so we can access
|
||||
* the script's global variables and functions.
|
||||
*/
|
||||
public XMRInstAbstract xmrInst;
|
||||
|
||||
/*
|
||||
* What class we actually are in the hierarchy
|
||||
* used for casting.
|
||||
*/
|
||||
public TokenDeclSDTypeClass sdtcClass;
|
||||
|
||||
/*
|
||||
* Our VTable array, used for calling virtual functions.
|
||||
* And ITable array, used for calling our implementation of interface functions.
|
||||
*/
|
||||
public Delegate[] sdtcVTable;
|
||||
public Delegate[][] sdtcITable;
|
||||
|
||||
/*
|
||||
* These arrays hold the insance variable values.
|
||||
* The array lengths are determined by the script compilation,
|
||||
* and are found in TokenDeclSDTypeClass.instSizes.
|
||||
*/
|
||||
public XMRInstArrays instVars;
|
||||
|
||||
/**
|
||||
* @brief Called by script's $new() to initialize a new object.
|
||||
*/
|
||||
public XMRSDTypeClObj (XMRInstAbstract inst, int classindex)
|
||||
{
|
||||
Construct (inst, classindex);
|
||||
instVars.AllocVarArrays (sdtcClass.instSizes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set up everything except the instVars arrays.
|
||||
* @param inst = script instance this object is part of
|
||||
* @param classindex = which script-defined type class this object is an onstance of
|
||||
* @returns with the vtables filled in
|
||||
*/
|
||||
private void Construct (XMRInstAbstract inst, int classindex)
|
||||
{
|
||||
Delegate[] thisMid = null;
|
||||
TokenDeclSDTypeClass clas = (TokenDeclSDTypeClass)inst.m_ObjCode.sdObjTypesIndx[classindex];
|
||||
|
||||
xmrInst = inst;
|
||||
sdtcClass = clas;
|
||||
instVars = new XMRInstArrays (inst);
|
||||
|
||||
/*
|
||||
* VTable consists of delegates built from DynamicMethods and the 'this' pointer.
|
||||
* Yes, yes, lots of shitty little mallocs.
|
||||
*/
|
||||
DynamicMethod[] vDynMeths = clas.vDynMeths;
|
||||
if (vDynMeths != null) {
|
||||
int n = vDynMeths.Length;
|
||||
Type[] vMethTypes = clas.vMethTypes;
|
||||
sdtcVTable = new Delegate[n];
|
||||
for (int i = 0; i < n; i ++) {
|
||||
sdtcVTable[i] = vDynMeths[i].CreateDelegate (vMethTypes[i], this);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in interface vtables.
|
||||
* There is one array of delegates for each implemented interface.
|
||||
* The array of delegates IS the interface's object, ie, the 'this' value of the interface.
|
||||
* To cast from the class type to the interface type, just get the correct array from the table.
|
||||
* To cast from the interface type to the class type, just get Target of entry 0.
|
||||
*
|
||||
* So we end up with this:
|
||||
* sdtcITable[interfacenumber][methodofintfnumber] = delegate of this.ourimplementationofinterfacesmethod
|
||||
*/
|
||||
if (clas.iDynMeths != null) {
|
||||
int nIFaces = clas.iDynMeths.Length;
|
||||
sdtcITable = new Delegate[nIFaces][];
|
||||
for (int i = 0; i < nIFaces; i ++) {
|
||||
|
||||
// get vector of entrypoints of our instance methods that implement that interface
|
||||
DynamicMethod[] iDynMeths = clas.iDynMeths[i];
|
||||
Type[] iMethTypes = clas.iMethTypes[i];
|
||||
|
||||
// allocate an array with a slot for each method the interface defines
|
||||
int nMeths = iDynMeths.Length;
|
||||
Delegate[] ivec;
|
||||
if (nMeths > 0) {
|
||||
// fill in the array with delegates that reference back to this class instance
|
||||
ivec = new Delegate[nMeths];
|
||||
for (int j = 0; j < nMeths; j ++) {
|
||||
ivec[j] = iDynMeths[j].CreateDelegate (iMethTypes[j], this);
|
||||
}
|
||||
} else {
|
||||
// just a marker interface with no methods,
|
||||
// allocate a one-element array and fill
|
||||
// with a dummy entry. this will allow casting
|
||||
// back to the original class instance (this)
|
||||
// by reading Target of entry 0.
|
||||
if (thisMid == null) {
|
||||
thisMid = new Delegate[1];
|
||||
thisMid[0] = markerInterfaceDummy.CreateDelegate (typeof (MarkerInterfaceDummy), this);
|
||||
}
|
||||
ivec = thisMid;
|
||||
}
|
||||
|
||||
// save whatever we ended up allocating
|
||||
sdtcITable[i] = ivec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private delegate void MarkerInterfaceDummy ();
|
||||
private static DynamicMethod markerInterfaceDummy = MakeMarkerInterfaceDummy ();
|
||||
private static DynamicMethod MakeMarkerInterfaceDummy ()
|
||||
{
|
||||
DynamicMethod dm = new DynamicMethod ("XMRSDTypeClObj.MarkerInterfaceDummy", null, new Type[] { typeof (XMRSDTypeClObj) });
|
||||
ILGenerator ilGen = dm.GetILGenerator ();
|
||||
ilGen.Emit (OpCodes.Ret);
|
||||
return dm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform runtime casting of script-defined interface object to
|
||||
* its corresponding script-defined class object.
|
||||
* @param da = interface object (array of delegates pointing to class's implementations of interface's methods)
|
||||
* @param classindex = what class those implementations are supposedly part of
|
||||
* @returns original script-defined class object
|
||||
*/
|
||||
public static XMRSDTypeClObj CastIFace2Class (Delegate[] da, int classindex)
|
||||
{
|
||||
return CastClass2Class (da[0].Target, classindex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform runtime casting of XMRSDTypeClObj's.
|
||||
* @param ob = XMRSDTypeClObj of unknown script-defined class to cast
|
||||
* @param classindex = script-defined class to cast it to
|
||||
* @returns ob is a valid instance of classindex; else exception thrown
|
||||
*/
|
||||
public static XMRSDTypeClObj CastClass2Class (object ob, int classindex)
|
||||
{
|
||||
/*
|
||||
* Let mono check to see if we at least have an XMRSDTypeClObj.
|
||||
*/
|
||||
XMRSDTypeClObj ci = (XMRSDTypeClObj)ob;
|
||||
if (ci != null) {
|
||||
|
||||
/*
|
||||
* This is the target class, ie, what we are hoping the object can cast to.
|
||||
*/
|
||||
TokenDeclSDTypeClass tc = (TokenDeclSDTypeClass)ci.xmrInst.m_ObjCode.sdObjTypesIndx[classindex];
|
||||
|
||||
/*
|
||||
* Step from the object's actual class rootward.
|
||||
* If we find the target 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 = ci.sdtcClass; ac != tc; ac = ac.extends) {
|
||||
if (ac == null) throw new InvalidCastException ("invalid cast from " + ci.sdtcClass.longName.val +
|
||||
" to " + tc.longName.val);
|
||||
}
|
||||
|
||||
/*
|
||||
* The target class is at or rootward of the actual class,
|
||||
* so the cast is valid.
|
||||
*/
|
||||
}
|
||||
return ci;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cast an arbitrary object to the given interface.
|
||||
* @param ob = object to be cast of unknown type
|
||||
* @returns ob cast to the interface type
|
||||
*/
|
||||
public static Delegate[] CastObj2IFace (object ob, string ifacename)
|
||||
{
|
||||
if (ob == null) return null;
|
||||
|
||||
/*
|
||||
* If it is already one of our interfaces, extract the script-defined class object from it.
|
||||
*/
|
||||
if (ob is Delegate[]) {
|
||||
Delegate[] da = (Delegate[])ob;
|
||||
ob = da[0].Target;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that we have a presumed script-defined class object, cast that to the requested interface
|
||||
* by picking the array of delegates that corresponds to the requested interface.
|
||||
*/
|
||||
XMRSDTypeClObj ci = (XMRSDTypeClObj)ob;
|
||||
int iFaceIndex = ci.sdtcClass.intfIndices[ifacename];
|
||||
return ci.sdtcITable[iFaceIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write the whole thing out to a stream.
|
||||
*/
|
||||
public void Capture (XMRInstArrays.Sender sendValue)
|
||||
{
|
||||
sendValue (this.sdtcClass.sdTypeIndex);
|
||||
this.instVars.SendArrays (sendValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the whole thing in from a stream.
|
||||
*/
|
||||
public XMRSDTypeClObj () { }
|
||||
public void Restore (XMRInstAbstract inst, XMRInstArrays.Recver recvValue)
|
||||
{
|
||||
int classindex = (int)recvValue ();
|
||||
Construct (inst, classindex);
|
||||
this.instVars.RecvArrays (recvValue);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* 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 Mono.Tasklets;
|
||||
using OpenSim.Framework.Monitoring;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief There are NUMSCRIPTHREADWKRS of these.
|
||||
* Each sits in a loop checking the Start and Yield queues for
|
||||
* a script to run and calls the script as a microthread.
|
||||
*/
|
||||
public class XMRScriptThread {
|
||||
private static int m_WakeUpOne = 0;
|
||||
public static object m_WakeUpLock = new object();
|
||||
private static Dictionary<Thread,XMRScriptThread> m_AllThreads = new Dictionary<Thread,XMRScriptThread> ();
|
||||
|
||||
/**
|
||||
* @brief Something was just added to the Start or Yield queue so
|
||||
* wake one of the XMRScriptThread instances to run it.
|
||||
*/
|
||||
public static void WakeUpOne()
|
||||
{
|
||||
lock (m_WakeUpLock)
|
||||
{
|
||||
m_WakeUpOne ++;
|
||||
Monitor.Pulse (m_WakeUpLock);
|
||||
}
|
||||
}
|
||||
|
||||
public static XMRScriptThread CurrentScriptThread ()
|
||||
{
|
||||
XMRScriptThread st;
|
||||
lock (m_AllThreads) {
|
||||
m_AllThreads.TryGetValue (Thread.CurrentThread, out st);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
private bool m_Exiting = false;
|
||||
private bool m_SuspendScriptThreadFlag = false;
|
||||
private bool m_WakeUpThis = false;
|
||||
private bool m_Continuations = false;
|
||||
public DateTime m_LastRanAt = DateTime.MinValue;
|
||||
public int m_ScriptThreadTID = 0;
|
||||
public long m_ScriptExecTime = 0;
|
||||
private Thread thd;
|
||||
private XMREngine engine;
|
||||
public XMRInstance m_RunInstance = null;
|
||||
|
||||
public XMRScriptThread(XMREngine eng)
|
||||
{
|
||||
engine = eng;
|
||||
m_Continuations = engine.uThreadCtor.DeclaringType == typeof (ScriptUThread_Con);
|
||||
thd = XMREngine.StartMyThread (RunScriptThread, "xmrengine script", ThreadPriority.BelowNormal);
|
||||
lock (m_AllThreads) {
|
||||
m_AllThreads.Add (thd, this);
|
||||
}
|
||||
}
|
||||
|
||||
public void SuspendThread()
|
||||
{
|
||||
m_SuspendScriptThreadFlag = true;
|
||||
WakeUpScriptThread();
|
||||
}
|
||||
|
||||
public void ResumeThread()
|
||||
{
|
||||
m_SuspendScriptThreadFlag = false;
|
||||
WakeUpScriptThread();
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
m_Exiting = true;
|
||||
WakeUpScriptThread();
|
||||
thd.Join();
|
||||
lock (m_AllThreads) {
|
||||
m_AllThreads.Remove (thd);
|
||||
}
|
||||
thd = null;
|
||||
}
|
||||
|
||||
public void TimeSlice()
|
||||
{
|
||||
XMRInstance instance = m_RunInstance;
|
||||
if (instance != null) {
|
||||
instance.suspendOnCheckRunTemp = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wake up this XMRScriptThread instance.
|
||||
*/
|
||||
private void WakeUpScriptThread()
|
||||
{
|
||||
lock (m_WakeUpLock) {
|
||||
m_WakeUpThis = true;
|
||||
Monitor.PulseAll (m_WakeUpLock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Thread that runs the scripts.
|
||||
*/
|
||||
private void RunScriptThread()
|
||||
{
|
||||
XMRInstance inst;
|
||||
Mono.Tasklets.Continuation engstack = null;
|
||||
if (m_Continuations) {
|
||||
engstack = new Mono.Tasklets.Continuation ();
|
||||
engstack.Mark ();
|
||||
}
|
||||
m_ScriptThreadTID = System.Threading.Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
while (!m_Exiting) {
|
||||
XMREngine.UpdateMyThread ();
|
||||
|
||||
/*
|
||||
* Handle 'xmr resume/suspend' commands.
|
||||
*/
|
||||
if (m_SuspendScriptThreadFlag) {
|
||||
lock (m_WakeUpLock) {
|
||||
while (m_SuspendScriptThreadFlag &&
|
||||
!m_Exiting &&
|
||||
(engine.m_ThunkQueue.Count == 0)) {
|
||||
Monitor.Wait (m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
|
||||
XMREngine.UpdateMyThread ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Maybe there are some scripts waiting to be migrated in or out.
|
||||
*/
|
||||
ThreadStart thunk = null;
|
||||
lock (m_WakeUpLock) {
|
||||
if (engine.m_ThunkQueue.Count > 0) {
|
||||
thunk = engine.m_ThunkQueue.Dequeue ();
|
||||
}
|
||||
}
|
||||
if (thunk != null) {
|
||||
inst = (XMRInstance)thunk.Target;
|
||||
if (m_Continuations && (inst.scrstack == null)) {
|
||||
inst.engstack = engstack;
|
||||
inst.scrstack = new Mono.Tasklets.Continuation ();
|
||||
inst.scrstack.Mark ();
|
||||
}
|
||||
thunk ();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (engine.m_StartProcessing) {
|
||||
|
||||
/*
|
||||
* If event just queued to any idle scripts
|
||||
* start them right away. But only start so
|
||||
* many so we can make some progress on yield
|
||||
* queue.
|
||||
*/
|
||||
int numStarts;
|
||||
for (numStarts = 5; -- numStarts >= 0;) {
|
||||
lock (engine.m_StartQueue) {
|
||||
inst = engine.m_StartQueue.RemoveHead();
|
||||
}
|
||||
if (inst == null) break;
|
||||
if (inst.m_IState != XMRInstState.ONSTARTQ) throw new Exception("bad state");
|
||||
if (m_Continuations && (inst.scrstack == null)) {
|
||||
inst.engstack = engstack;
|
||||
inst.scrstack = new Mono.Tasklets.Continuation ();
|
||||
inst.scrstack.Mark ();
|
||||
}
|
||||
RunInstance (inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is something to run, run it
|
||||
* then rescan from the beginning in case
|
||||
* a lot of things have changed meanwhile.
|
||||
*
|
||||
* These are considered lower priority than
|
||||
* m_StartQueue as they have been taking at
|
||||
* least one quantum of CPU time and event
|
||||
* handlers are supposed to be quick.
|
||||
*/
|
||||
lock (engine.m_YieldQueue) {
|
||||
inst = engine.m_YieldQueue.RemoveHead();
|
||||
}
|
||||
if (inst != null) {
|
||||
if (inst.m_IState != XMRInstState.ONYIELDQ) throw new Exception("bad state");
|
||||
RunInstance (inst);
|
||||
numStarts = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we left something dangling in the m_StartQueue or m_YieldQueue, go back to check it.
|
||||
*/
|
||||
if (numStarts < 0) continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nothing to do, sleep.
|
||||
*/
|
||||
lock (m_WakeUpLock) {
|
||||
if (!m_WakeUpThis && (m_WakeUpOne <= 0) && !m_Exiting) {
|
||||
Monitor.Wait (m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
|
||||
}
|
||||
m_WakeUpThis = false;
|
||||
if ((m_WakeUpOne > 0) && (-- m_WakeUpOne > 0)) {
|
||||
Monitor.Pulse (m_WakeUpLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
XMREngine.MyThreadExiting ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A script instance was just removed from the Start or Yield Queue.
|
||||
* So run it for a little bit then stick in whatever queue it should go in.
|
||||
*/
|
||||
private void RunInstance (XMRInstance inst)
|
||||
{
|
||||
m_LastRanAt = DateTime.UtcNow;
|
||||
m_ScriptExecTime -= (long)(m_LastRanAt - DateTime.MinValue).TotalMilliseconds;
|
||||
inst.m_IState = XMRInstState.RUNNING;
|
||||
m_RunInstance = inst;
|
||||
XMRInstState newIState = inst.RunOne();
|
||||
m_RunInstance = null;
|
||||
engine.HandleNewIState(inst, newIState);
|
||||
m_ScriptExecTime += (long)(DateTime.UtcNow - DateTime.MinValue).TotalMilliseconds;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,557 @@
|
|||
/*
|
||||
* 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 Mono.Tasklets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
|
||||
/***************************\
|
||||
* Use standard C# code *
|
||||
* - uses system threads *
|
||||
\***************************/
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine {
|
||||
|
||||
public class ScriptUThread_Sys : IScriptUThread, IDisposable
|
||||
{
|
||||
private Exception except;
|
||||
private int active; // -1: hibernating
|
||||
// 0: exited
|
||||
// 1: running
|
||||
private object activeLock = new object ();
|
||||
private XMRInstance instance;
|
||||
|
||||
public ScriptUThread_Sys (XMRInstance instance)
|
||||
{
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start script event handler from the beginning.
|
||||
* Return when either the script event handler completes
|
||||
* or the script calls Hiber().
|
||||
* @returns null: script did not throw any exception so far
|
||||
* else: script threw an exception
|
||||
*/
|
||||
public Exception StartEx ()
|
||||
{
|
||||
lock (activeLock) {
|
||||
|
||||
/*
|
||||
* We should only be called when script is inactive.
|
||||
*/
|
||||
if (active != 0) throw new Exception ("active=" + active);
|
||||
|
||||
/*
|
||||
* Tell CallSEHThread() to run script event handler in a thread.
|
||||
*/
|
||||
active = 1;
|
||||
TredPoo.RunSomething (CallSEHThread);
|
||||
|
||||
/*
|
||||
* Wait for script to call Hiber() or for script to
|
||||
* return back out to CallSEHThread().
|
||||
*/
|
||||
while (active > 0) {
|
||||
Monitor.Wait (activeLock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether or not script threw an exception.
|
||||
*/
|
||||
return except;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief We now want to run some more script code from where it last hibernated
|
||||
* until it either finishes the script event handler or until the script
|
||||
* calls Hiber() again.
|
||||
*/
|
||||
public Exception ResumeEx ()
|
||||
{
|
||||
lock (activeLock) {
|
||||
|
||||
/*
|
||||
* We should only be called when script is hibernating.
|
||||
*/
|
||||
if (active >= 0) throw new Exception ("active=" + active);
|
||||
|
||||
/*
|
||||
* Tell Hiber() to return back to script.
|
||||
*/
|
||||
active = 1;
|
||||
Monitor.PulseAll (activeLock);
|
||||
|
||||
/*
|
||||
* Wait for script to call Hiber() again or for script to
|
||||
* return back out to CallSEHThread().
|
||||
*/
|
||||
while (active > 0) {
|
||||
Monitor.Wait (activeLock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether or not script threw an exception.
|
||||
*/
|
||||
return except;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Script is being closed out.
|
||||
* Terminate thread asap.
|
||||
*/
|
||||
public void Dispose ()
|
||||
{
|
||||
lock (activeLock) {
|
||||
instance = null;
|
||||
Monitor.PulseAll (activeLock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine if script is active.
|
||||
* Returns: 0: nothing started or has returned
|
||||
* Resume() must not be called
|
||||
* Start() may be called
|
||||
* Hiber() must not be called
|
||||
* -1: thread has called Hiber()
|
||||
* Resume() may be called
|
||||
* Start() may be called
|
||||
* Hiber() must not be called
|
||||
* 1: thread is running
|
||||
* Resume() must not be called
|
||||
* Start() must not be called
|
||||
* Hiber() may be called
|
||||
*/
|
||||
public int Active ()
|
||||
{
|
||||
return active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This thread executes the script event handler code.
|
||||
*/
|
||||
private void CallSEHThread ()
|
||||
{
|
||||
lock (activeLock) {
|
||||
if (active <= 0) throw new Exception ("active=" + active);
|
||||
|
||||
except = null; // assume completion without exception
|
||||
try {
|
||||
instance.CallSEH (); // run script event handler
|
||||
} catch (Exception e) {
|
||||
except = e; // threw exception, save for Start()/Resume()
|
||||
}
|
||||
|
||||
active = 0; // tell Start() or Resume() we're done
|
||||
Monitor.PulseAll (activeLock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called by the script event handler whenever it wants to hibernate.
|
||||
*/
|
||||
public void Hiber ()
|
||||
{
|
||||
if (active <= 0) throw new Exception ("active=" + active);
|
||||
|
||||
// tell Start() or Resume() we are hibernating
|
||||
active = -1;
|
||||
Monitor.PulseAll (activeLock);
|
||||
|
||||
// wait for Resume() or Dispose() to be called
|
||||
while ((active < 0) && (instance != null)) {
|
||||
Monitor.Wait (activeLock);
|
||||
}
|
||||
|
||||
// don't execute any more script code, just exit
|
||||
if (instance == null) {
|
||||
throw new AbortedByDisposeException ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Number of remaining stack bytes.
|
||||
*/
|
||||
public int StackLeft ()
|
||||
{
|
||||
return 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
public class AbortedByDisposeException : Exception, IXMRUncatchable { }
|
||||
|
||||
/**
|
||||
* @brief Pool of threads that run script event handlers.
|
||||
*/
|
||||
private class TredPoo {
|
||||
private static readonly TimeSpan idleTimeSpan = new TimeSpan (0, 0, 1, 0, 0); // 1 minute
|
||||
|
||||
private static int tredPooAvail = 0;
|
||||
private static object tredPooLock = new object ();
|
||||
private static Queue<ThreadStart> tredPooQueue = new Queue<ThreadStart> ();
|
||||
|
||||
/**
|
||||
* @brief Queue a function for execution in a system thread.
|
||||
*/
|
||||
public static void RunSomething (ThreadStart entry)
|
||||
{
|
||||
lock (tredPooLock) {
|
||||
tredPooQueue.Enqueue (entry);
|
||||
Monitor.Pulse (tredPooLock);
|
||||
if (tredPooAvail < tredPooQueue.Count) {
|
||||
new TredPoo ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start a new system thread.
|
||||
* It will shortly attempt to dequeue work or if none,
|
||||
* add itself to the available thread list.
|
||||
*/
|
||||
private TredPoo ()
|
||||
{
|
||||
Thread thread = new Thread (Main);
|
||||
thread.Name = "XMRUThread_sys";
|
||||
thread.IsBackground = true;
|
||||
thread.Start ();
|
||||
tredPooAvail ++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Executes items from the queue or waits a little while
|
||||
* if nothing. If idle for a while, it exits.
|
||||
*/
|
||||
private void Main ()
|
||||
{
|
||||
int first = 1;
|
||||
ThreadStart entry;
|
||||
while (true) {
|
||||
lock (tredPooLock) {
|
||||
tredPooAvail -= first;
|
||||
first = 0;
|
||||
while (tredPooQueue.Count <= 0) {
|
||||
tredPooAvail ++;
|
||||
bool keepgoing = Monitor.Wait (tredPooLock, idleTimeSpan);
|
||||
-- tredPooAvail;
|
||||
if (!keepgoing) return;
|
||||
}
|
||||
entry = tredPooQueue.Dequeue ();
|
||||
}
|
||||
entry ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*************************************\
|
||||
* Use Mono.Tasklets.Continuations *
|
||||
* - memcpy's stack *
|
||||
\*************************************/
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine {
|
||||
|
||||
public partial class XMRInstance {
|
||||
public Mono.Tasklets.Continuation engstack;
|
||||
public Mono.Tasklets.Continuation scrstack;
|
||||
}
|
||||
|
||||
public class ScriptUThread_Con : IScriptUThread, IDisposable
|
||||
{
|
||||
private XMRInstance instance;
|
||||
|
||||
public ScriptUThread_Con (XMRInstance instance)
|
||||
{
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
private const int SAVEENGINESTACK = 0;
|
||||
private const int LOADENGINESTACK = 1;
|
||||
private const int SAVESCRIPTSTACK = 2;
|
||||
private const int LOADSCRIPTSTACK = 3;
|
||||
|
||||
private Exception except;
|
||||
private int active;
|
||||
|
||||
/**
|
||||
* @brief Start script event handler from the beginning.
|
||||
* Return when either the script event handler completes
|
||||
* or the script calls Hiber().
|
||||
* @returns null: script did not throw any exception so far
|
||||
* else: script threw an exception
|
||||
*/
|
||||
public Exception StartEx ()
|
||||
{
|
||||
/*
|
||||
* Save engine stack so we know how to jump back to engine in case
|
||||
* the script calls Hiber().
|
||||
*/
|
||||
switch (instance.engstack.Store (SAVEENGINESTACK)) {
|
||||
|
||||
/*
|
||||
* Engine stack has been saved, start running the event handler.
|
||||
*/
|
||||
case SAVEENGINESTACK: {
|
||||
|
||||
/*
|
||||
* Run event handler according to stackFrames.
|
||||
* In either case it is assumed that stateCode and eventCode
|
||||
* indicate which event handler is to be called and that ehArgs
|
||||
* points to the event handler argument list.
|
||||
*/
|
||||
active = 1;
|
||||
except = null;
|
||||
try {
|
||||
instance.CallSEH ();
|
||||
} catch (Exception e) {
|
||||
except = e;
|
||||
}
|
||||
|
||||
/*
|
||||
* We now want to return to the script engine.
|
||||
* Setting active = 0 means the microthread has exited.
|
||||
* We need to call engstack.Restore() in case the script called Hiber()
|
||||
* anywhere, we want to return out the corresponding Restore() and not the
|
||||
* Start().
|
||||
*/
|
||||
active = 0;
|
||||
instance.engstack.Restore (LOADENGINESTACK);
|
||||
throw new Exception ("returned from Restore()");
|
||||
}
|
||||
|
||||
/*
|
||||
* Script called Hiber() somewhere so just return back out.
|
||||
*/
|
||||
case LOADENGINESTACK: {
|
||||
break;
|
||||
}
|
||||
|
||||
default: throw new Exception ("bad engstack code");
|
||||
}
|
||||
|
||||
return except;
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Determine if script is active.
|
||||
* Returns: 0: nothing started or has returned
|
||||
* Resume() must not be called
|
||||
* Start() may be called
|
||||
* Hiber() must not be called
|
||||
* -1: thread has called Hiber()
|
||||
* Resume() may be called
|
||||
* Start() may be called
|
||||
* Hiber() must not be called
|
||||
* 1: thread is running
|
||||
* Resume() must not be called
|
||||
* Start() must not be called
|
||||
* Hiber() may be called
|
||||
*/
|
||||
public int Active ()
|
||||
{
|
||||
return active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called by the script wherever it wants to hibernate.
|
||||
* So this means to save the scripts stack in 'instance.scrstack' then
|
||||
* restore the engstack to cause us to return back to the engine.
|
||||
*/
|
||||
public void Hiber ()
|
||||
{
|
||||
/*
|
||||
* Save where we are in the script's code in 'instance.scrstack'
|
||||
* so we can wake the script when Resume() is called.
|
||||
*/
|
||||
switch (instance.scrstack.Store (SAVESCRIPTSTACK)) {
|
||||
|
||||
/*
|
||||
* Script's stack is now saved in 'instance.scrstack'.
|
||||
* Reload the engine's stack from 'instance.engstack' and jump to it.
|
||||
*/
|
||||
case SAVESCRIPTSTACK: {
|
||||
active = -1;
|
||||
instance.engstack.Restore (LOADENGINESTACK);
|
||||
throw new Exception ("returned from Restore()");
|
||||
}
|
||||
|
||||
/*
|
||||
* Resume() was just called and we want to resume executing script code.
|
||||
*/
|
||||
case LOADSCRIPTSTACK: {
|
||||
break;
|
||||
}
|
||||
|
||||
default: throw new Exception ("bad scrstack code");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief We now want to run some more script code from where it last hibernated
|
||||
* until it either finishes the script event handler or until the script
|
||||
* calls Hiber() again.
|
||||
*/
|
||||
public Exception ResumeEx ()
|
||||
{
|
||||
/*
|
||||
* Save where we are in the engine's code in 'instance.engstack'
|
||||
* so if the script calls Hiber() again or exits, we know how to get
|
||||
* back to the engine.
|
||||
*/
|
||||
switch (instance.engstack.Store (SAVEENGINESTACK)) {
|
||||
|
||||
/*
|
||||
* This is original call to Resume() from the engine,
|
||||
* jump to where we left off within Hiber().
|
||||
*/
|
||||
case SAVEENGINESTACK: {
|
||||
active = 1;
|
||||
instance.scrstack.Restore (LOADSCRIPTSTACK);
|
||||
throw new Exception ("returned from Restore()");
|
||||
}
|
||||
|
||||
/*
|
||||
* Script has called Hiber() again, so return back to
|
||||
* script engine code.
|
||||
*/
|
||||
case LOADENGINESTACK: {
|
||||
break;
|
||||
}
|
||||
|
||||
default: throw new Exception ("bad engstack code");
|
||||
}
|
||||
|
||||
return except;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Number of remaining stack bytes.
|
||||
*/
|
||||
public int StackLeft ()
|
||||
{
|
||||
return 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************\
|
||||
* Use Mono.Tasklets.MMRUThreads *
|
||||
* - switches stack pointer *
|
||||
\***********************************/
|
||||
|
||||
namespace OpenSim.Region.ScriptEngine.XMREngine {
|
||||
|
||||
public class ScriptUThread_MMR : IScriptUThread, IDisposable
|
||||
{
|
||||
private static Exception uthread_looked;
|
||||
private static Type uttype;
|
||||
private static Type uthread_entry;
|
||||
private static MethodInfo uthread_dispose;
|
||||
private static MethodInfo uthread_startex;
|
||||
private static MethodInfo uthread_resumex;
|
||||
private static MethodInfo uthread_suspend;
|
||||
private static MethodInfo uthread_active;
|
||||
private static MethodInfo uthread_stackleft;
|
||||
|
||||
public static Exception LoadMono ()
|
||||
{
|
||||
if ((uthread_looked == null) && (uthread_stackleft == null)) {
|
||||
try {
|
||||
Assembly mt = Assembly.Load ("Mono.Tasklets");
|
||||
uttype = mt.GetType ("Mono.Tasklets.MMRUThread", true);
|
||||
uthread_entry = mt.GetType ("Mono.Tasklets.MMRUThread+Entry", true);
|
||||
|
||||
uthread_dispose = uttype.GetMethod ("Dispose"); // no parameters, no return value
|
||||
uthread_startex = uttype.GetMethod ("StartEx"); // takes uthread_entry delegate as parameter, returns exception
|
||||
uthread_resumex = uttype.GetMethod ("ResumeEx"); // takes exception as parameter, returns exception
|
||||
uthread_suspend = uttype.GetMethod ("Suspend", new Type[] { }); // no return value
|
||||
uthread_active = uttype.GetMethod ("Active"); // no parameters, returns int
|
||||
uthread_stackleft = uttype.GetMethod ("StackLeft"); // no parameters, returns IntPtr
|
||||
} catch (Exception e) {
|
||||
uthread_looked = new NotSupportedException ("'mmr' thread model requires patched mono", e);
|
||||
}
|
||||
}
|
||||
return uthread_looked;
|
||||
}
|
||||
|
||||
private static object[] resumex_args = new object[] { null };
|
||||
|
||||
private object uthread; // type MMRUThread
|
||||
private object[] startex_args = new object[1];
|
||||
|
||||
public ScriptUThread_MMR (XMRInstance instance)
|
||||
{
|
||||
this.uthread = Activator.CreateInstance (uttype, new object[] { (IntPtr) instance.m_StackSize, instance.m_DescName });
|
||||
startex_args[0] = Delegate.CreateDelegate (uthread_entry, instance, "CallSEH");
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
uthread_dispose.Invoke (uthread, null);
|
||||
uthread = null;
|
||||
}
|
||||
|
||||
public Exception StartEx ()
|
||||
{
|
||||
return (Exception) uthread_startex.Invoke (uthread, startex_args);
|
||||
}
|
||||
|
||||
public Exception ResumeEx ()
|
||||
{
|
||||
return (Exception) uthread_resumex.Invoke (uthread, resumex_args);
|
||||
}
|
||||
|
||||
public void Hiber ()
|
||||
{
|
||||
uthread_suspend.Invoke (null, null);
|
||||
}
|
||||
|
||||
public int Active ()
|
||||
{
|
||||
return (int) uthread_active.Invoke (uthread, null);
|
||||
}
|
||||
|
||||
public int StackLeft ()
|
||||
{
|
||||
return (int) (IntPtr) uthread_stackleft.Invoke (null, null);
|
||||
}
|
||||
}
|
||||
}
|
67
prebuild.xml
67
prebuild.xml
|
@ -2418,6 +2418,73 @@
|
|||
</Files>
|
||||
</Project>
|
||||
|
||||
<!-- XMRengine -->
|
||||
<Project frameworkVersion="v4_6" name="Mono.Tasklets" path="OpenSim/Region/ScriptEngine/XMREngine" type="Library">
|
||||
<Configuration name="Debug">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
<Configuration name="Release">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
|
||||
<ReferencePath>../../../../bin/</ReferencePath>
|
||||
|
||||
<Files>
|
||||
<Match pattern="MonoTaskletsDummy.cs" recurse="true">
|
||||
<Exclude name="obj" pattern="obj"/>
|
||||
</Match>
|
||||
</Files>
|
||||
</Project>
|
||||
|
||||
<Project frameworkVersion="v4_6" name="OpenSim.Region.ScriptEngine.XMREngine" path="OpenSim/Region/ScriptEngine/XMREngine" type="Library">
|
||||
<Configuration name="Debug">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
<Configuration name="Release">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
|
||||
<ReferencePath>../../../../bin/</ReferencePath>
|
||||
<Reference name="log4net"/>
|
||||
<Reference name="Mono.Addins"/>
|
||||
<Reference name="Mono.Tasklets"/>
|
||||
<Reference name="Nini"/>
|
||||
<Reference name="OpenMetaverse"/>
|
||||
<Reference name="OpenMetaverse.StructuredData"/>
|
||||
<Reference name="OpenMetaverseTypes"/>
|
||||
<Reference name="OpenSim.Framework"/>
|
||||
<Reference name="OpenSim.Framework.Console"/>
|
||||
<Reference name="OpenSim.Framework.Monitoring"/>
|
||||
<Reference name="OpenSim.Region.ClientStack.LindenCaps"/>
|
||||
<Reference name="OpenSim.Region.CoreModules"/>
|
||||
<Reference name="OpenSim.Region.Framework"/>
|
||||
<Reference name="OpenSim.Region.ScriptEngine.Shared"/>
|
||||
<Reference name="OpenSim.Region.ScriptEngine.Shared.Api"/>
|
||||
<Reference name="OpenSim.Region.ScriptEngine.Shared.Api.Runtime"/>
|
||||
<Reference name="OpenSim.Services.Interfaces"/>
|
||||
<Reference name="System"/>
|
||||
<Reference name="System.Drawing"/>
|
||||
<Reference name="System.Xml"/>
|
||||
|
||||
<Files>
|
||||
<Match pattern="MMR*.cs" recurse="true">
|
||||
<Exclude name="obj" pattern="obj"/>
|
||||
</Match>
|
||||
<Match pattern="XMR*.cs" recurse="true">
|
||||
<Exclude name="obj" pattern="obj"/>
|
||||
</Match>
|
||||
</Files>
|
||||
</Project>
|
||||
|
||||
|
||||
<!-- Addons -->
|
||||
|
||||
<Project frameworkVersion="v4_6" name="OpenSim.Addons.OfflineIM" path="OpenSim/Addons/OfflineIM" type="Library">
|
||||
|
|
Loading…
Reference in New Issue