aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs28
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRDelegateCommon.cs106
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRIEventHandlers.cs77
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRInternalFuncDict.cs94
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptBinOpStr.cs1559
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs6262
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs2637
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompValu.cs1677
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompile.cs216
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptConsts.cs250
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptEventCode.cs95
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs664
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptMyILGen.cs81
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjCode.cs256
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjWriter.cs947
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs7719
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTokenize.cs1729
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTypeCast.cs819
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptVarDict.cs371
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRWebRequest.cs269
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MonoTaskletsDummy.cs55
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs534
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMREngXmrTestLs.cs491
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMREngine.cs1979
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMREvents.cs369
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRHeapTracker.cs172
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRInstAbstract.cs2031
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs644
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs436
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs878
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRInstMain.cs230
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRInstMisc.cs384
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRInstQueue.cs186
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs1051
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRInstSorpra.cs76
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRObjectTokens.cs5476
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRSDTypeClObj.cs259
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRScriptThread.cs262
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs557
-rw-r--r--prebuild.xml67
40 files changed, 41980 insertions, 13 deletions
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs
index e01d2e4..5eb3c5c 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs
@@ -284,22 +284,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
284 // Remove from: Timers 284 // Remove from: Timers
285 m_Timer[engine].UnSetTimerEvents(localID, itemID); 285 m_Timer[engine].UnSetTimerEvents(localID, itemID);
286 286
287 // Remove from: HttpRequest 287 if(engine.World != null)
288 IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface<IHttpRequestModule>(); 288 {
289 if (iHttpReq != null) 289 // Remove from: HttpRequest
290 iHttpReq.StopHttpRequest(localID, itemID); 290 IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface<IHttpRequestModule>();
291 if (iHttpReq != null)
292 iHttpReq.StopHttpRequest(localID, itemID);
291 293
292 IWorldComm comms = engine.World.RequestModuleInterface<IWorldComm>(); 294 IWorldComm comms = engine.World.RequestModuleInterface<IWorldComm>();
293 if (comms != null) 295 if (comms != null)
294 comms.DeleteListener(itemID); 296 comms.DeleteListener(itemID);
295 297
296 IXMLRPC xmlrpc = engine.World.RequestModuleInterface<IXMLRPC>(); 298 IXMLRPC xmlrpc = engine.World.RequestModuleInterface<IXMLRPC>();
297 if (xmlrpc != null) 299 if (xmlrpc != null)
298 { 300 {
299 xmlrpc.DeleteChannels(itemID); 301 xmlrpc.DeleteChannels(itemID);
300 xmlrpc.CancelSRDRequests(itemID); 302 xmlrpc.CancelSRDRequests(itemID);
303 }
301 } 304 }
302
303 // Remove Sensors 305 // Remove Sensors
304 m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID); 306 m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID);
305 } 307 }
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRDelegateCommon.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRDelegateCommon.cs
new file mode 100644
index 0000000..48b665b
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRDelegateCommon.cs
@@ -0,0 +1,106 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Reflection.Emit;
32
33namespace OpenSim.Region.ScriptEngine.XMREngine {
34
35 public class DelegateCommon {
36 private string sig; // rettype(arg1type,arg2type,...), eg, "void(list,string,integer)"
37 private Type type; // resultant delegate type
38
39 private static Dictionary<string, DelegateCommon> delegateCommons = new Dictionary<string, DelegateCommon> ();
40 private static Dictionary<Type, DelegateCommon> delegateCommonsBySysType = new Dictionary<Type, DelegateCommon> ();
41 private static ModuleBuilder delegateModuleBuilder = null;
42 public static Type[] constructorArgTypes = new Type[] { typeof (object), typeof (IntPtr) };
43
44 private DelegateCommon () { }
45
46 public static Type GetType (System.Type ret, System.Type[] args, string sig)
47 {
48 DelegateCommon dc;
49 lock (delegateCommons) {
50 if (!delegateCommons.TryGetValue (sig, out dc)) {
51 dc = new DelegateCommon ();
52 dc.sig = sig;
53 dc.type = CreateDelegateType (sig, ret, args);
54 delegateCommons.Add (sig, dc);
55 delegateCommonsBySysType.Add (dc.type, dc);
56 }
57 }
58 return dc.type;
59 }
60
61 public static Type TryGetType (string sig)
62 {
63 DelegateCommon dc;
64 lock (delegateCommons) {
65 if (!delegateCommons.TryGetValue (sig, out dc)) dc = null;
66 }
67 return (dc == null) ? null : dc.type;
68 }
69
70 public static string TryGetName (Type t)
71 {
72 DelegateCommon dc;
73 lock (delegateCommons) {
74 if (!delegateCommonsBySysType.TryGetValue (t, out dc)) dc = null;
75 }
76 return (dc == null) ? null : dc.sig;
77 }
78
79 // http://blog.bittercoder.com/PermaLink,guid,a770377a-b1ad-4590-9145-36381757a52b.aspx
80 private static Type CreateDelegateType (string name, Type retType, Type[] argTypes)
81 {
82 if (delegateModuleBuilder == null) {
83 AssemblyName assembly = new AssemblyName();
84 assembly.Name = "CustomDelegateAssembly";
85 AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run);
86 delegateModuleBuilder = assemblyBuilder.DefineDynamicModule("CustomDelegateModule");
87 }
88
89 TypeBuilder typeBuilder = delegateModuleBuilder.DefineType(name,
90 TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class |
91 TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof (MulticastDelegate));
92
93 ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(
94 MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
95 CallingConventions.Standard, constructorArgTypes);
96 constructorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
97
98 MethodBuilder methodBuilder = typeBuilder.DefineMethod("Invoke",
99 MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot |
100 MethodAttributes.Virtual, retType, argTypes);
101 methodBuilder.SetImplementationFlags(MethodImplAttributes.Managed | MethodImplAttributes.Runtime);
102
103 return typeBuilder.CreateType();
104 }
105 }
106}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRIEventHandlers.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRIEventHandlers.cs
new file mode 100644
index 0000000..440beb3
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRIEventHandlers.cs
@@ -0,0 +1,77 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
29using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
30using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
31using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
32using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
33using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
34using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
35
36namespace OpenSim.Region.ScriptEngine.XMREngine
37{
38 public interface IEventHandlers {
39 void at_rot_target (int tnum, LSL_Rotation targetrot, LSL_Rotation ourrot);
40 void at_target (int tnum, LSL_Vector targetpos, LSL_Vector ourpos);
41 void attach (string id);
42 void changed (int change);
43 void collision (int num_detected);
44 void collision_end (int num_detected);
45 void collision_start (int num_detected);
46 void control (string id, int held, int change);
47 void dataserver (string queryid, string data);
48 void email (string time, string address, string subj, string message, int num_left);
49 void http_request (string request_id, string method, string body);
50 void http_response (string request_id, int status, LSL_List metadata, string body);
51 void land_collision (LSL_Vector pos);
52 void land_collision_end (LSL_Vector pos);
53 void land_collision_start (LSL_Vector pos);
54 void link_message (int sender_num, int num, string str, string id);
55 void listen (int channel, string name, string id, string message);
56 void money (string id, int amount);
57 void moving_end ();
58 void moving_start ();
59 void no_sensor ();
60 void not_at_rot_target ();
61 void not_at_target ();
62 void object_rez (string id);
63 void on_rez (int start_param);
64 void remote_data (int event_type, string channel, string message_id, string sender, int idata, string sdata);
65 void run_time_permissions (int perm);
66 void sensor (int num_detected);
67 void state_entry ();
68 void state_exit ();
69 void timer ();
70 void touch (int num_detected);
71 void touch_start (int num_detected);
72 void touch_end (int num_detected);
73 void transaction_result(string id, int success, string data);
74 void path_update(int type, LSL_List data);
75 void region_cross(LSL_Vector newpos, LSL_Vector oldpos);
76 }
77}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRInternalFuncDict.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRInternalFuncDict.cs
new file mode 100644
index 0000000..c2c01b5
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRInternalFuncDict.cs
@@ -0,0 +1,94 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32
33namespace OpenSim.Region.ScriptEngine.XMREngine {
34
35 public class InternalFuncDict : VarDict {
36
37 /**
38 * @brief build dictionary of internal functions from an interface.
39 * @param iface = interface with function definitions
40 * @param inclSig = true: catalog by name with arg sig, eg, llSay(integer,string)
41 * false: catalog by simple name only, eg, state_entry
42 * @returns dictionary of function definition tokens
43 */
44 public InternalFuncDict (Type iface, bool inclSig)
45 : base (false)
46 {
47 /*
48 * Loop through list of all methods declared in the interface.
49 */
50 System.Reflection.MethodInfo[] ifaceMethods = iface.GetMethods ();
51 foreach (System.Reflection.MethodInfo ifaceMethod in ifaceMethods) {
52 string key = ifaceMethod.Name;
53
54 /*
55 * Only do ones that begin with lower-case letters...
56 * as any others can't be referenced by scripts
57 */
58 if ((key[0] < 'a') || (key[0] > 'z')) continue;
59
60 try {
61
62 /*
63 * Create a corresponding TokenDeclVar struct.
64 */
65 System.Reflection.ParameterInfo[] parameters = ifaceMethod.GetParameters ();
66 TokenArgDecl argDecl = new TokenArgDecl (null);
67 for (int i = 0; i < parameters.Length; i++) {
68 System.Reflection.ParameterInfo param = parameters[i];
69 TokenType type = TokenType.FromSysType (null, param.ParameterType);
70 TokenName name = new TokenName (null, param.Name);
71 argDecl.AddArg (type, name);
72 }
73 TokenDeclVar declFunc = new TokenDeclVar (null, null, null);
74 declFunc.name = new TokenName (null, key);
75 declFunc.retType = TokenType.FromSysType (null, ifaceMethod.ReturnType);
76 declFunc.argDecl = argDecl;
77
78 /*
79 * Add the TokenDeclVar struct to the dictionary.
80 */
81 this.AddEntry (declFunc);
82 } catch (Exception except) {
83
84 string msg = except.ToString ();
85 int i = msg.IndexOf ("\n");
86 if (i > 0) msg = msg.Substring (0, i);
87 Console.WriteLine ("InternalFuncDict*: {0}: {1}", key, msg);
88
89 ///??? IGNORE ANY THAT FAIL - LIKE UNRECOGNIZED TYPE ???///
90 }
91 }
92 }
93 }
94}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptBinOpStr.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptBinOpStr.cs
new file mode 100644
index 0000000..f8c9b22
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptBinOpStr.cs
@@ -0,0 +1,1559 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using OpenSim.Region.ScriptEngine.XMREngine;
30using System;
31using System.Collections.Generic;
32using System.IO;
33using System.Reflection;
34using System.Reflection.Emit;
35using System.Text;
36using System.Text.RegularExpressions;
37
38using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
39using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
40using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
41using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
42using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
43using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
44using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
45
46namespace OpenSim.Region.ScriptEngine.XMREngine {
47
48 /**
49 * @brief This class is used to catalog the code emit routines based on a key string
50 * The key string has the two types (eg, "integer", "rotation") and the operator (eg, "*", "!=")
51 */
52 public delegate void BinOpStrEmitBO (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result);
53 public class BinOpStr {
54 public static readonly Dictionary<string, BinOpStr> defined = DefineBinOps ();
55
56 public Type outtype; // type of result of computation
57 public BinOpStrEmitBO emitBO; // how to compute result
58 public bool rmwOK; // is the <operator>= form valid?
59
60 public BinOpStr (Type outtype, BinOpStrEmitBO emitBO)
61 {
62 this.outtype = outtype;
63 this.emitBO = emitBO;
64 this.rmwOK = false;
65 }
66
67 public BinOpStr (Type outtype, BinOpStrEmitBO emitBO, bool rmwOK)
68 {
69 this.outtype = outtype;
70 this.emitBO = emitBO;
71 this.rmwOK = rmwOK;
72 }
73
74 private static TokenTypeBool tokenTypeBool = new TokenTypeBool (null);
75 private static TokenTypeChar tokenTypeChar = new TokenTypeChar (null);
76 private static TokenTypeFloat tokenTypeFloat = new TokenTypeFloat (null);
77 private static TokenTypeInt tokenTypeInt = new TokenTypeInt (null);
78 private static TokenTypeList tokenTypeList = new TokenTypeList (null);
79 private static TokenTypeRot tokenTypeRot = new TokenTypeRot (null);
80 private static TokenTypeStr tokenTypeStr = new TokenTypeStr (null);
81 private static TokenTypeVec tokenTypeVec = new TokenTypeVec (null);
82
83 private static MethodInfo stringAddStringMethInfo = ScriptCodeGen.GetStaticMethod (typeof (string), "Concat", new Type[] { typeof (string), typeof (string) });
84 private static MethodInfo stringCmpStringMethInfo = ScriptCodeGen.GetStaticMethod (typeof (string), "Compare", new Type[] { typeof (string), typeof (string) });
85
86 private static MethodInfo infoMethListAddFloat = GetBinOpsMethod ("MethListAddFloat", new Type[] { typeof (LSL_List), typeof (double) });
87 private static MethodInfo infoMethListAddInt = GetBinOpsMethod ("MethListAddInt", new Type[] { typeof (LSL_List), typeof (int) });
88 private static MethodInfo infoMethListAddKey = GetBinOpsMethod ("MethListAddKey", new Type[] { typeof (LSL_List), typeof (string) });
89 private static MethodInfo infoMethListAddRot = GetBinOpsMethod ("MethListAddRot", new Type[] { typeof (LSL_List), typeof (LSL_Rotation) });
90 private static MethodInfo infoMethListAddStr = GetBinOpsMethod ("MethListAddStr", new Type[] { typeof (LSL_List), typeof (string) });
91 private static MethodInfo infoMethListAddVec = GetBinOpsMethod ("MethListAddVec", new Type[] { typeof (LSL_List), typeof (LSL_Vector) });
92 private static MethodInfo infoMethListAddList = GetBinOpsMethod ("MethListAddList", new Type[] { typeof (LSL_List), typeof (LSL_List) });
93 private static MethodInfo infoMethFloatAddList = GetBinOpsMethod ("MethFloatAddList", new Type[] { typeof (double), typeof (LSL_List) });
94 private static MethodInfo infoMethIntAddList = GetBinOpsMethod ("MethIntAddList", new Type[] { typeof (int), typeof (LSL_List) });
95 private static MethodInfo infoMethKeyAddList = GetBinOpsMethod ("MethKeyAddList", new Type[] { typeof (string), typeof (LSL_List) });
96 private static MethodInfo infoMethRotAddList = GetBinOpsMethod ("MethRotAddList", new Type[] { typeof (LSL_Rotation), typeof (LSL_List) });
97 private static MethodInfo infoMethStrAddList = GetBinOpsMethod ("MethStrAddList", new Type[] { typeof (string), typeof (LSL_List) });
98 private static MethodInfo infoMethVecAddList = GetBinOpsMethod ("MethVecAddList", new Type[] { typeof (LSL_Vector), typeof (LSL_List) });
99 private static MethodInfo infoMethListEqList = GetBinOpsMethod ("MethListEqList", new Type[] { typeof (LSL_List), typeof (LSL_List) });
100 private static MethodInfo infoMethListNeList = GetBinOpsMethod ("MethListNeList", new Type[] { typeof (LSL_List), typeof (LSL_List) });
101 private static MethodInfo infoMethRotEqRot = GetBinOpsMethod ("MethRotEqRot", new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
102 private static MethodInfo infoMethRotNeRot = GetBinOpsMethod ("MethRotNeRot", new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
103 private static MethodInfo infoMethRotAddRot = GetBinOpsMethod ("MethRotAddRot", new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
104 private static MethodInfo infoMethRotSubRot = GetBinOpsMethod ("MethRotSubRot", new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
105 private static MethodInfo infoMethRotMulRot = GetBinOpsMethod ("MethRotMulRot", new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
106 private static MethodInfo infoMethRotDivRot = GetBinOpsMethod ("MethRotDivRot", new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
107 private static MethodInfo infoMethVecEqVec = GetBinOpsMethod ("MethVecEqVec", new Type[] { typeof (LSL_Vector), typeof (LSL_Vector) });
108 private static MethodInfo infoMethVecNeVec = GetBinOpsMethod ("MethVecNeVec", new Type[] { typeof (LSL_Vector), typeof (LSL_Vector) });
109 private static MethodInfo infoMethVecAddVec = GetBinOpsMethod ("MethVecAddVec", new Type[] { typeof (LSL_Vector), typeof (LSL_Vector) });
110 private static MethodInfo infoMethVecSubVec = GetBinOpsMethod ("MethVecSubVec", new Type[] { typeof (LSL_Vector), typeof (LSL_Vector) });
111 private static MethodInfo infoMethVecMulVec = GetBinOpsMethod ("MethVecMulVec", new Type[] { typeof (LSL_Vector), typeof (LSL_Vector) });
112 private static MethodInfo infoMethVecModVec = GetBinOpsMethod ("MethVecModVec", new Type[] { typeof (LSL_Vector), typeof (LSL_Vector) });
113 private static MethodInfo infoMethVecMulFloat = GetBinOpsMethod ("MethVecMulFloat", new Type[] { typeof (LSL_Vector), typeof (double) });
114 private static MethodInfo infoMethFloatMulVec = GetBinOpsMethod ("MethFloatMulVec", new Type[] { typeof (double), typeof (LSL_Vector) });
115 private static MethodInfo infoMethVecDivFloat = GetBinOpsMethod ("MethVecDivFloat", new Type[] { typeof (LSL_Vector), typeof (double) });
116 private static MethodInfo infoMethVecMulInt = GetBinOpsMethod ("MethVecMulInt", new Type[] { typeof (LSL_Vector), typeof (int) });
117 private static MethodInfo infoMethIntMulVec = GetBinOpsMethod ("MethIntMulVec", new Type[] { typeof (int), typeof (LSL_Vector) });
118 private static MethodInfo infoMethVecDivInt = GetBinOpsMethod ("MethVecDivInt", new Type[] { typeof (LSL_Vector), typeof (int) });
119 private static MethodInfo infoMethVecMulRot = GetBinOpsMethod ("MethVecMulRot", new Type[] { typeof (LSL_Vector), typeof (LSL_Rotation) });
120 private static MethodInfo infoMethVecDivRot = GetBinOpsMethod ("MethVecDivRot", new Type[] { typeof (LSL_Vector), typeof (LSL_Rotation) });
121
122 private static MethodInfo GetBinOpsMethod (string name, Type[] types)
123 {
124 return ScriptCodeGen.GetStaticMethod (typeof (BinOpStr), name, types);
125 }
126
127 /**
128 * @brief Create a dictionary for processing binary operators.
129 * This tells us, for a given type, an operator and another type,
130 * is the operation permitted, and if so, what is the type of the result?
131 * The key is <lefttype><opcode><righttype>,
132 * where <lefttype> and <righttype> are strings returned by (TokenType...).ToString()
133 * and <opcode> is string returned by (TokenKw...).ToString()
134 * The value is a BinOpStr struct giving the resultant type and a method to generate the code.
135 */
136 private static Dictionary<string, BinOpStr> DefineBinOps ()
137 {
138 Dictionary<string, BinOpStr> bos = new Dictionary<string, BinOpStr> ();
139
140 string[] booltypes = new string[] { "bool", "char", "float", "integer", "key", "list", "string" };
141
142 /*
143 * Get the && and || all out of the way...
144 * Simply cast their left and right operands to boolean then process.
145 */
146 for (int i = 0; i < booltypes.Length; i ++) {
147 for (int j = 0; j < booltypes.Length; j ++) {
148 bos.Add (booltypes[i] + "&&" + booltypes[j],
149 new BinOpStr (typeof (bool), BinOpStrAndAnd));
150 bos.Add (booltypes[i] + "||" + booltypes[j],
151 new BinOpStr (typeof (bool), BinOpStrOrOr));
152 }
153 }
154
155 /*
156 * Pound through all the other combinations we support.
157 */
158
159 // boolean : somethingelse
160 DefineBinOpsBoolX (bos, "bool");
161 DefineBinOpsBoolX (bos, "char");
162 DefineBinOpsBoolX (bos, "float");
163 DefineBinOpsBoolX (bos, "integer");
164 DefineBinOpsBoolX (bos, "key");
165 DefineBinOpsBoolX (bos, "list");
166 DefineBinOpsBoolX (bos, "string");
167
168 // stuff with chars
169 DefineBinOpsChar (bos);
170
171 // somethingelse : boolean
172 DefineBinOpsXBool (bos, "char");
173 DefineBinOpsXBool (bos, "float");
174 DefineBinOpsXBool (bos, "integer");
175 DefineBinOpsXBool (bos, "key");
176 DefineBinOpsXBool (bos, "list");
177 DefineBinOpsXBool (bos, "string");
178
179 // float : somethingelse
180 DefineBinOpsFloatX (bos, "float");
181 DefineBinOpsFloatX (bos, "integer");
182
183 // integer : float
184 DefineBinOpsXFloat (bos, "integer");
185
186 // anything else with integers
187 DefineBinOpsInteger (bos);
188
189 // key : somethingelse
190 DefineBinOpsKeyX (bos, "key");
191 DefineBinOpsKeyX (bos, "string");
192
193 // string : key
194 DefineBinOpsXKey (bos, "string");
195
196 // things with lists
197 DefineBinOpsList (bos);
198
199 // things with rotations
200 DefineBinOpsRotation (bos);
201
202 // things with strings
203 DefineBinOpsString (bos);
204
205 // things with vectors
206 DefineBinOpsVector (bos);
207
208 // Contrary to some beliefs, scripts do things like string+integer and integer+string
209 bos.Add ("bool+string", new BinOpStr (typeof (string), BinOpStrStrAddStr));
210 bos.Add ("char+string", new BinOpStr (typeof (string), BinOpStrStrAddStr));
211 bos.Add ("float+string", new BinOpStr (typeof (string), BinOpStrStrAddStr));
212 bos.Add ("integer+string", new BinOpStr (typeof (string), BinOpStrStrAddStr));
213 bos.Add ("string+bool", new BinOpStr (typeof (string), BinOpStrStrAddStr, true));
214 bos.Add ("string+char", new BinOpStr (typeof (string), BinOpStrStrAddStr, true));
215 bos.Add ("string+float", new BinOpStr (typeof (string), BinOpStrStrAddStr, true));
216 bos.Add ("string+integer", new BinOpStr (typeof (string), BinOpStrStrAddStr, true));
217
218 // Now for our final slight-of-hand, we're going to scan through all those.
219 // And wherever we see an 'integer' in the key, we are going to make another
220 // entry with 'bool', as we want to accept a bool as having a value of 0 or 1.
221 // This lets us do things like 3.5 * (x > 0).
222
223 Dictionary<string, BinOpStr> bos2 = new Dictionary<string, BinOpStr> ();
224 foreach (KeyValuePair<string, BinOpStr> kvp in bos) {
225 string key = kvp.Key;
226 BinOpStr val = kvp.Value;
227 bos2.Add (key, val);
228 }
229 Regex wordReg = new Regex("\\w+");
230 Regex opReg = new Regex("\\W+");
231 foreach (KeyValuePair<string, BinOpStr> kvp in bos) {
232 string key = kvp.Key;
233 BinOpStr val = kvp.Value;
234 MatchCollection matches = wordReg.Matches(key);
235 if (matches.Count != 2)
236 continue;
237 Match opM = opReg.Match(key);
238 if (!opM.Success)
239 continue;
240 string left = matches[0].Value;
241 string right = matches[1].Value;
242 string op = opM.Value;
243 string key2;
244 if (left == "integer" && right == "integer")
245 {
246 key2 = "bool"+op+"bool";
247 if (!bos2.ContainsKey (key2)) bos2.Add (key2, val);
248 key2 = "bool"+op+"integer";
249 if (!bos2.ContainsKey (key2)) bos2.Add (key2, val);
250 key2 = "integer"+op+"bool";
251 if (!bos2.ContainsKey (key2)) bos2.Add (key2, val);
252 }
253 else
254 {
255 key2 = key.Replace("integer", "bool");
256 if (!bos2.ContainsKey (key2)) bos2.Add (key2, val);
257 }
258 }
259 return bos2;
260 }
261
262 private static void DefineBinOpsBoolX (Dictionary<string, BinOpStr> bos, string x)
263 {
264 bos.Add ("bool|" + x, new BinOpStr (typeof (int), BinOpStrBoolOrX));
265 bos.Add ("bool^" + x, new BinOpStr (typeof (int), BinOpStrBoolXorX));
266 bos.Add ("bool&" + x, new BinOpStr (typeof (int), BinOpStrBoolAndX));
267 bos.Add ("bool==" + x, new BinOpStr (typeof (bool), BinOpStrBoolEqX));
268 bos.Add ("bool!=" + x, new BinOpStr (typeof (bool), BinOpStrBoolNeX));
269 }
270
271 private static void DefineBinOpsXBool (Dictionary<string, BinOpStr> bos, string x)
272 {
273 bos.Add (x + "|bool", new BinOpStr (typeof (int), BinOpStrBoolOrX));
274 bos.Add (x + "^bool", new BinOpStr (typeof (int), BinOpStrBoolXorX));
275 bos.Add (x + "&bool", new BinOpStr (typeof (int), BinOpStrBoolAndX));
276 bos.Add (x + "==bool", new BinOpStr (typeof (bool), BinOpStrBoolEqX));
277 bos.Add (x + "!=bool", new BinOpStr (typeof (bool), BinOpStrBoolNeX));
278 }
279
280 private static void DefineBinOpsFloatX (Dictionary<string, BinOpStr> bos, string x)
281 {
282 bos.Add ("float==" + x, new BinOpStr (typeof (bool), BinOpStrFloatEqX));
283 bos.Add ("float!=" + x, new BinOpStr (typeof (bool), BinOpStrFloatNeX));
284 bos.Add ("float<" + x, new BinOpStr (typeof (bool), BinOpStrFloatLtX));
285 bos.Add ("float<=" + x, new BinOpStr (typeof (bool), BinOpStrFloatLeX));
286 bos.Add ("float>" + x, new BinOpStr (typeof (bool), BinOpStrFloatGtX));
287 bos.Add ("float>=" + x, new BinOpStr (typeof (bool), BinOpStrFloatGeX));
288 bos.Add ("float+" + x, new BinOpStr (typeof (double), BinOpStrFloatAddX, true));
289 bos.Add ("float-" + x, new BinOpStr (typeof (double), BinOpStrFloatSubX, true));
290 bos.Add ("float*" + x, new BinOpStr (typeof (double), BinOpStrFloatMulX, true));
291 bos.Add ("float/" + x, new BinOpStr (typeof (double), BinOpStrFloatDivX, true));
292 bos.Add ("float%" + x, new BinOpStr (typeof (double), BinOpStrFloatModX, true));
293 }
294
295 private static void DefineBinOpsXFloat (Dictionary<string, BinOpStr> bos, string x)
296 {
297 bos.Add (x + "==float", new BinOpStr (typeof (bool), BinOpStrXEqFloat));
298 bos.Add (x + "!=float", new BinOpStr (typeof (bool), BinOpStrXNeFloat));
299 bos.Add (x + "<float", new BinOpStr (typeof (bool), BinOpStrXLtFloat));
300 bos.Add (x + "<=float", new BinOpStr (typeof (bool), BinOpStrXLeFloat));
301 bos.Add (x + ">float", new BinOpStr (typeof (bool), BinOpStrXGtFloat));
302 bos.Add (x + ">=float", new BinOpStr (typeof (bool), BinOpStrXGeFloat));
303 bos.Add (x + "+float", new BinOpStr (typeof (double), BinOpStrXAddFloat, true));
304 bos.Add (x + "-float", new BinOpStr (typeof (double), BinOpStrXSubFloat, true));
305 bos.Add (x + "*float", new BinOpStr (typeof (double), BinOpStrXMulFloat, true));
306 bos.Add (x + "/float", new BinOpStr (typeof (double), BinOpStrXDivFloat, true));
307 bos.Add (x + "%float", new BinOpStr (typeof (double), BinOpStrXModFloat, true));
308 }
309
310 private static void DefineBinOpsChar (Dictionary<string, BinOpStr> bos)
311 {
312 bos.Add ("char==char", new BinOpStr (typeof (bool), BinOpStrCharEqChar));
313 bos.Add ("char!=char", new BinOpStr (typeof (bool), BinOpStrCharNeChar));
314 bos.Add ("char<char", new BinOpStr (typeof (bool), BinOpStrCharLtChar));
315 bos.Add ("char<=char", new BinOpStr (typeof (bool), BinOpStrCharLeChar));
316 bos.Add ("char>char", new BinOpStr (typeof (bool), BinOpStrCharGtChar));
317 bos.Add ("char>=char", new BinOpStr (typeof (bool), BinOpStrCharGeChar));
318 bos.Add ("char+integer", new BinOpStr (typeof (char), BinOpStrCharAddInt, true));
319 bos.Add ("char-integer", new BinOpStr (typeof (char), BinOpStrCharSubInt, true));
320 bos.Add ("char-char", new BinOpStr (typeof (int), BinOpStrCharSubChar));
321 }
322
323 private static void DefineBinOpsInteger (Dictionary<string, BinOpStr> bos)
324 {
325 bos.Add ("integer==integer", new BinOpStr (typeof (bool), BinOpStrIntEqInt));
326 bos.Add ("integer!=integer", new BinOpStr (typeof (bool), BinOpStrIntNeInt));
327 bos.Add ("integer<integer", new BinOpStr (typeof (bool), BinOpStrIntLtInt));
328 bos.Add ("integer<=integer", new BinOpStr (typeof (bool), BinOpStrIntLeInt));
329 bos.Add ("integer>integer", new BinOpStr (typeof (bool), BinOpStrIntGtInt));
330 bos.Add ("integer>=integer", new BinOpStr (typeof (bool), BinOpStrIntGeInt));
331 bos.Add ("integer|integer", new BinOpStr (typeof (int), BinOpStrIntOrInt, true));
332 bos.Add ("integer^integer", new BinOpStr (typeof (int), BinOpStrIntXorInt, true));
333 bos.Add ("integer&integer", new BinOpStr (typeof (int), BinOpStrIntAndInt, true));
334 bos.Add ("integer+integer", new BinOpStr (typeof (int), BinOpStrIntAddInt, true));
335 bos.Add ("integer-integer", new BinOpStr (typeof (int), BinOpStrIntSubInt, true));
336 bos.Add ("integer*integer", new BinOpStr (typeof (int), BinOpStrIntMulInt, true));
337 bos.Add ("integer/integer", new BinOpStr (typeof (int), BinOpStrIntDivInt, true));
338 bos.Add ("integer%integer", new BinOpStr (typeof (int), BinOpStrIntModInt, true));
339 bos.Add ("integer<<integer", new BinOpStr (typeof (int), BinOpStrIntShlInt, true));
340 bos.Add ("integer>>integer", new BinOpStr (typeof (int), BinOpStrIntShrInt, true));
341 }
342
343 private static void DefineBinOpsKeyX (Dictionary<string, BinOpStr> bos, string x)
344 {
345 bos.Add ("key==" + x, new BinOpStr (typeof (bool), BinOpStrKeyEqX));
346 bos.Add ("key!=" + x, new BinOpStr (typeof (bool), BinOpStrKeyNeX));
347 }
348
349 private static void DefineBinOpsXKey (Dictionary<string, BinOpStr> bos, string x)
350 {
351 bos.Add (x + "==key", new BinOpStr (typeof (bool), BinOpStrKeyEqX));
352 bos.Add (x + "!=key", new BinOpStr (typeof (bool), BinOpStrKeyNeX));
353 }
354
355 private static void DefineBinOpsList (Dictionary<string, BinOpStr> bos)
356 {
357 bos.Add ("list+float", new BinOpStr (typeof (LSL_List), BinOpStrListAddFloat, true));
358 bos.Add ("list+integer", new BinOpStr (typeof (LSL_List), BinOpStrListAddInt, true));
359 bos.Add ("list+key", new BinOpStr (typeof (LSL_List), BinOpStrListAddKey, true));
360 bos.Add ("list+list", new BinOpStr (typeof (LSL_List), BinOpStrListAddList, true));
361 bos.Add ("list+rotation", new BinOpStr (typeof (LSL_List), BinOpStrListAddRot, true));
362 bos.Add ("list+string", new BinOpStr (typeof (LSL_List), BinOpStrListAddStr, true));
363 bos.Add ("list+vector", new BinOpStr (typeof (LSL_List), BinOpStrListAddVec, true));
364
365 bos.Add ("float+list", new BinOpStr (typeof (LSL_List), BinOpStrFloatAddList));
366 bos.Add ("integer+list", new BinOpStr (typeof (LSL_List), BinOpStrIntAddList));
367 bos.Add ("key+list", new BinOpStr (typeof (LSL_List), BinOpStrKeyAddList));
368 bos.Add ("rotation+list", new BinOpStr (typeof (LSL_List), BinOpStrRotAddList));
369 bos.Add ("string+list", new BinOpStr (typeof (LSL_List), BinOpStrStrAddList));
370 bos.Add ("vector+list", new BinOpStr (typeof (LSL_List), BinOpStrVecAddList));
371
372 bos.Add ("list==list", new BinOpStr (typeof (bool), BinOpStrListEqList));
373 bos.Add ("list!=list", new BinOpStr (typeof (int), BinOpStrListNeList));
374 }
375
376 // all operations allowed by LSL_Rotation definition
377 private static void DefineBinOpsRotation (Dictionary<string, BinOpStr> bos)
378 {
379 bos.Add ("rotation==rotation", new BinOpStr (typeof (bool), BinOpStrRotEqRot));
380 bos.Add ("rotation!=rotation", new BinOpStr (typeof (bool), BinOpStrRotNeRot));
381 bos.Add ("rotation+rotation", new BinOpStr (typeof (LSL_Rotation), BinOpStrRotAddRot, true));
382 bos.Add ("rotation-rotation", new BinOpStr (typeof (LSL_Rotation), BinOpStrRotSubRot, true));
383 bos.Add ("rotation*rotation", new BinOpStr (typeof (LSL_Rotation), BinOpStrRotMulRot, true));
384 bos.Add ("rotation/rotation", new BinOpStr (typeof (LSL_Rotation), BinOpStrRotDivRot, true));
385 }
386
387 private static void DefineBinOpsString (Dictionary<string, BinOpStr> bos)
388 {
389 bos.Add ("string==string", new BinOpStr (typeof (bool), BinOpStrStrEqStr));
390 bos.Add ("string!=string", new BinOpStr (typeof (bool), BinOpStrStrNeStr));
391 bos.Add ("string<string", new BinOpStr (typeof (bool), BinOpStrStrLtStr));
392 bos.Add ("string<=string", new BinOpStr (typeof (bool), BinOpStrStrLeStr));
393 bos.Add ("string>string", new BinOpStr (typeof (bool), BinOpStrStrGtStr));
394 bos.Add ("string>=string", new BinOpStr (typeof (bool), BinOpStrStrGeStr));
395 bos.Add ("string+string", new BinOpStr (typeof (string), BinOpStrStrAddStr, true));
396 }
397
398 // all operations allowed by LSL_Vector definition
399 private static void DefineBinOpsVector (Dictionary<string, BinOpStr> bos)
400 {
401 bos.Add ("vector==vector", new BinOpStr (typeof (bool), BinOpStrVecEqVec));
402 bos.Add ("vector!=vector", new BinOpStr (typeof (bool), BinOpStrVecNeVec));
403 bos.Add ("vector+vector", new BinOpStr (typeof (LSL_Vector), BinOpStrVecAddVec, true));
404 bos.Add ("vector-vector", new BinOpStr (typeof (LSL_Vector), BinOpStrVecSubVec, true));
405 bos.Add ("vector*vector", new BinOpStr (typeof (double), BinOpStrVecMulVec));
406 bos.Add ("vector%vector", new BinOpStr (typeof (LSL_Vector), BinOpStrVecModVec, true));
407
408 bos.Add ("vector*float", new BinOpStr (typeof (LSL_Vector), BinOpStrVecMulFloat, true));
409 bos.Add ("float*vector", new BinOpStr (typeof (LSL_Vector), BinOpStrFloatMulVec));
410 bos.Add ("vector/float", new BinOpStr (typeof (LSL_Vector), BinOpStrVecDivFloat, true));
411
412 bos.Add ("vector*integer", new BinOpStr (typeof (LSL_Vector), BinOpStrVecMulInt, true));
413 bos.Add ("integer*vector", new BinOpStr (typeof (LSL_Vector), BinOpStrIntMulVec));
414 bos.Add ("vector/integer", new BinOpStr (typeof (LSL_Vector), BinOpStrVecDivInt, true));
415
416 bos.Add ("vector*rotation", new BinOpStr (typeof (LSL_Vector), BinOpStrVecMulRot, true));
417 bos.Add ("vector/rotation", new BinOpStr (typeof (LSL_Vector), BinOpStrVecDivRot, true));
418 }
419
420 /**
421 * @brief These methods actually emit the code to perform the arithmetic.
422 * @param scg = what script we are compiling
423 * @param left = left-hand operand location in memory (type as given by BinOpStr entry)
424 * @param right = right-hand operand location in memory (type as given by BinOpStr entry)
425 * @param result = result location in memory (type as given by BinOpStr entry)
426 */
427 private static void BinOpStrAndAnd (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
428 {
429 result.PopPre (scg, errorAt);
430 left.PushVal (scg, errorAt, tokenTypeBool);
431 right.PushVal (scg, errorAt, tokenTypeBool);
432 scg.ilGen.Emit (errorAt, OpCodes.And);
433 result.PopPost (scg, errorAt, tokenTypeBool);
434 }
435
436 private static void BinOpStrOrOr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
437 {
438 result.PopPre (scg, errorAt);
439 left.PushVal (scg, errorAt, tokenTypeBool);
440 right.PushVal (scg, errorAt, tokenTypeBool);
441 scg.ilGen.Emit (errorAt, OpCodes.Or);
442 result.PopPost (scg, errorAt, tokenTypeBool);
443 }
444
445 private static void BinOpStrBoolOrX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
446 {
447 result.PopPre (scg, errorAt);
448 left.PushVal (scg, errorAt, tokenTypeInt);
449 right.PushVal (scg, errorAt, tokenTypeInt);
450 scg.ilGen.Emit (errorAt, OpCodes.Or);
451 result.PopPost (scg, errorAt, tokenTypeInt);
452 }
453
454 private static void BinOpStrBoolXorX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
455 {
456 result.PopPre (scg, errorAt);
457 left.PushVal (scg, errorAt, tokenTypeInt);
458 right.PushVal (scg, errorAt, tokenTypeInt);
459 scg.ilGen.Emit (errorAt, OpCodes.Xor);
460 result.PopPost (scg, errorAt, tokenTypeInt);
461 }
462
463 private static void BinOpStrBoolAndX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
464 {
465 result.PopPre (scg, errorAt);
466 left.PushVal (scg, errorAt, tokenTypeInt);
467 right.PushVal (scg, errorAt, tokenTypeInt);
468 scg.ilGen.Emit (errorAt, OpCodes.And);
469 result.PopPost (scg, errorAt, tokenTypeInt);
470 }
471
472 private static void BinOpStrBoolEqX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
473 {
474 result.PopPre (scg, errorAt);
475 left.PushVal (scg, errorAt, tokenTypeBool);
476 right.PushVal (scg, errorAt, tokenTypeBool);
477 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
478 result.PopPost (scg, errorAt, tokenTypeBool);
479 }
480
481 private static void BinOpStrBoolNeX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
482 {
483 result.PopPre (scg, errorAt);
484 left.PushVal (scg, errorAt, tokenTypeBool);
485 right.PushVal (scg, errorAt, tokenTypeBool);
486 scg.ilGen.Emit (errorAt, OpCodes.Xor);
487 result.PopPost (scg, errorAt, tokenTypeBool);
488 }
489
490 private static void BinOpStrFloatEqX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
491 {
492 result.PopPre (scg, errorAt);
493 left.PushVal (scg, errorAt, tokenTypeFloat);
494 right.PushVal (scg, errorAt, tokenTypeFloat);
495 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
496 result.PopPost (scg, errorAt, tokenTypeBool);
497 }
498
499 private static void BinOpStrFloatNeX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
500 {
501 result.PopPre (scg, errorAt);
502 left.PushVal (scg, errorAt, tokenTypeFloat);
503 right.PushVal (scg, errorAt, tokenTypeFloat);
504 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
505 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
506 scg.ilGen.Emit (errorAt, OpCodes.Xor);
507 result.PopPost (scg, errorAt, tokenTypeBool);
508 }
509
510 private static void BinOpStrFloatLtX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
511 {
512 result.PopPre (scg, errorAt);
513 left.PushVal (scg, errorAt, tokenTypeFloat);
514 right.PushVal (scg, errorAt, tokenTypeFloat);
515 scg.ilGen.Emit (errorAt, OpCodes.Clt);
516 result.PopPost (scg, errorAt, tokenTypeBool);
517 }
518
519 private static void BinOpStrFloatLeX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
520 {
521 result.PopPre (scg, errorAt);
522 left.PushVal (scg, errorAt, tokenTypeFloat);
523 right.PushVal (scg, errorAt, tokenTypeFloat);
524 scg.ilGen.Emit (errorAt, OpCodes.Cgt);
525 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
526 scg.ilGen.Emit (errorAt, OpCodes.Xor);
527 result.PopPost (scg, errorAt, tokenTypeBool);
528 }
529
530 private static void BinOpStrFloatGtX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
531 {
532 result.PopPre (scg, errorAt);
533 left.PushVal (scg, errorAt, tokenTypeFloat);
534 right.PushVal (scg, errorAt, tokenTypeFloat);
535 scg.ilGen.Emit (errorAt, OpCodes.Cgt);
536 result.PopPost (scg, errorAt, tokenTypeBool);
537 }
538
539 private static void BinOpStrFloatGeX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
540 {
541 result.PopPre (scg, errorAt);
542 left.PushVal (scg, errorAt, tokenTypeFloat);
543 right.PushVal (scg, errorAt, tokenTypeFloat);
544 scg.ilGen.Emit (errorAt, OpCodes.Clt);
545 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
546 scg.ilGen.Emit (errorAt, OpCodes.Xor);
547 result.PopPost (scg, errorAt, tokenTypeBool);
548 }
549
550 private static void BinOpStrFloatAddX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
551 {
552 result.PopPre (scg, errorAt);
553 left.PushVal (scg, errorAt, tokenTypeFloat);
554 right.PushVal (scg, errorAt, tokenTypeFloat);
555 scg.ilGen.Emit (errorAt, OpCodes.Add);
556 result.PopPost (scg, errorAt, tokenTypeFloat);
557 }
558
559 private static void BinOpStrFloatSubX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
560 {
561 result.PopPre (scg, errorAt);
562 left.PushVal (scg, errorAt, tokenTypeFloat);
563 right.PushVal (scg, errorAt, tokenTypeFloat);
564 scg.ilGen.Emit (errorAt, OpCodes.Sub);
565 result.PopPost (scg, errorAt, tokenTypeFloat);
566 }
567
568 private static void BinOpStrFloatMulX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
569 {
570 result.PopPre (scg, errorAt);
571 left.PushVal (scg, errorAt, tokenTypeFloat);
572 right.PushVal (scg, errorAt, tokenTypeFloat);
573 scg.ilGen.Emit (errorAt, OpCodes.Mul);
574 result.PopPost (scg, errorAt, tokenTypeFloat);
575 }
576
577 private static void BinOpStrFloatDivX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
578 {
579 result.PopPre (scg, errorAt);
580 left.PushVal (scg, errorAt, tokenTypeFloat);
581 right.PushVal (scg, errorAt, tokenTypeFloat);
582 scg.ilGen.Emit (errorAt, OpCodes.Div);
583 result.PopPost (scg, errorAt, tokenTypeFloat);
584 }
585
586 private static void BinOpStrFloatModX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
587 {
588 result.PopPre (scg, errorAt);
589 left.PushVal (scg, errorAt, tokenTypeFloat);
590 right.PushVal (scg, errorAt, tokenTypeFloat);
591 scg.ilGen.Emit (errorAt, OpCodes.Rem);
592 result.PopPost (scg, errorAt, tokenTypeFloat);
593 }
594
595 private static void BinOpStrXEqFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
596 {
597 result.PopPre (scg, errorAt);
598 left.PushVal (scg, errorAt, tokenTypeFloat);
599 right.PushVal (scg, errorAt, tokenTypeFloat);
600 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
601 result.PopPost (scg, errorAt, tokenTypeBool);
602 }
603
604 private static void BinOpStrXNeFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
605 {
606 result.PopPre (scg, errorAt);
607 left.PushVal (scg, errorAt, tokenTypeFloat);
608 right.PushVal (scg, errorAt, tokenTypeFloat);
609 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
610 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
611 scg.ilGen.Emit (errorAt, OpCodes.Xor);
612 result.PopPost (scg, errorAt, tokenTypeBool);
613 }
614
615 private static void BinOpStrXLtFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
616 {
617 result.PopPre (scg, errorAt);
618 left.PushVal (scg, errorAt, tokenTypeFloat);
619 right.PushVal (scg, errorAt, tokenTypeFloat);
620 scg.ilGen.Emit (errorAt, OpCodes.Clt);
621 result.PopPost (scg, errorAt, tokenTypeBool);
622 }
623
624 private static void BinOpStrXLeFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
625 {
626 result.PopPre (scg, errorAt);
627 left.PushVal (scg, errorAt, tokenTypeFloat);
628 right.PushVal (scg, errorAt, tokenTypeFloat);
629 scg.ilGen.Emit (errorAt, OpCodes.Cgt);
630 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
631 scg.ilGen.Emit (errorAt, OpCodes.Xor);
632 result.PopPost (scg, errorAt, tokenTypeBool);
633 }
634
635 private static void BinOpStrXGtFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
636 {
637 result.PopPre (scg, errorAt);
638 left.PushVal (scg, errorAt, tokenTypeFloat);
639 right.PushVal (scg, errorAt, tokenTypeFloat);
640 scg.ilGen.Emit (errorAt, OpCodes.Cgt);
641 result.PopPost (scg, errorAt, tokenTypeBool);
642 }
643
644 private static void BinOpStrXGeFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
645 {
646 result.PopPre (scg, errorAt);
647 left.PushVal (scg, errorAt, tokenTypeFloat);
648 right.PushVal (scg, errorAt, tokenTypeFloat);
649 scg.ilGen.Emit (errorAt, OpCodes.Clt);
650 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
651 scg.ilGen.Emit (errorAt, OpCodes.Xor);
652 result.PopPost (scg, errorAt, tokenTypeBool);
653 }
654
655 private static void BinOpStrXAddFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
656 {
657 result.PopPre (scg, errorAt);
658 left.PushVal (scg, errorAt, tokenTypeFloat);
659 right.PushVal (scg, errorAt, tokenTypeFloat);
660 scg.ilGen.Emit (errorAt, OpCodes.Add);
661 result.PopPost (scg, errorAt, tokenTypeFloat);
662 }
663
664 private static void BinOpStrXSubFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
665 {
666 result.PopPre (scg, errorAt);
667 left.PushVal (scg, errorAt, tokenTypeFloat);
668 right.PushVal (scg, errorAt, tokenTypeFloat);
669 scg.ilGen.Emit (errorAt, OpCodes.Sub);
670 result.PopPost (scg, errorAt, tokenTypeFloat);
671 }
672
673 private static void BinOpStrXMulFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
674 {
675 result.PopPre (scg, errorAt);
676 left.PushVal (scg, errorAt, tokenTypeFloat);
677 right.PushVal (scg, errorAt, tokenTypeFloat);
678 scg.ilGen.Emit (errorAt, OpCodes.Mul);
679 result.PopPost (scg, errorAt, tokenTypeFloat);
680 }
681
682 private static void BinOpStrXDivFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
683 {
684 result.PopPre (scg, errorAt);
685 left.PushVal (scg, errorAt, tokenTypeFloat);
686 right.PushVal (scg, errorAt, tokenTypeFloat);
687 scg.ilGen.Emit (errorAt, OpCodes.Div);
688 result.PopPost (scg, errorAt, tokenTypeFloat);
689 }
690
691 private static void BinOpStrXModFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
692 {
693 result.PopPre (scg, errorAt);
694 left.PushVal (scg, errorAt, tokenTypeFloat);
695 right.PushVal (scg, errorAt, tokenTypeFloat);
696 scg.ilGen.Emit (errorAt, OpCodes.Rem);
697 result.PopPost (scg, errorAt, tokenTypeFloat);
698 }
699
700 private static void BinOpStrCharEqChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
701 {
702 result.PopPre (scg, errorAt);
703 left.PushVal (scg, errorAt, tokenTypeChar);
704 right.PushVal (scg, errorAt, tokenTypeChar);
705 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
706 result.PopPost (scg, errorAt, tokenTypeBool);
707 }
708
709 private static void BinOpStrCharNeChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
710 {
711 result.PopPre (scg, errorAt);
712 left.PushVal (scg, errorAt, tokenTypeChar);
713 right.PushVal (scg, errorAt, tokenTypeChar);
714 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
715 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
716 scg.ilGen.Emit (errorAt, OpCodes.Xor);
717 result.PopPost (scg, errorAt, tokenTypeBool);
718 }
719
720 private static void BinOpStrCharLtChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
721 {
722 result.PopPre (scg, errorAt);
723 left.PushVal (scg, errorAt, tokenTypeChar);
724 right.PushVal (scg, errorAt, tokenTypeChar);
725 scg.ilGen.Emit (errorAt, OpCodes.Clt);
726 result.PopPost (scg, errorAt, tokenTypeBool);
727 }
728
729 private static void BinOpStrCharLeChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
730 {
731 result.PopPre (scg, errorAt);
732 left.PushVal (scg, errorAt, tokenTypeChar);
733 right.PushVal (scg, errorAt, tokenTypeChar);
734 scg.ilGen.Emit (errorAt, OpCodes.Cgt);
735 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
736 scg.ilGen.Emit (errorAt, OpCodes.Xor);
737 result.PopPost (scg, errorAt, tokenTypeBool);
738 }
739
740 private static void BinOpStrCharGtChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
741 {
742 result.PopPre (scg, errorAt);
743 left.PushVal (scg, errorAt, tokenTypeChar);
744 right.PushVal (scg, errorAt, tokenTypeChar);
745 scg.ilGen.Emit (errorAt, OpCodes.Cgt);
746 result.PopPost (scg, errorAt, tokenTypeBool);
747 }
748
749 private static void BinOpStrCharGeChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
750 {
751 result.PopPre (scg, errorAt);
752 left.PushVal (scg, errorAt, tokenTypeChar);
753 right.PushVal (scg, errorAt, tokenTypeChar);
754 scg.ilGen.Emit (errorAt, OpCodes.Clt);
755 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
756 scg.ilGen.Emit (errorAt, OpCodes.Xor);
757 result.PopPost (scg, errorAt, tokenTypeBool);
758 }
759
760 private static void BinOpStrCharAddInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
761 {
762 result.PopPre (scg, errorAt);
763 left.PushVal (scg, errorAt, tokenTypeChar);
764 right.PushVal (scg, errorAt, tokenTypeInt);
765 scg.ilGen.Emit (errorAt, OpCodes.Add);
766 result.PopPost (scg, errorAt, tokenTypeChar);
767 }
768
769 private static void BinOpStrCharSubInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
770 {
771 result.PopPre (scg, errorAt);
772 left.PushVal (scg, errorAt, tokenTypeChar);
773 right.PushVal (scg, errorAt, tokenTypeInt);
774 scg.ilGen.Emit (errorAt, OpCodes.Sub);
775 result.PopPost (scg, errorAt, tokenTypeChar);
776 }
777
778 private static void BinOpStrCharSubChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
779 {
780 result.PopPre (scg, errorAt);
781 left.PushVal (scg, errorAt, tokenTypeChar);
782 right.PushVal (scg, errorAt, tokenTypeChar);
783 scg.ilGen.Emit (errorAt, OpCodes.Sub);
784 result.PopPost (scg, errorAt, tokenTypeInt);
785 }
786
787 private static void BinOpStrIntEqInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
788 {
789 result.PopPre (scg, errorAt);
790 left.PushVal (scg, errorAt, tokenTypeInt);
791 right.PushVal (scg, errorAt, tokenTypeInt);
792 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
793 result.PopPost (scg, errorAt, tokenTypeBool);
794 }
795
796 private static void BinOpStrIntNeInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
797 {
798 result.PopPre (scg, errorAt);
799 left.PushVal (scg, errorAt, tokenTypeInt);
800 right.PushVal (scg, errorAt, tokenTypeInt);
801 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
802 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
803 scg.ilGen.Emit (errorAt, OpCodes.Xor);
804 result.PopPost (scg, errorAt, tokenTypeBool);
805 }
806
807 private static void BinOpStrIntLtInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
808 {
809 result.PopPre (scg, errorAt);
810 left.PushVal (scg, errorAt, tokenTypeInt);
811 right.PushVal (scg, errorAt, tokenTypeInt);
812 scg.ilGen.Emit (errorAt, OpCodes.Clt);
813 result.PopPost (scg, errorAt, tokenTypeBool);
814 }
815
816 private static void BinOpStrIntLeInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
817 {
818 result.PopPre (scg, errorAt);
819 left.PushVal (scg, errorAt, tokenTypeInt);
820 right.PushVal (scg, errorAt, tokenTypeInt);
821 scg.ilGen.Emit (errorAt, OpCodes.Cgt);
822 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
823 scg.ilGen.Emit (errorAt, OpCodes.Xor);
824 result.PopPost (scg, errorAt, tokenTypeBool);
825 }
826
827 private static void BinOpStrIntGtInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
828 {
829 result.PopPre (scg, errorAt);
830 left.PushVal (scg, errorAt, tokenTypeInt);
831 right.PushVal (scg, errorAt, tokenTypeInt);
832 scg.ilGen.Emit (errorAt, OpCodes.Cgt);
833 result.PopPost (scg, errorAt, tokenTypeBool);
834 }
835
836 private static void BinOpStrIntGeInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
837 {
838 result.PopPre (scg, errorAt);
839 left.PushVal (scg, errorAt, tokenTypeInt);
840 right.PushVal (scg, errorAt, tokenTypeInt);
841 scg.ilGen.Emit (errorAt, OpCodes.Clt);
842 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
843 scg.ilGen.Emit (errorAt, OpCodes.Xor);
844 result.PopPost (scg, errorAt, tokenTypeBool);
845 }
846
847 private static void BinOpStrIntOrInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
848 {
849 result.PopPre (scg, errorAt);
850 left.PushVal (scg, errorAt, tokenTypeInt);
851 right.PushVal (scg, errorAt, tokenTypeInt);
852 scg.ilGen.Emit (errorAt, OpCodes.Or);
853 result.PopPost (scg, errorAt, tokenTypeInt);
854 }
855
856 private static void BinOpStrIntXorInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
857 {
858 result.PopPre (scg, errorAt);
859 left.PushVal (scg, errorAt, tokenTypeInt);
860 right.PushVal (scg, errorAt, tokenTypeInt);
861 scg.ilGen.Emit (errorAt, OpCodes.Xor);
862 result.PopPost (scg, errorAt, tokenTypeInt);
863 }
864
865 private static void BinOpStrIntAndInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
866 {
867 result.PopPre (scg, errorAt);
868 left.PushVal (scg, errorAt, tokenTypeInt);
869 right.PushVal (scg, errorAt, tokenTypeInt);
870 scg.ilGen.Emit (errorAt, OpCodes.And);
871 result.PopPost (scg, errorAt, tokenTypeInt);
872 }
873
874 private static void BinOpStrIntAddInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
875 {
876 result.PopPre (scg, errorAt);
877 left.PushVal (scg, errorAt, tokenTypeInt);
878 right.PushVal (scg, errorAt, tokenTypeInt);
879 scg.ilGen.Emit (errorAt, OpCodes.Add);
880 result.PopPost (scg, errorAt, tokenTypeInt);
881 }
882
883 private static void BinOpStrIntSubInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
884 {
885 result.PopPre (scg, errorAt);
886 left.PushVal (scg, errorAt, tokenTypeInt);
887 right.PushVal (scg, errorAt, tokenTypeInt);
888 scg.ilGen.Emit (errorAt, OpCodes.Sub);
889 result.PopPost (scg, errorAt, tokenTypeInt);
890 }
891
892 private static void BinOpStrIntMulInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
893 {
894 result.PopPre (scg, errorAt);
895 left.PushVal (scg, errorAt, tokenTypeInt);
896 right.PushVal (scg, errorAt, tokenTypeInt);
897 scg.ilGen.Emit (errorAt, OpCodes.Mul);
898 result.PopPost (scg, errorAt, tokenTypeInt);
899 }
900
901 private static void BinOpStrIntDivInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
902 {
903 // note that we must allow 0x800000/-1 -> 0x80000000 for lslangtest1.lsl
904 // so sign-extend the operands to 64-bit then divide and truncate result
905 result.PopPre (scg, errorAt);
906 left.PushVal (scg, errorAt, tokenTypeInt);
907 scg.ilGen.Emit (errorAt, OpCodes.Conv_I8);
908 right.PushVal (scg, errorAt, tokenTypeInt);
909 scg.ilGen.Emit (errorAt, OpCodes.Conv_I8);
910 scg.ilGen.Emit (errorAt, OpCodes.Div);
911 scg.ilGen.Emit (errorAt, OpCodes.Conv_I4);
912 result.PopPost (scg, errorAt, tokenTypeInt);
913 }
914
915 private static void BinOpStrIntModInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
916 {
917 // note that we must allow 0x800000%-1 -> 0 for lslangtest1.lsl
918 // so sign-extend the operands to 64-bit then mod and truncate result
919 result.PopPre (scg, errorAt);
920 left.PushVal (scg, errorAt, tokenTypeInt);
921 scg.ilGen.Emit (errorAt, OpCodes.Conv_I8);
922 right.PushVal (scg, errorAt, tokenTypeInt);
923 scg.ilGen.Emit (errorAt, OpCodes.Conv_I8);
924 scg.ilGen.Emit (errorAt, OpCodes.Rem);
925 scg.ilGen.Emit (errorAt, OpCodes.Conv_I4);
926 result.PopPost (scg, errorAt, tokenTypeInt);
927 }
928
929 private static void BinOpStrIntShlInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
930 {
931 result.PopPre (scg, errorAt);
932 left.PushVal (scg, errorAt, tokenTypeInt);
933 right.PushVal (scg, errorAt, tokenTypeInt);
934 scg.ilGen.Emit (errorAt, OpCodes.Shl);
935 result.PopPost (scg, errorAt, tokenTypeInt);
936 }
937
938 private static void BinOpStrIntShrInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
939 {
940 result.PopPre (scg, errorAt);
941 left.PushVal (scg, errorAt, tokenTypeInt);
942 right.PushVal (scg, errorAt, tokenTypeInt);
943 scg.ilGen.Emit (errorAt, OpCodes.Shr);
944 result.PopPost (scg, errorAt, tokenTypeInt);
945 }
946
947 private static void BinOpStrKeyEqX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
948 {
949 result.PopPre (scg, errorAt);
950 left.PushVal (scg, errorAt, tokenTypeStr);
951 right.PushVal (scg, errorAt, tokenTypeStr);
952 scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
953 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
954 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
955 result.PopPost (scg, errorAt, tokenTypeBool);
956 }
957
958 private static void BinOpStrKeyNeX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
959 {
960 result.PopPre (scg, errorAt);
961 left.PushVal (scg, errorAt, tokenTypeStr);
962 right.PushVal (scg, errorAt, tokenTypeStr);
963 scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
964 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
965 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
966 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
967 scg.ilGen.Emit (errorAt, OpCodes.Xor);
968 result.PopPost (scg, errorAt, tokenTypeBool);
969 }
970
971 private static void BinOpStrListAddFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
972 {
973 result.PopPre (scg, errorAt);
974 left.PushVal (scg, errorAt, tokenTypeList);
975 right.PushVal (scg, errorAt, tokenTypeFloat);
976 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddFloat);
977 result.PopPost (scg, errorAt, tokenTypeList);
978 }
979
980 private static void BinOpStrListAddInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
981 {
982 result.PopPre (scg, errorAt);
983 left.PushVal (scg, errorAt, tokenTypeList);
984 right.PushVal (scg, errorAt, tokenTypeInt);
985 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddInt);
986 result.PopPost (scg, errorAt, tokenTypeList);
987 }
988
989 private static void BinOpStrListAddKey (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
990 {
991 result.PopPre (scg, errorAt);
992 left.PushVal (scg, errorAt, tokenTypeList);
993 right.PushVal (scg, errorAt, tokenTypeStr);
994 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddKey);
995 result.PopPost (scg, errorAt, tokenTypeList);
996 }
997
998 private static void BinOpStrListAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
999 {
1000 result.PopPre (scg, errorAt);
1001 left.PushVal (scg, errorAt, tokenTypeList);
1002 right.PushVal (scg, errorAt, tokenTypeList);
1003 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddList);
1004 result.PopPost (scg, errorAt, tokenTypeList);
1005 }
1006
1007 private static void BinOpStrListAddRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1008 {
1009 result.PopPre (scg, errorAt);
1010 left.PushVal (scg, errorAt, tokenTypeList);
1011 right.PushVal (scg, errorAt, tokenTypeRot);
1012 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddRot);
1013 result.PopPost (scg, errorAt, tokenTypeList);
1014 }
1015
1016 private static void BinOpStrListAddStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1017 {
1018 result.PopPre (scg, errorAt);
1019 left.PushVal (scg, errorAt, tokenTypeList);
1020 right.PushVal (scg, errorAt, tokenTypeStr);
1021 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddStr);
1022 result.PopPost (scg, errorAt, tokenTypeList);
1023 }
1024
1025 private static void BinOpStrListAddVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1026 {
1027 result.PopPre (scg, errorAt);
1028 left.PushVal (scg, errorAt, tokenTypeList);
1029 right.PushVal (scg, errorAt, tokenTypeVec);
1030 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddVec);
1031 result.PopPost (scg, errorAt, tokenTypeList);
1032 }
1033
1034 private static void BinOpStrFloatAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1035 {
1036 result.PopPre (scg, errorAt);
1037 left.PushVal (scg, errorAt, tokenTypeFloat);
1038 right.PushVal (scg, errorAt, tokenTypeList);
1039 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethFloatAddList);
1040 result.PopPost (scg, errorAt, tokenTypeList);
1041 }
1042
1043 private static void BinOpStrIntAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1044 {
1045 result.PopPre (scg, errorAt);
1046 left.PushVal (scg, errorAt, tokenTypeInt);
1047 right.PushVal (scg, errorAt, tokenTypeList);
1048 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethIntAddList);
1049 result.PopPost (scg, errorAt, tokenTypeList);
1050 }
1051
1052 private static void BinOpStrKeyAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1053 {
1054 result.PopPre (scg, errorAt);
1055 left.PushVal (scg, errorAt, tokenTypeStr);
1056 right.PushVal (scg, errorAt, tokenTypeList);
1057 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethKeyAddList);
1058 result.PopPost (scg, errorAt, tokenTypeList);
1059 }
1060
1061 private static void BinOpStrRotAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1062 {
1063 result.PopPre (scg, errorAt);
1064 left.PushVal (scg, errorAt, tokenTypeRot);
1065 right.PushVal (scg, errorAt, tokenTypeList);
1066 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotAddList);
1067 result.PopPost (scg, errorAt, tokenTypeList);
1068 }
1069
1070 private static void BinOpStrStrAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1071 {
1072 result.PopPre (scg, errorAt);
1073 left.PushVal (scg, errorAt, tokenTypeStr);
1074 right.PushVal (scg, errorAt, tokenTypeList);
1075 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethStrAddList);
1076 result.PopPost (scg, errorAt, tokenTypeList);
1077 }
1078
1079 private static void BinOpStrVecAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1080 {
1081 result.PopPre (scg, errorAt);
1082 left.PushVal (scg, errorAt, tokenTypeVec);
1083 right.PushVal (scg, errorAt, tokenTypeList);
1084 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecAddList);
1085 result.PopPost (scg, errorAt, tokenTypeList);
1086 }
1087
1088 private static void BinOpStrListEqList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1089 {
1090 result.PopPre (scg, errorAt);
1091 left.PushVal (scg, errorAt, tokenTypeList);
1092 right.PushVal (scg, errorAt, tokenTypeList);
1093 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListEqList);
1094 result.PopPost (scg, errorAt, tokenTypeBool);
1095 }
1096
1097 private static void BinOpStrListNeList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1098 {
1099 result.PopPre (scg, errorAt);
1100 left.PushVal (scg, errorAt, tokenTypeList);
1101 right.PushVal (scg, errorAt, tokenTypeList);
1102 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListNeList);
1103 result.PopPost (scg, errorAt, tokenTypeBool);
1104 }
1105
1106 private static void BinOpStrRotEqRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1107 {
1108 result.PopPre (scg, errorAt);
1109 left.PushVal (scg, errorAt, tokenTypeRot);
1110 right.PushVal (scg, errorAt, tokenTypeRot);
1111 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotEqRot);
1112 result.PopPost (scg, errorAt, tokenTypeBool);
1113 }
1114
1115 private static void BinOpStrRotNeRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1116 {
1117 result.PopPre (scg, errorAt);
1118 left.PushVal (scg, errorAt, tokenTypeRot);
1119 right.PushVal (scg, errorAt, tokenTypeRot);
1120 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotNeRot);
1121 result.PopPost (scg, errorAt, tokenTypeBool);
1122 }
1123
1124 private static void BinOpStrRotAddRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1125 {
1126 result.PopPre (scg, errorAt);
1127 left.PushVal (scg, errorAt, tokenTypeRot);
1128 right.PushVal (scg, errorAt, tokenTypeRot);
1129 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotAddRot);
1130 result.PopPost (scg, errorAt, tokenTypeRot);
1131 }
1132
1133 private static void BinOpStrRotSubRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1134 {
1135 result.PopPre (scg, errorAt);
1136 left.PushVal (scg, errorAt, tokenTypeRot);
1137 right.PushVal (scg, errorAt, tokenTypeRot);
1138 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotSubRot);
1139 result.PopPost (scg, errorAt, tokenTypeRot);
1140 }
1141
1142 private static void BinOpStrRotMulRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1143 {
1144 result.PopPre (scg, errorAt);
1145 left.PushVal (scg, errorAt, tokenTypeRot);
1146 right.PushVal (scg, errorAt, tokenTypeRot);
1147 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotMulRot);
1148 result.PopPost (scg, errorAt, tokenTypeRot);
1149 }
1150
1151 private static void BinOpStrRotDivRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1152 {
1153 result.PopPre (scg, errorAt);
1154 left.PushVal (scg, errorAt, tokenTypeRot);
1155 right.PushVal (scg, errorAt, tokenTypeRot);
1156 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotDivRot);
1157 result.PopPost (scg, errorAt, tokenTypeRot);
1158 }
1159
1160 private static void BinOpStrStrEqStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1161 {
1162 result.PopPre (scg, errorAt);
1163 left.PushVal (scg, errorAt, tokenTypeStr);
1164 right.PushVal (scg, errorAt, tokenTypeStr);
1165 scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
1166 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
1167 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
1168 result.PopPost (scg, errorAt, tokenTypeBool);
1169 }
1170
1171 private static void BinOpStrStrNeStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1172 {
1173 result.PopPre (scg, errorAt);
1174 left.PushVal (scg, errorAt, tokenTypeStr);
1175 right.PushVal (scg, errorAt, tokenTypeStr);
1176 scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
1177 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
1178 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
1179 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
1180 scg.ilGen.Emit (errorAt, OpCodes.Xor);
1181 result.PopPost (scg, errorAt, tokenTypeBool);
1182 }
1183
1184 private static void BinOpStrStrLtStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1185 {
1186 result.PopPre (scg, errorAt);
1187 left.PushVal (scg, errorAt, tokenTypeStr);
1188 right.PushVal (scg, errorAt, tokenTypeStr);
1189 scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
1190 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
1191 scg.ilGen.Emit (errorAt, OpCodes.Clt);
1192 result.PopPost (scg, errorAt, tokenTypeBool);
1193 }
1194
1195 private static void BinOpStrStrLeStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1196 {
1197 result.PopPre (scg, errorAt);
1198 left.PushVal (scg, errorAt, tokenTypeStr);
1199 right.PushVal (scg, errorAt, tokenTypeStr);
1200 scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
1201 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
1202 scg.ilGen.Emit (errorAt, OpCodes.Clt);
1203 result.PopPost (scg, errorAt, tokenTypeBool);
1204 }
1205
1206 private static void BinOpStrStrGtStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1207 {
1208 result.PopPre (scg, errorAt);
1209 left.PushVal (scg, errorAt, tokenTypeStr);
1210 right.PushVal (scg, errorAt, tokenTypeStr);
1211 scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
1212 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
1213 scg.ilGen.Emit (errorAt, OpCodes.Cgt);
1214 result.PopPost (scg, errorAt, tokenTypeBool);
1215 }
1216
1217 private static void BinOpStrStrGeStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1218 {
1219 result.PopPre (scg, errorAt);
1220 left.PushVal (scg, errorAt, tokenTypeStr);
1221 right.PushVal (scg, errorAt, tokenTypeStr);
1222 scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
1223 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_M1);
1224 scg.ilGen.Emit (errorAt, OpCodes.Cgt);
1225 result.PopPost (scg, errorAt, tokenTypeBool);
1226 }
1227
1228 // Called by many type combinations so both operands need to be cast to strings
1229 private static void BinOpStrStrAddStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1230 {
1231 result.PopPre (scg, errorAt);
1232 left.PushVal (scg, errorAt, tokenTypeStr);
1233 right.PushVal (scg, errorAt, tokenTypeStr);
1234 scg.ilGen.Emit (errorAt, OpCodes.Call, stringAddStringMethInfo);
1235 result.PopPost (scg, errorAt, tokenTypeStr);
1236 }
1237
1238 private static void BinOpStrVecEqVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1239 {
1240 result.PopPre (scg, errorAt);
1241 left.PushVal (scg, errorAt, tokenTypeVec);
1242 right.PushVal (scg, errorAt, tokenTypeVec);
1243 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecEqVec);
1244 result.PopPost (scg, errorAt, tokenTypeBool);
1245 }
1246
1247 private static void BinOpStrVecNeVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1248 {
1249 result.PopPre (scg, errorAt);
1250 left.PushVal (scg, errorAt, tokenTypeVec);
1251 right.PushVal (scg, errorAt, tokenTypeVec);
1252 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecNeVec);
1253 result.PopPost (scg, errorAt, tokenTypeBool);
1254 }
1255
1256 private static void BinOpStrVecAddVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1257 {
1258 result.PopPre (scg, errorAt);
1259 left.PushVal (scg, errorAt, tokenTypeVec);
1260 right.PushVal (scg, errorAt, tokenTypeVec);
1261 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecAddVec);
1262 result.PopPost (scg, errorAt, tokenTypeVec);
1263 }
1264
1265 private static void BinOpStrVecSubVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1266 {
1267 result.PopPre (scg, errorAt);
1268 left.PushVal (scg, errorAt, tokenTypeVec);
1269 right.PushVal (scg, errorAt, tokenTypeVec);
1270 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecSubVec);
1271 result.PopPost (scg, errorAt, tokenTypeVec);
1272 }
1273
1274 private static void BinOpStrVecMulVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1275 {
1276 result.PopPre (scg, errorAt);
1277 left.PushVal (scg, errorAt, tokenTypeVec);
1278 right.PushVal (scg, errorAt, tokenTypeVec);
1279 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecMulVec);
1280 result.PopPost (scg, errorAt, tokenTypeFloat);
1281 }
1282
1283 private static void BinOpStrVecModVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1284 {
1285 result.PopPre (scg, errorAt);
1286 left.PushVal (scg, errorAt, tokenTypeVec);
1287 right.PushVal (scg, errorAt, tokenTypeVec);
1288 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecModVec);
1289 result.PopPost (scg, errorAt, tokenTypeVec);
1290 }
1291
1292 private static void BinOpStrVecMulFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1293 {
1294 result.PopPre (scg, errorAt);
1295 left.PushVal (scg, errorAt, tokenTypeVec);
1296 right.PushVal (scg, errorAt, tokenTypeFloat);
1297 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecMulFloat);
1298 result.PopPost (scg, errorAt, tokenTypeVec);
1299 }
1300
1301 private static void BinOpStrFloatMulVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1302 {
1303 result.PopPre (scg, errorAt);
1304 left.PushVal (scg, errorAt, tokenTypeFloat);
1305 right.PushVal (scg, errorAt, tokenTypeVec);
1306 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethFloatMulVec);
1307 result.PopPost (scg, errorAt, tokenTypeVec);
1308 }
1309
1310 private static void BinOpStrVecDivFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1311 {
1312 result.PopPre (scg, errorAt);
1313 left.PushVal (scg, errorAt, tokenTypeVec);
1314 right.PushVal (scg, errorAt, tokenTypeFloat);
1315 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecDivFloat);
1316 result.PopPost (scg, errorAt, tokenTypeVec);
1317 }
1318
1319 private static void BinOpStrVecMulInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1320 {
1321 result.PopPre (scg, errorAt);
1322 left.PushVal (scg, errorAt, tokenTypeVec);
1323 right.PushVal (scg, errorAt, tokenTypeInt);
1324 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecMulInt);
1325 result.PopPost (scg, errorAt, tokenTypeVec);
1326 }
1327
1328 private static void BinOpStrIntMulVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1329 {
1330 result.PopPre (scg, errorAt);
1331 left.PushVal (scg, errorAt, tokenTypeInt);
1332 right.PushVal (scg, errorAt, tokenTypeVec);
1333 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethIntMulVec);
1334 result.PopPost (scg, errorAt, tokenTypeVec);
1335 }
1336
1337 private static void BinOpStrVecDivInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1338 {
1339 result.PopPre (scg, errorAt);
1340 left.PushVal (scg, errorAt, tokenTypeVec);
1341 right.PushVal (scg, errorAt, tokenTypeInt);
1342 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecDivInt);
1343 result.PopPost (scg, errorAt, tokenTypeVec);
1344 }
1345
1346 private static void BinOpStrVecMulRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1347 {
1348 result.PopPre (scg, errorAt);
1349 left.PushVal (scg, errorAt, tokenTypeVec);
1350 right.PushVal (scg, errorAt, tokenTypeRot);
1351 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecMulRot);
1352 result.PopPost (scg, errorAt, tokenTypeVec);
1353 }
1354
1355 private static void BinOpStrVecDivRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
1356 {
1357 result.PopPre (scg, errorAt);
1358 left.PushVal (scg, errorAt, tokenTypeVec);
1359 right.PushVal (scg, errorAt, tokenTypeRot);
1360 scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecDivRot);
1361 result.PopPost (scg, errorAt, tokenTypeVec);
1362 }
1363
1364 /**
1365 * @brief These methods are called at runtime as helpers.
1366 * Needed to pick up functionality defined by overloaded operators of LSL_ types.
1367 * They need to be marked public or runtime says they are inaccessible.
1368 */
1369 public static LSL_List MethListAddFloat (LSL_List left, double right)
1370 {
1371 return MethListAddObj (left, new LSL_Float (right));
1372 }
1373 public static LSL_List MethListAddInt (LSL_List left, int right)
1374 {
1375 return MethListAddObj (left, new LSL_Integer (right));
1376 }
1377 public static LSL_List MethListAddKey (LSL_List left, string right)
1378 {
1379 return MethListAddObj (left, new LSL_Key (right));
1380 }
1381 public static LSL_List MethListAddRot (LSL_List left, LSL_Rotation right)
1382 {
1383 return MethListAddObj (left, right);
1384 }
1385 public static LSL_List MethListAddStr (LSL_List left, string right)
1386 {
1387 return MethListAddObj (left, new LSL_String (right));
1388 }
1389 public static LSL_List MethListAddVec (LSL_List left, LSL_Vector right)
1390 {
1391 return MethListAddObj (left, right);
1392 }
1393 public static LSL_List MethListAddObj (LSL_List left, object right)
1394 {
1395 int oldlen = left.Length;
1396 object[] newarr = new object[oldlen+1];
1397 Array.Copy (left.Data, newarr, oldlen);
1398 newarr[oldlen] = right;
1399 return new LSL_List (newarr);
1400 }
1401
1402 public static LSL_List MethListAddList (LSL_List left, LSL_List right)
1403 {
1404 int leftlen = left.Length;
1405 int ritelen = right.Length;
1406 object[] newarr = new object[leftlen+ritelen];
1407 Array.Copy (left.Data, newarr, leftlen);
1408 Array.Copy (right.Data, 0, newarr, leftlen, ritelen);
1409 return new LSL_List (newarr);
1410 }
1411
1412 public static LSL_List MethFloatAddList (double left, LSL_List right)
1413 {
1414 return MethObjAddList (new LSL_Float (left), right);
1415 }
1416 public static LSL_List MethIntAddList (int left, LSL_List right)
1417 {
1418 return MethObjAddList (new LSL_Integer (left), right);
1419 }
1420 public static LSL_List MethKeyAddList (string left, LSL_List right)
1421 {
1422 return MethObjAddList (new LSL_Key (left), right);
1423 }
1424 public static LSL_List MethRotAddList (LSL_Rotation left, LSL_List right)
1425 {
1426 return MethObjAddList (left, right);
1427 }
1428 public static LSL_List MethStrAddList (string left, LSL_List right)
1429 {
1430 return MethObjAddList (new LSL_String (left), right);
1431 }
1432 public static LSL_List MethVecAddList (LSL_Vector left, LSL_List right)
1433 {
1434 return MethObjAddList (left, right);
1435 }
1436 public static LSL_List MethObjAddList (object left, LSL_List right)
1437 {
1438 int oldlen = right.Length;
1439 object[] newarr = new object[oldlen+1];
1440 newarr[0] = left;
1441 Array.Copy (right.Data, 0, newarr, 1, oldlen);
1442 return new LSL_List (newarr);
1443 }
1444
1445 public static bool MethListEqList (LSL_List left, LSL_List right)
1446 {
1447 return left == right;
1448 }
1449
1450 // According to http://wiki.secondlife.com/wiki/LlGetListLength
1451 // jackassed LSL allows 'somelist != []' to get the length of a list
1452 public static int MethListNeList (LSL_List left, LSL_List right)
1453 {
1454 int leftlen = left.Length;
1455 int ritelen = right.Length;
1456 return leftlen - ritelen;
1457 }
1458
1459 public static bool MethRotEqRot (LSL_Rotation left, LSL_Rotation right)
1460 {
1461 return left == right;
1462 }
1463
1464 public static bool MethRotNeRot (LSL_Rotation left, LSL_Rotation right)
1465 {
1466 return left != right;
1467 }
1468
1469 public static LSL_Rotation MethRotAddRot (LSL_Rotation left, LSL_Rotation right)
1470 {
1471 return left + right;
1472 }
1473
1474 public static LSL_Rotation MethRotSubRot (LSL_Rotation left, LSL_Rotation right)
1475 {
1476 return left - right;
1477 }
1478
1479 public static LSL_Rotation MethRotMulRot (LSL_Rotation left, LSL_Rotation right)
1480 {
1481 return left * right;
1482 }
1483
1484 public static LSL_Rotation MethRotDivRot (LSL_Rotation left, LSL_Rotation right)
1485 {
1486 return left / right;
1487 }
1488
1489 public static bool MethVecEqVec (LSL_Vector left, LSL_Vector right)
1490 {
1491 return left == right;
1492 }
1493
1494 public static bool MethVecNeVec (LSL_Vector left, LSL_Vector right)
1495 {
1496 return left != right;
1497 }
1498
1499 public static LSL_Vector MethVecAddVec (LSL_Vector left, LSL_Vector right)
1500 {
1501 return left + right;
1502 }
1503
1504 public static LSL_Vector MethVecSubVec (LSL_Vector left, LSL_Vector right)
1505 {
1506 return left - right;
1507 }
1508
1509 public static double MethVecMulVec (LSL_Vector left, LSL_Vector right)
1510 {
1511 return (double)(left * right).value;
1512 }
1513
1514 public static LSL_Vector MethVecModVec (LSL_Vector left, LSL_Vector right)
1515 {
1516 return left % right;
1517 }
1518
1519 public static LSL_Vector MethVecMulFloat (LSL_Vector left, double right)
1520 {
1521 return left * right;
1522 }
1523
1524 public static LSL_Vector MethFloatMulVec (double left, LSL_Vector right)
1525 {
1526 return left * right;
1527 }
1528
1529 public static LSL_Vector MethVecDivFloat (LSL_Vector left, double right)
1530 {
1531 return left / right;
1532 }
1533
1534 public static LSL_Vector MethVecMulInt (LSL_Vector left, int right)
1535 {
1536 return left * right;
1537 }
1538
1539 public static LSL_Vector MethIntMulVec (int left, LSL_Vector right)
1540 {
1541 return left * right;
1542 }
1543
1544 public static LSL_Vector MethVecDivInt (LSL_Vector left, int right)
1545 {
1546 return left / right;
1547 }
1548
1549 public static LSL_Vector MethVecMulRot (LSL_Vector left, LSL_Rotation right)
1550 {
1551 return left * right;
1552 }
1553
1554 public static LSL_Vector MethVecDivRot (LSL_Vector left, LSL_Rotation right)
1555 {
1556 return left / right;
1557 }
1558 }
1559}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs
new file mode 100644
index 0000000..5219fa8
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs
@@ -0,0 +1,6262 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using OpenSim.Region.ScriptEngine.XMREngine;
30using System;
31using System.Collections.Generic;
32using System.IO;
33using System.Reflection;
34using System.Reflection.Emit;
35using System.Runtime.Serialization;
36using System.Text;
37using System.Threading;
38
39using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
40using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
41using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
42using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
43using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
44using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
45using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
46
47/**
48 * @brief translate a reduced script token into corresponding CIL code.
49 * The single script token contains a tokenized and textured version of the whole script file.
50 */
51
52namespace OpenSim.Region.ScriptEngine.XMREngine
53{
54 public interface IScriptCodeGen
55 {
56 ScriptMyILGen ilGen { get; } // the output instruction stream
57 void ErrorMsg (Token token, string message);
58 void PushDefaultValue (TokenType type);
59 void PushXMRInst ();
60 }
61
62 public class ScriptCodeGen : IScriptCodeGen
63 {
64 private static readonly bool DEBUG_STACKCAPRES = false;
65 private static readonly bool DEBUG_TRYSTMT = false;
66
67 public static readonly string OBJECT_CODE_MAGIC = "XMRObjectCode";
68 public static int COMPILED_VERSION_VALUE = 20; // incremented when compiler changes for compatibility testing
69
70 public static readonly int CALL_FRAME_MEMUSE = 64;
71 public static readonly int STRING_LEN_TO_MEMUSE = 2;
72
73 public static Type xmrInstSuperType = null; // typeof whatever is actually malloc'd for script instances
74 // - must inherit from XMRInstAbstract
75
76 /*
77 * Static tables that there only needs to be one copy of for all.
78 */
79 private static VarDict legalEventHandlers = CreateLegalEventHandlers ();
80 private static CompValu[] zeroCompValus = new CompValu[0];
81 private static TokenType[] zeroArgs = new TokenType[0];
82 private static TokenTypeBool tokenTypeBool = new TokenTypeBool (null);
83 private static TokenTypeExc tokenTypeExc = new TokenTypeExc (null);
84 private static TokenTypeFloat tokenTypeFlt = new TokenTypeFloat (null);
85 private static TokenTypeInt tokenTypeInt = new TokenTypeInt (null);
86 private static TokenTypeObject tokenTypeObj = new TokenTypeObject (null);
87 private static TokenTypeRot tokenTypeRot = new TokenTypeRot (null);
88 private static TokenTypeStr tokenTypeStr = new TokenTypeStr (null);
89 private static TokenTypeVec tokenTypeVec = new TokenTypeVec (null);
90 private static Type[] instanceTypeArg = new Type[] { typeof (XMRInstAbstract) };
91 private static string[] instanceNameArg = new string[] { "$xmrthis" };
92
93 private static ConstructorInfo lslFloatConstructorInfo = typeof (LSL_Float).GetConstructor (new Type[] { typeof (double) });
94 private static ConstructorInfo lslIntegerConstructorInfo = typeof (LSL_Integer).GetConstructor (new Type[] { typeof (int) });
95 private static ConstructorInfo lslListConstructorInfo = typeof (LSL_List).GetConstructor (new Type[] { typeof (object[]) });
96 public static ConstructorInfo lslRotationConstructorInfo = typeof (LSL_Rotation).GetConstructor (new Type[] { typeof (double), typeof (double), typeof (double), typeof (double) });
97 private static ConstructorInfo lslStringConstructorInfo = typeof (LSL_String).GetConstructor (new Type[] { typeof (string) });
98 public static ConstructorInfo lslVectorConstructorInfo = typeof (LSL_Vector).GetConstructor (new Type[] { typeof (double), typeof (double), typeof (double) });
99 private static ConstructorInfo scriptBadCallNoExceptionConstructorInfo = typeof (ScriptBadCallNoException).GetConstructor (new Type[] { typeof (int) });
100 private static ConstructorInfo scriptChangeStateExceptionConstructorInfo = typeof (ScriptChangeStateException).GetConstructor (new Type[] { typeof (int) });
101 private static ConstructorInfo scriptRestoreCatchExceptionConstructorInfo = typeof (ScriptRestoreCatchException).GetConstructor (new Type[] { typeof (Exception) });
102 private static ConstructorInfo scriptUndefinedStateExceptionConstructorInfo = typeof (ScriptUndefinedStateException).GetConstructor (new Type[] { typeof (string) });
103 private static ConstructorInfo sdtClassConstructorInfo = typeof (XMRSDTypeClObj).GetConstructor (new Type[] { typeof (XMRInstAbstract), typeof (int) });
104 private static ConstructorInfo xmrArrayConstructorInfo = typeof (XMR_Array).GetConstructor (new Type[] { typeof (XMRInstAbstract) });
105 private static FieldInfo callModeFieldInfo = typeof (XMRInstAbstract).GetField ("callMode");
106 private static FieldInfo doGblInitFieldInfo = typeof (XMRInstAbstract).GetField ("doGblInit");
107 private static FieldInfo ehArgsFieldInfo = typeof (XMRInstAbstract).GetField ("ehArgs");
108 private static FieldInfo rotationXFieldInfo = typeof (LSL_Rotation).GetField ("x");
109 private static FieldInfo rotationYFieldInfo = typeof (LSL_Rotation).GetField ("y");
110 private static FieldInfo rotationZFieldInfo = typeof (LSL_Rotation).GetField ("z");
111 private static FieldInfo rotationSFieldInfo = typeof (LSL_Rotation).GetField ("s");
112 private static FieldInfo sdtXMRInstFieldInfo = typeof (XMRSDTypeClObj).GetField ("xmrInst");
113 private static FieldInfo vectorXFieldInfo = typeof (LSL_Vector).GetField ("x");
114 private static FieldInfo vectorYFieldInfo = typeof (LSL_Vector).GetField ("y");
115 private static FieldInfo vectorZFieldInfo = typeof (LSL_Vector).GetField ("z");
116
117 private static MethodInfo arrayClearMethodInfo = typeof (XMR_Array).GetMethod ("__pub_clear", new Type[] { });
118 private static MethodInfo arrayCountMethodInfo = typeof (XMR_Array).GetMethod ("__pub_count", new Type[] { });
119 private static MethodInfo arrayIndexMethodInfo = typeof (XMR_Array).GetMethod ("__pub_index", new Type[] { typeof (int) });
120 private static MethodInfo arrayValueMethodInfo = typeof (XMR_Array).GetMethod ("__pub_value", new Type[] { typeof (int) });
121 private static MethodInfo checkRunStackMethInfo = typeof (XMRInstAbstract).GetMethod ("CheckRunStack", new Type[] { });
122 private static MethodInfo checkRunQuickMethInfo = typeof (XMRInstAbstract).GetMethod ("CheckRunQuick", new Type[] { });
123 private static MethodInfo ehArgUnwrapFloat = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapFloat", new Type[] { typeof (object) });
124 private static MethodInfo ehArgUnwrapInteger = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapInteger", new Type[] { typeof (object) });
125 private static MethodInfo ehArgUnwrapRotation = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapRotation", new Type[] { typeof (object) });
126 private static MethodInfo ehArgUnwrapString = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapString", new Type[] { typeof (object) });
127 private static MethodInfo ehArgUnwrapVector = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapVector", new Type[] { typeof (object) });
128 private static MethodInfo xmrArrPubIndexMethod = typeof (XMR_Array).GetMethod ("__pub_index", new Type[] { typeof (int) });
129 private static MethodInfo xmrArrPubValueMethod = typeof (XMR_Array).GetMethod ("__pub_value", new Type[] { typeof (int) });
130 private static MethodInfo captureStackFrameMethodInfo = typeof (XMRInstAbstract).GetMethod ("CaptureStackFrame", new Type[] { typeof (string), typeof (int), typeof (int) });
131 private static MethodInfo restoreStackFrameMethodInfo = typeof (XMRInstAbstract).GetMethod ("RestoreStackFrame", new Type[] { typeof (string), typeof (int).MakeByRefType () });
132 private static MethodInfo stringCompareMethodInfo = GetStaticMethod (typeof (String), "Compare", new Type[] { typeof (string), typeof (string), typeof (StringComparison) });
133 private static MethodInfo stringConcat2MethodInfo = GetStaticMethod (typeof (String), "Concat", new Type[] { typeof (string), typeof (string) });
134 private static MethodInfo stringConcat3MethodInfo = GetStaticMethod (typeof (String), "Concat", new Type[] { typeof (string), typeof (string), typeof (string) });
135 private static MethodInfo stringConcat4MethodInfo = GetStaticMethod (typeof (String), "Concat", new Type[] { typeof (string), typeof (string), typeof (string), typeof (string) });
136 private static MethodInfo lslRotationNegateMethodInfo = GetStaticMethod (typeof (ScriptCodeGen),
137 "LSLRotationNegate",
138 new Type[] { typeof (LSL_Rotation) });
139 private static MethodInfo lslVectorNegateMethodInfo = GetStaticMethod (typeof (ScriptCodeGen),
140 "LSLVectorNegate",
141 new Type[] { typeof (LSL_Vector) });
142 private static MethodInfo scriptRestoreCatchExceptionUnwrap = GetStaticMethod (typeof (ScriptRestoreCatchException), "Unwrap", new Type[] { typeof (Exception) });
143 private static MethodInfo thrownExceptionWrapMethodInfo = GetStaticMethod (typeof (ScriptThrownException), "Wrap", new Type[] { typeof (object) });
144 private static MethodInfo heapTrackerListPush = typeof (HeapTrackerList). GetMethod ("Push", new Type[0]);
145 private static MethodInfo heapTrackerObjectPush = typeof (HeapTrackerObject).GetMethod ("Push", new Type[0]);
146 private static MethodInfo heapTrackerStringPush = typeof (HeapTrackerString).GetMethod ("Push", new Type[0]);
147
148 private static MethodInfo catchExcToStrMethodInfo = GetStaticMethod (typeof (ScriptCodeGen),
149 "CatchExcToStr",
150 new Type[] { typeof (Exception) });
151
152 private static MethodInfo consoleWriteMethodInfo = GetStaticMethod (typeof (ScriptCodeGen), "ConsoleWrite", new Type[] { typeof (object) });
153 public static void ConsoleWrite (object o)
154 {
155 if (o == null) o = "<<null>>";
156 Console.Write (o.ToString ());
157 }
158
159 public static bool CodeGen (TokenScript tokenScript, BinaryWriter objFileWriter, string sourceHash)
160 {
161 /*
162 * Run compiler such that it has a 'this' context for convenience.
163 */
164 ScriptCodeGen scg = new ScriptCodeGen (tokenScript, objFileWriter, sourceHash);
165
166 /*
167 * Return pointer to resultant script object code.
168 */
169 return !scg.youveAnError;
170 }
171
172 /*
173 * There is one set of these variables for each script being compiled.
174 */
175 private bool mightGetHere = false;
176 private bool youveAnError = false;
177 private BreakContTarg curBreakTarg = null;
178 private BreakContTarg curContTarg = null;
179 private int lastErrorLine = 0;
180 private int nStates = 0;
181 private string sourceHash;
182 private string lastErrorFile = "";
183 private string[] stateNames;
184 private XMRInstArSizes glblSizes = new XMRInstArSizes ();
185 private Token errorMessageToken = null;
186 private TokenDeclVar curDeclFunc = null;
187 private TokenStmtBlock curStmtBlock = null;
188 private BinaryWriter objFileWriter = null;
189 private TokenScript tokenScript = null;
190 public int tempCompValuNum = 0;
191 private TokenDeclSDTypeClass currentSDTClass = null;
192
193 private Dictionary<string, int> stateIndices = null;
194
195 // These get cleared at beginning of every function definition
196 private ScriptMyLocal instancePointer; // holds XMRInstanceSuperType pointer
197 private ScriptMyLabel retLabel = null; // where to jump to exit function
198 private ScriptMyLocal retValue = null;
199 private ScriptMyLocal actCallNo = null; // for the active try/catch/finally stack or the big one outside them all
200 private LinkedList<CallLabel> actCallLabels = new LinkedList<CallLabel> (); // for the active try/catch/finally stack or the big one outside them all
201 private LinkedList<CallLabel> allCallLabels = new LinkedList<CallLabel> (); // this holds each and every one for all stacks in total
202 public CallLabel openCallLabel = null; // only one call label can be open at a time
203 // - the call label is open from the time of CallPre() until corresponding CallPost()
204 // - so no non-trivial pushes/pops etc allowed between a CallPre() and a CallPost()
205
206 private ScriptMyILGen _ilGen;
207 public ScriptMyILGen ilGen { get { return _ilGen; } }
208
209 private ScriptCodeGen (TokenScript tokenScript, BinaryWriter objFileWriter, string sourceHash)
210 {
211 this.tokenScript = tokenScript;
212 this.objFileWriter = objFileWriter;
213 this.sourceHash = sourceHash;
214
215 try {
216 PerformCompilation ();
217 } catch {
218 // if we've an error, just punt on any exception
219 // it's probably just a null reference from something
220 // not being filled in etc.
221 if (!youveAnError) throw;
222 } finally {
223 objFileWriter = null;
224 }
225 }
226
227 /**
228 * @brief Convert 'tokenScript' to 'objFileWriter' format.
229 * 'tokenScript' is a parsed/reduced abstract syntax tree of the script source file
230 * 'objFileWriter' is a serialized form of the CIL code that we generate
231 */
232 private void PerformCompilation ()
233 {
234 /*
235 * errorMessageToken is used only when the given token doesn't have a
236 * output delegate associated with it such as for backend API functions
237 * that only have one copy for the whole system. It is kept up-to-date
238 * approximately but is rarely needed so going to assume it doesn't have
239 * to be exact.
240 */
241 errorMessageToken = tokenScript;
242
243 /*
244 * Set up dictionary to translate state names to their index number.
245 */
246 stateIndices = new Dictionary<string, int> ();
247
248 /*
249 * Assign each state its own unique index.
250 * The default state gets 0.
251 */
252 nStates = 0;
253 tokenScript.defaultState.body.index = nStates ++;
254 stateIndices.Add ("default", 0);
255 foreach (KeyValuePair<string, TokenDeclState> kvp in tokenScript.states) {
256 TokenDeclState declState = kvp.Value;
257 declState.body.index = nStates ++;
258 stateIndices.Add (declState.name.val, declState.body.index);
259 }
260
261 /*
262 * Make up an array that translates state indices to state name strings.
263 */
264 stateNames = new string[nStates];
265 stateNames[0] = "default";
266 foreach (KeyValuePair<string, TokenDeclState> kvp in tokenScript.states) {
267 TokenDeclState declState = kvp.Value;
268 stateNames[declState.body.index] = declState.name.val;
269 }
270
271 /*
272 * Make sure we have delegates for all script-defined functions and methods,
273 * creating anonymous ones if needed. Note that this includes all property
274 * getter and setter methods.
275 */
276 foreach (TokenDeclVar declFunc in tokenScript.variablesStack) {
277 if (declFunc.retType != null) {
278 declFunc.GetDelType ();
279 }
280 }
281 while (true) {
282 bool itIsAGoodDayToDie = true;
283 try {
284 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
285 itIsAGoodDayToDie = false;
286 if (sdType is TokenDeclSDTypeClass) {
287 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
288 foreach (TokenDeclVar declFunc in sdtClass.members) {
289 if (declFunc.retType != null) {
290 declFunc.GetDelType ();
291 if (declFunc.funcNameSig.val.StartsWith ("$ctor(")) {
292 // this is for the "$new()" static method that we create below.
293 // See GenerateStmtNewobj() etc.
294 new TokenTypeSDTypeDelegate (declFunc, sdtClass.MakeRefToken (declFunc),
295 declFunc.argDecl.types, tokenScript);
296 }
297 }
298 }
299 }
300 if (sdType is TokenDeclSDTypeInterface) {
301 TokenDeclSDTypeInterface sdtIFace = (TokenDeclSDTypeInterface)sdType;
302 foreach (TokenDeclVar declFunc in sdtIFace.methsNProps) {
303 if (declFunc.retType != null) {
304 declFunc.GetDelType ();
305 }
306 }
307 }
308 itIsAGoodDayToDie = true;
309 }
310 break;
311 } catch (InvalidOperationException) {
312 if (!itIsAGoodDayToDie) throw;
313 // fetching the delegate created an anonymous entry in tokenScript.sdSrcTypesValues
314 // which made the foreach statement puque, so start over...
315 }
316 }
317
318 /*
319 * No more types can be defined or we won't be able to write them to the object file.
320 */
321 tokenScript.sdSrcTypesSeal ();
322
323 /*
324 * Assign all global variables a slot in its corresponding XMRInstance.gbl<Type>s[] array.
325 * Global variables are simply elements of those arrays at runtime, thus we don't need to create
326 * an unique class for each script, we can just use XMRInstance as is for all.
327 */
328 foreach (TokenDeclVar declVar in tokenScript.variablesStack) {
329
330 /*
331 * Omit 'constant' variables as they are coded inline so don't need a slot.
332 */
333 if (declVar.constant) continue;
334
335 /*
336 * Do functions later.
337 */
338 if (declVar.retType != null) continue;
339
340 /*
341 * Create entry in the value array for the variable or property.
342 */
343 declVar.location = new CompValuGlobalVar (declVar, glblSizes);
344 }
345
346 /*
347 * Likewise for any static fields in script-defined classes.
348 * They can be referenced anywhere by <typename>.<fieldname>, see
349 * GenerateFromLValSField().
350 */
351 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
352 if (!(sdType is TokenDeclSDTypeClass)) continue;
353 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
354
355 foreach (TokenDeclVar declVar in sdtClass.members) {
356
357 /*
358 * Omit 'constant' variables as they are coded inline so don't need a slot.
359 */
360 if (declVar.constant) continue;
361
362 /*
363 * Do methods later.
364 */
365 if (declVar.retType != null) continue;
366
367 /*
368 * Ignore non-static fields for now.
369 * They get assigned below.
370 */
371 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) == 0) continue;
372
373 /*
374 * Create entry in the value array for the static field or static property.
375 */
376 declVar.location = new CompValuGlobalVar (declVar, glblSizes);
377 }
378 }
379
380 /*
381 * Assign slots for all interface method prototypes.
382 * These indices are used to index the array of delegates that holds a class' implementation of an
383 * interface.
384 * Properties do not get a slot because they aren't called as such. But their corresponding
385 * <name>$get() and <name>$set(<type>) methods are in the table and they each get a slot.
386 */
387 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
388 if (!(sdType is TokenDeclSDTypeInterface)) continue;
389 TokenDeclSDTypeInterface sdtIFace = (TokenDeclSDTypeInterface)sdType;
390 int vti = 0;
391 foreach (TokenDeclVar im in sdtIFace.methsNProps) {
392 if ((im.getProp == null) && (im.setProp == null)) {
393 im.vTableIndex = vti ++;
394 }
395 }
396 }
397
398 /*
399 * Assign slots for all instance fields and virtual methods of script-defined classes.
400 */
401 int maxExtends = tokenScript.sdSrcTypesCount;
402 bool didOne;
403 do {
404 didOne = false;
405 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
406 if (!(sdType is TokenDeclSDTypeClass)) continue;
407 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
408 if (sdtClass.slotsAssigned) continue;
409
410 /*
411 * If this class extends another, the extended class has to already
412 * be set up, because our slots add on to the end of the extended class.
413 */
414 TokenDeclSDTypeClass extends = sdtClass.extends;
415 if (extends != null) {
416 if (!extends.slotsAssigned) continue;
417 sdtClass.instSizes = extends.instSizes;
418 sdtClass.numVirtFuncs = extends.numVirtFuncs;
419 sdtClass.numInterfaces = extends.numInterfaces;
420
421 int n = maxExtends;
422 for (TokenDeclSDTypeClass ex = extends; ex != null; ex = ex.extends) {
423 if (-- n < 0) break;
424 }
425 if (n < 0) {
426 ErrorMsg (sdtClass, "loop in extended classes");
427 sdtClass.slotsAssigned = true;
428 continue;
429 }
430 }
431
432 /*
433 * Extended class's slots all assigned, assign our instance fields
434 * slots in the XMRSDTypeClObj arrays.
435 */
436 foreach (TokenDeclVar declVar in sdtClass.members) {
437 if (declVar.retType != null) continue;
438 if (declVar.constant) continue;
439 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) continue;
440 if ((declVar.getProp == null) && (declVar.setProp == null)) {
441 declVar.type.AssignVarSlot (declVar, sdtClass.instSizes);
442 }
443 }
444
445 /*
446 * ... and assign virtual method vtable slots.
447 *
448 * - : error if any overridden method, doesn't need a slot
449 * abstract : error if any overridden method, alloc new slot but leave it empty
450 * new : ignore any overridden method, doesn't need a slot
451 * new abstract : ignore any overridden method, alloc new slot but leave it empty
452 * override : must have overridden abstract/virtual, use old slot
453 * override abstract : must have overridden abstract, use old slot but it is still empty
454 * static : error if any overridden method, doesn't need a slot
455 * static new : ignore any overridden method, doesn't need a slot
456 * virtual : error if any overridden method, alloc new slot and fill it in
457 * virtual new : ignore any overridden method, alloc new slot and fill it in
458 */
459 foreach (TokenDeclVar declFunc in sdtClass.members) {
460 if (declFunc.retType == null) continue;
461 curDeclFunc = declFunc;
462
463 /*
464 * See if there is a method in an extended class that this method overshadows.
465 * If so, check for various conflicts.
466 * In any case, SDT_NEW on our method means to ignore any overshadowed method.
467 */
468 string declLongName = sdtClass.longName.val + "." + declFunc.funcNameSig.val;
469 uint declFlags = declFunc.sdtFlags;
470 TokenDeclVar overridden = null;
471 if ((declFlags & ScriptReduce.SDT_NEW) == 0) {
472 for (TokenDeclSDTypeClass sdtd = extends; sdtd != null; sdtd = sdtd.extends) {
473 overridden = FindExactWithRet (sdtd.members, declFunc.name, declFunc.retType, declFunc.argDecl.types);
474 if (overridden != null) break;
475 }
476 }
477 if (overridden != null) do {
478 string overLongName = overridden.sdtClass.longName.val;
479 uint overFlags = overridden.sdtFlags;
480
481 /*
482 * See if overridden method allows itself to be overridden.
483 */
484 if ((overFlags & ScriptReduce.SDT_ABSTRACT) != 0) {
485 if ((declFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE)) == 0) {
486 ErrorMsg (declFunc, declLongName + " overshadows abstract " + overLongName + " but is not marked abstract, new or override");
487 break;
488 }
489 } else if ((overFlags & ScriptReduce.SDT_FINAL) != 0) {
490 ErrorMsg (declFunc, declLongName + " overshadows final " + overLongName + " but is not marked new");
491 } else if ((overFlags & (ScriptReduce.SDT_OVERRIDE | ScriptReduce.SDT_VIRTUAL)) != 0) {
492 if ((declFlags & (ScriptReduce.SDT_NEW | ScriptReduce.SDT_OVERRIDE)) == 0) {
493 ErrorMsg (declFunc, declLongName + " overshadows virtual " + overLongName + " but is not marked new or override");
494 break;
495 }
496 } else {
497 ErrorMsg (declFunc, declLongName + " overshadows non-virtual " + overLongName + " but is not marked new");
498 break;
499 }
500
501 /*
502 * See if our method is capable of overriding the other method.
503 */
504 if ((declFlags & ScriptReduce.SDT_ABSTRACT) != 0) {
505 if ((overFlags & ScriptReduce.SDT_ABSTRACT) == 0) {
506 ErrorMsg (declFunc, declLongName + " abstract overshadows non-abstract " + overLongName + " but is not marked new");
507 break;
508 }
509 } else if ((declFlags & ScriptReduce.SDT_OVERRIDE) != 0) {
510 if ((overFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE | ScriptReduce.SDT_VIRTUAL)) == 0) {
511 ErrorMsg (declFunc, declLongName + " override overshadows non-abstract/non-virtual " + overLongName);
512 break;
513 }
514 } else {
515 ErrorMsg (declFunc, declLongName + " overshadows " + overLongName + " but is not marked new");
516 break;
517 }
518 } while (false);
519
520 /*
521 * Now we can assign it a vtable slot if it needs one (ie, it is virtual).
522 */
523 declFunc.vTableIndex = -1;
524 if (overridden != null) {
525 declFunc.vTableIndex = overridden.vTableIndex;
526 } else if ((declFlags & ScriptReduce.SDT_OVERRIDE) != 0) {
527 ErrorMsg (declFunc, declLongName + " marked override but nothing matching found that it overrides");
528 }
529 if ((declFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_VIRTUAL)) != 0) {
530 declFunc.vTableIndex = sdtClass.numVirtFuncs ++;
531 }
532 }
533 curDeclFunc = null;
534
535 /*
536 * ... and assign implemented interface slots.
537 * Note that our implementations of a given interface is completely independent of any
538 * rootward class's implementation of that same interface.
539 */
540 int nIFaces = sdtClass.numInterfaces + sdtClass.implements.Count;
541 sdtClass.iFaces = new TokenDeclSDTypeInterface[nIFaces];
542 sdtClass.iImplFunc = new TokenDeclVar[nIFaces][];
543 for (int i = 0; i < sdtClass.numInterfaces; i ++) {
544 sdtClass.iFaces[i] = extends.iFaces[i];
545 sdtClass.iImplFunc[i] = extends.iImplFunc[i];
546 }
547
548 foreach (TokenDeclSDTypeInterface intf in sdtClass.implements) {
549 int i = sdtClass.numInterfaces ++;
550 sdtClass.iFaces[i] = intf;
551 sdtClass.intfIndices.Add (intf.longName.val, i);
552 int nMeths = 0;
553 foreach (TokenDeclVar m in intf.methsNProps) {
554 if ((m.getProp == null) && (m.setProp == null)) nMeths ++;
555 }
556 sdtClass.iImplFunc[i] = new TokenDeclVar[nMeths];
557 }
558
559 foreach (TokenDeclVar classMeth in sdtClass.members) {
560 if (classMeth.retType == null) continue;
561 curDeclFunc = classMeth;
562 for (TokenIntfImpl intfImpl = classMeth.implements; intfImpl != null; intfImpl = (TokenIntfImpl)intfImpl.nextToken) {
563
564 /*
565 * One of the class methods implements an interface method.
566 * Try to find the interface method that is implemented and verify its signature.
567 */
568 TokenDeclSDTypeInterface intfType = intfImpl.intfType.decl;
569 TokenDeclVar intfMeth = FindExactWithRet (intfType.methsNProps, intfImpl.methName, classMeth.retType, classMeth.argDecl.types);
570 if (intfMeth == null) {
571 ErrorMsg (intfImpl, "interface does not define method " + intfImpl.methName.val + classMeth.argDecl.GetArgSig ());
572 continue;
573 }
574
575 /*
576 * See if this class was declared to implement that interface.
577 */
578 bool found = false;
579 foreach (TokenDeclSDTypeInterface intf in sdtClass.implements) {
580 if (intf == intfType) {
581 found = true;
582 break;
583 }
584 }
585 if (!found) {
586 ErrorMsg (intfImpl, "class not declared to implement " + intfType.longName.val);
587 continue;
588 }
589
590 /*
591 * Get index in iFaces[] and iImplFunc[] arrays.
592 * Start scanning from the end in case one of our rootward classes also implements the interface.
593 * We should always be successful because we know by now that this class implements the interface.
594 */
595 int i;
596 for (i = sdtClass.numInterfaces; -- i >= 0;) {
597 if (sdtClass.iFaces[i] == intfType) break;
598 }
599
600 /*
601 * Now remember which of the class methods implements that interface method.
602 */
603 int j = intfMeth.vTableIndex;
604 if (sdtClass.iImplFunc[i][j] != null) {
605 ErrorMsg (intfImpl, "also implemented by " + sdtClass.iImplFunc[i][j].funcNameSig.val);
606 continue;
607 }
608 sdtClass.iImplFunc[i][j] = classMeth;
609 }
610 }
611 curDeclFunc = null;
612
613 /*
614 * Now make sure this class implements all methods for all declared interfaces.
615 */
616 for (int i = sdtClass.numInterfaces - sdtClass.implements.Count; i < sdtClass.numInterfaces; i ++) {
617 TokenDeclVar[] implementations = sdtClass.iImplFunc[i];
618 for (int j = implementations.Length; -- j >= 0;) {
619 if (implementations[j] == null) {
620 TokenDeclSDTypeInterface intf = sdtClass.iFaces[i];
621 TokenDeclVar meth = null;
622 foreach (TokenDeclVar im in intf.methsNProps) {
623 if (im.vTableIndex == j) {
624 meth = im;
625 break;
626 }
627 }
628 ErrorMsg (sdtClass, "does not implement " + intf.longName.val + "." + meth.funcNameSig.val);
629 }
630 }
631 }
632
633 /*
634 * All slots for this class have been assigned.
635 */
636 sdtClass.slotsAssigned = true;
637 didOne = true;
638 }
639 } while (didOne);
640
641 /*
642 * Compute final values for all variables/fields declared as 'constant'.
643 * Note that there may be forward references.
644 */
645 do {
646 didOne = false;
647 foreach (TokenDeclVar tdv in tokenScript.variablesStack) {
648 if (tdv.constant && !(tdv.init is TokenRValConst)) {
649 tdv.init = tdv.init.TryComputeConstant (LookupInitConstants, ref didOne);
650 }
651 }
652 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
653 if (!(sdType is TokenDeclSDTypeClass)) continue;
654 currentSDTClass = (TokenDeclSDTypeClass)sdType;
655 foreach (TokenDeclVar tdv in currentSDTClass.members) {
656 if (tdv.constant && !(tdv.init is TokenRValConst)) {
657 tdv.init = tdv.init.TryComputeConstant (LookupInitConstants, ref didOne);
658 }
659 }
660 }
661 currentSDTClass = null;
662 } while (didOne);
663
664 /*
665 * Now we should be able to assign all those constants their type and location.
666 */
667 foreach (TokenDeclVar tdv in tokenScript.variablesStack) {
668 if (tdv.constant) {
669 if (tdv.init is TokenRValConst) {
670 TokenRValConst rvc = (TokenRValConst)tdv.init;
671 tdv.type = rvc.tokType;
672 tdv.location = rvc.GetCompValu ();
673 } else {
674 ErrorMsg (tdv, "value is not constant");
675 }
676 }
677 }
678 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
679 if (!(sdType is TokenDeclSDTypeClass)) continue;
680 currentSDTClass = (TokenDeclSDTypeClass)sdType;
681 foreach (TokenDeclVar tdv in currentSDTClass.members) {
682 if (tdv.constant) {
683 if (tdv.init is TokenRValConst) {
684 TokenRValConst rvc = (TokenRValConst)tdv.init;
685 tdv.type = rvc.tokType;
686 tdv.location = rvc.GetCompValu ();
687 } else {
688 ErrorMsg (tdv, "value is not constant");
689 }
690 }
691 }
692 }
693 currentSDTClass = null;
694
695 /*
696 * For all classes that define all the methods needed for the class, ie, they aren't abstract,
697 * define a static class.$new() method with same args as the $ctor(s). This will allow the
698 * class to be instantiated via the new operator.
699 */
700 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
701 if (!(sdType is TokenDeclSDTypeClass)) continue;
702 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
703
704 /*
705 * See if the class as it stands would be able to fill every slot of its vtable.
706 */
707 bool[] filled = new bool[sdtClass.numVirtFuncs];
708 int numFilled = 0;
709 for (TokenDeclSDTypeClass sdtc = sdtClass; sdtc != null; sdtc = sdtc.extends) {
710 foreach (TokenDeclVar tdf in sdtc.members) {
711 if ((tdf.retType != null) && (tdf.vTableIndex >= 0) && ((tdf.sdtFlags & ScriptReduce.SDT_ABSTRACT) == 0)) {
712 if (!filled[tdf.vTableIndex]) {
713 filled[tdf.vTableIndex] = true;
714 numFilled ++;
715 }
716 }
717 }
718 }
719
720 /*
721 * If so, define a static class.$new() method for every constructor defined for the class.
722 * Give it the same access (private/protected/public) as the script declared for the constructor.
723 * Note that the reducer made sure there is at least a default constructor for every class.
724 */
725 if (numFilled >= sdtClass.numVirtFuncs) {
726 List<TokenDeclVar> newobjDeclFuncs = new List<TokenDeclVar> ();
727 foreach (TokenDeclVar ctorDeclFunc in sdtClass.members) {
728 if ((ctorDeclFunc.funcNameSig != null) && ctorDeclFunc.funcNameSig.val.StartsWith ("$ctor(")) {
729 TokenDeclVar newobjDeclFunc = DefineNewobjFunc (ctorDeclFunc);
730 newobjDeclFuncs.Add (newobjDeclFunc);
731 }
732 }
733 foreach (TokenDeclVar newobjDeclFunc in newobjDeclFuncs) {
734 sdtClass.members.AddEntry (newobjDeclFunc);
735 }
736 }
737 }
738
739 /*
740 * Write fixed portion of object file.
741 */
742 objFileWriter.Write (OBJECT_CODE_MAGIC.ToCharArray ());
743 objFileWriter.Write (COMPILED_VERSION_VALUE);
744 objFileWriter.Write (sourceHash);
745 objFileWriter.Write (tokenScript.expiryDays);
746 glblSizes.WriteToFile (objFileWriter);
747
748 objFileWriter.Write (nStates);
749 for (int i = 0; i < nStates; i ++) {
750 objFileWriter.Write (stateNames[i]);
751 }
752
753 /*
754 * For debugging, we also write out global variable array slot assignments.
755 */
756 foreach (TokenDeclVar declVar in tokenScript.variablesStack) {
757 if (declVar.retType == null) {
758 WriteOutGblAssignment ("", declVar);
759 }
760 }
761 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
762 if (!(sdType is TokenDeclSDTypeClass)) continue;
763 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
764 foreach (TokenDeclVar declVar in sdtClass.members) {
765 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) {
766 WriteOutGblAssignment (sdtClass.longName.val + ".", declVar);
767 }
768 }
769 }
770 objFileWriter.Write ("");
771
772 /*
773 * Write out script-defined types.
774 */
775 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
776 objFileWriter.Write (sdType.longName.val);
777 sdType.WriteToFile (objFileWriter);
778 }
779 objFileWriter.Write ("");
780
781 /*
782 * Output function headers then bodies.
783 * Do all headers first in case bodies do forward references.
784 * Do both global functions, script-defined class static methods and
785 * script-defined instance methods, as we handle the differences
786 * during compilation of the functions/methods themselves.
787 */
788 for (int pass = 0; pass < 2; pass ++) {
789 foreach (TokenDeclVar declFunc in tokenScript.variablesStack) {
790 if (declFunc.retType != null) {
791 if (pass == 0) GenerateMethodHeader (declFunc);
792 else GenerateMethodBody (declFunc);
793 }
794 }
795 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
796 if (sdType is TokenDeclSDTypeClass) {
797 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
798 foreach (TokenDeclVar declFunc in sdtClass.members) {
799 if ((declFunc.retType != null) && ((declFunc.sdtFlags & ScriptReduce.SDT_ABSTRACT) == 0)) {
800 if (pass == 0) GenerateMethodHeader (declFunc);
801 else GenerateMethodBody (declFunc);
802 }
803 }
804 }
805 }
806 }
807
808 /*
809 * Output default state event handler functions.
810 * Each event handler is a private static method named 'default <eventname>'.
811 * Splice in a default state_entry() handler if none defined so we can init global vars.
812 */
813 TokenDeclVar defaultStateEntry = null;
814 for (defaultStateEntry = tokenScript.defaultState.body.eventFuncs;
815 defaultStateEntry != null;
816 defaultStateEntry = (TokenDeclVar)defaultStateEntry.nextToken) {
817 if (defaultStateEntry.funcNameSig.val == "state_entry()") break;
818 }
819 if (defaultStateEntry == null) {
820 defaultStateEntry = new TokenDeclVar (tokenScript.defaultState.body, null, tokenScript);
821 defaultStateEntry.name = new TokenName (tokenScript.defaultState.body, "state_entry");
822 defaultStateEntry.retType = new TokenTypeVoid (tokenScript.defaultState.body);
823 defaultStateEntry.argDecl = new TokenArgDecl (tokenScript.defaultState.body);
824 defaultStateEntry.body = new TokenStmtBlock (tokenScript.defaultState.body);
825 defaultStateEntry.body.function = defaultStateEntry;
826
827 defaultStateEntry.nextToken = tokenScript.defaultState.body.eventFuncs;
828 tokenScript.defaultState.body.eventFuncs = defaultStateEntry;
829 }
830 GenerateStateEventHandlers ("default", tokenScript.defaultState.body);
831
832 /*
833 * Output script-defined state event handler methods.
834 * Each event handler is a private static method named <statename> <eventname>
835 */
836 foreach (KeyValuePair<string, TokenDeclState> kvp in tokenScript.states) {
837 TokenDeclState declState = kvp.Value;
838 GenerateStateEventHandlers (declState.name.val, declState.body);
839 }
840
841 ScriptObjWriter.TheEnd (objFileWriter);
842 }
843
844 /**
845 * @brief Write out what slot was assigned for a global or sdtclass static variable.
846 * Constants, functions, instance fields, methods, properties do not have slots in the global variables arrays.
847 */
848 private void WriteOutGblAssignment (string pfx, TokenDeclVar declVar)
849 {
850 if (!declVar.constant && (declVar.retType == null) && (declVar.getProp == null) && (declVar.setProp == null)) {
851 objFileWriter.Write (pfx + declVar.name.val); // string
852 objFileWriter.Write (declVar.vTableArray.Name); // string
853 objFileWriter.Write (declVar.vTableIndex); // int
854 }
855 }
856
857 /**
858 * @brief generate event handler code
859 * Writes out a function definition for each state handler
860 * named <statename> <eventname>
861 *
862 * However, each has just 'XMRInstance __sw' as its single argument
863 * and each of its user-visible argments is extracted from __sw.ehArgs[].
864 *
865 * So we end up generating something like this:
866 *
867 * private static void <statename> <eventname>(XMRInstance __sw)
868 * {
869 * <typeArg0> <nameArg0> = (<typeArg0>)__sw.ehArgs[0];
870 * <typeArg1> <nameArg1> = (<typeArg1>)__sw.ehArgs[1];
871 *
872 * ... script code ...
873 * }
874 *
875 * The continuations code assumes there will be no references to ehArgs[]
876 * after the first call to CheckRun() as CheckRun() makes no attempt to
877 * serialize the ehArgs[] array, as doing so would be redundant. Any values
878 * from ehArgs[] that are being used will be in local stack variables and
879 * thus preserved that way.
880 */
881 private void GenerateStateEventHandlers (string statename, TokenStateBody body)
882 {
883 Dictionary<string,TokenDeclVar> statehandlers = new Dictionary<string,TokenDeclVar> ();
884 for (Token t = body.eventFuncs; t != null; t = t.nextToken) {
885 TokenDeclVar tdv = (TokenDeclVar)t;
886 string eventname = tdv.GetSimpleName ();
887 if (statehandlers.ContainsKey (eventname)) {
888 ErrorMsg (tdv, "event handler " + eventname + " already defined for state " + statename);
889 } else {
890 statehandlers.Add (eventname, tdv);
891 GenerateEventHandler (statename, tdv);
892 }
893 }
894 }
895
896 private void GenerateEventHandler (string statename, TokenDeclVar declFunc)
897 {
898 string eventname = declFunc.GetSimpleName ();
899 TokenArgDecl argDecl = declFunc.argDecl;
900
901 /*
902 * Make sure event handler name is valid and that number and type of arguments is correct.
903 * Apparently some scripts exist with fewer than correct number of args in their declaration
904 * so allow for that. It is ok because the handlers are called with the arguments in an
905 * object[] array, and we just won't access the missing argments in the vector. But the
906 * specified types must match one of the prototypes in legalEventHandlers.
907 */
908 TokenDeclVar protoDeclFunc = legalEventHandlers.FindExact (eventname, argDecl.types);
909 if (protoDeclFunc == null) {
910 ErrorMsg (declFunc, "unknown event handler " + eventname + argDecl.GetArgSig ());
911 return;
912 }
913
914 /*
915 * Output function header.
916 * They just have the XMRInstAbstract pointer as the one argument.
917 */
918 string functionName = statename + " " + eventname;
919 _ilGen = new ScriptObjWriter (tokenScript,
920 functionName,
921 typeof (void),
922 instanceTypeArg,
923 instanceNameArg,
924 objFileWriter);
925 StartFunctionBody (declFunc);
926
927 /*
928 * Create a temp to hold XMRInstanceSuperType version of arg 0.
929 */
930 instancePointer = ilGen.DeclareLocal (xmrInstSuperType, "__xmrinst");
931 ilGen.Emit (declFunc, OpCodes.Ldarg_0);
932 ilGen.Emit (declFunc, OpCodes.Castclass, xmrInstSuperType);
933 ilGen.Emit (declFunc, OpCodes.Stloc, instancePointer);
934
935 /*
936 * Output args as variable definitions and initialize each from __sw.ehArgs[].
937 * If the script writer goofed, the typecast will complain.
938 */
939 int nArgs = argDecl.vars.Length;
940 for (int i = 0; i < nArgs; i ++) {
941
942 /*
943 * Say that the argument variable is going to be located in a local var.
944 */
945 TokenDeclVar argVar = argDecl.vars[i];
946 TokenType argTokType = argVar.type;
947 CompValuLocalVar local = new CompValuLocalVar (argTokType, argVar.name.val, this);
948 argVar.location = local;
949
950 /*
951 * Copy from the ehArgs[i] element to the temp var.
952 * Cast as needed, there is a lot of craziness like OpenMetaverse.Quaternion.
953 */
954 local.PopPre (this, argVar.name);
955 PushXMRInst (); // instance
956 ilGen.Emit (declFunc, OpCodes.Ldfld, ehArgsFieldInfo); // instance.ehArgs (array of objects)
957 ilGen.Emit (declFunc, OpCodes.Ldc_I4, i); // array index = i
958 ilGen.Emit (declFunc, OpCodes.Ldelem, typeof (object)); // select the argument we want
959 TokenType stkTokType = tokenTypeObj; // stack has a type 'object' on it now
960 Type argSysType = argTokType.ToSysType (); // this is the type the script expects
961 if (argSysType == typeof (double)) { // LSL_Float/double -> double
962 ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapFloat);
963 stkTokType = tokenTypeFlt; // stack has a type 'double' on it now
964 }
965 if (argSysType == typeof (int)) { // LSL_Integer/int -> int
966 ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapInteger);
967 stkTokType = tokenTypeInt; // stack has a type 'int' on it now
968 }
969 if (argSysType == typeof (LSL_List)) { // LSL_List -> LSL_List
970 TypeCast.CastTopOfStack (this, argVar.name, stkTokType, argTokType, true);
971 stkTokType = argTokType; // stack has a type 'LSL_List' on it now
972 }
973 if (argSysType == typeof (LSL_Rotation)) { // OpenMetaverse.Quaternion/LSL_Rotation -> LSL_Rotation
974 ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapRotation);
975 stkTokType = tokenTypeRot; // stack has a type 'LSL_Rotation' on it now
976 }
977 if (argSysType == typeof (string)) { // LSL_Key/LSL_String/string -> string
978 ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapString);
979 stkTokType = tokenTypeStr; // stack has a type 'string' on it now
980 }
981 if (argSysType == typeof (LSL_Vector)) { // OpenMetaverse.Vector3/LSL_Vector -> LSL_Vector
982 ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapVector);
983 stkTokType = tokenTypeVec; // stack has a type 'LSL_Vector' on it now
984 }
985 local.PopPost (this, argVar.name, stkTokType); // pop stack type into argtype
986 }
987
988 /*
989 * Output code for the statements and clean up.
990 */
991 GenerateFuncBody ();
992 }
993
994 /**
995 * @brief generate header for an arbitrary script-defined global function.
996 * @param declFunc = function being defined
997 */
998 private void GenerateMethodHeader (TokenDeclVar declFunc)
999 {
1000 curDeclFunc = declFunc;
1001
1002 /*
1003 * Make up array of all argument types as seen by the code generator.
1004 * We splice in XMRInstanceSuperType or XMRSDTypeClObj for the first
1005 * arg as the function itself is static, followed by script-visible
1006 * arg types.
1007 */
1008 TokenArgDecl argDecl = declFunc.argDecl;
1009 int nArgs = argDecl.vars.Length;
1010 Type[] argTypes = new Type[nArgs+1];
1011 string[] argNames = new string[nArgs+1];
1012 if (IsSDTInstMethod ()) {
1013 argTypes[0] = typeof (XMRSDTypeClObj);
1014 argNames[0] = "$sdtthis";
1015 } else {
1016 argTypes[0] = xmrInstSuperType;
1017 argNames[0] = "$xmrthis";
1018 }
1019 for (int i = 0; i < nArgs; i ++) {
1020 argTypes[i+1] = argDecl.vars[i].type.ToSysType ();
1021 argNames[i+1] = argDecl.vars[i].name.val;
1022 }
1023
1024 /*
1025 * Set up entrypoint.
1026 */
1027 string objCodeName = declFunc.GetObjCodeName ();
1028 declFunc.ilGen = new ScriptObjWriter (tokenScript,
1029 objCodeName,
1030 declFunc.retType.ToSysType (),
1031 argTypes,
1032 argNames,
1033 objFileWriter);
1034
1035 /*
1036 * This says how to generate a call to the function and to get a delegate.
1037 */
1038 declFunc.location = new CompValuGlobalMeth (declFunc);
1039
1040 curDeclFunc = null;
1041 }
1042
1043 /**
1044 * @brief generate code for an arbitrary script-defined function.
1045 * @param name = name of the function
1046 * @param argDecl = argument declarations
1047 * @param body = function's code body
1048 */
1049 private void GenerateMethodBody (TokenDeclVar declFunc)
1050 {
1051 /*
1052 * Set up code generator for the function's contents.
1053 */
1054 _ilGen = declFunc.ilGen;
1055 StartFunctionBody (declFunc);
1056
1057 /*
1058 * Create a temp to hold XMRInstanceSuperType version of arg 0.
1059 * For most functions, arg 0 is already XMRInstanceSuperType.
1060 * But for script-defined class instance methods, arg 0 holds
1061 * the XMRSDTypeClObj pointer and so we read the XMRInstAbstract
1062 * pointer from its XMRSDTypeClObj.xmrInst field then cast it to
1063 * XMRInstanceSuperType.
1064 */
1065 if (IsSDTInstMethod ()) {
1066 instancePointer = ilGen.DeclareLocal (xmrInstSuperType, "__xmrinst");
1067 ilGen.Emit (declFunc, OpCodes.Ldarg_0);
1068 ilGen.Emit (declFunc, OpCodes.Ldfld, sdtXMRInstFieldInfo);
1069 ilGen.Emit (declFunc, OpCodes.Castclass, xmrInstSuperType);
1070 ilGen.Emit (declFunc, OpCodes.Stloc, instancePointer);
1071 }
1072
1073 /*
1074 * Define location of all script-level arguments so script body can access them.
1075 * The argument indices need to have +1 added to them because XMRInstance or
1076 * XMRSDTypeClObj is spliced in at arg 0.
1077 */
1078 TokenArgDecl argDecl = declFunc.argDecl;
1079 int nArgs = argDecl.vars.Length;
1080 for (int i = 0; i < nArgs; i ++) {
1081 TokenDeclVar argVar = argDecl.vars[i];
1082 argVar.location = new CompValuArg (argVar.type, i + 1);
1083 }
1084
1085 /*
1086 * Output code for the statements and clean up.
1087 */
1088 GenerateFuncBody ();
1089 }
1090
1091 private void StartFunctionBody (TokenDeclVar declFunc)
1092 {
1093 /*
1094 * Start current function being processed.
1095 * Set 'mightGetHere' as the code at the top is always executed.
1096 */
1097 instancePointer = null;
1098 mightGetHere = true;
1099 curBreakTarg = null;
1100 curContTarg = null;
1101 curDeclFunc = declFunc;
1102
1103 /*
1104 * Start generating code.
1105 */
1106 ((ScriptObjWriter)ilGen).BegMethod ();
1107 }
1108
1109 /**
1110 * @brief Define function for a script-defined type's <typename>.$new(<argsig>) method.
1111 * See GenerateStmtNewobj() for more info.
1112 */
1113 private TokenDeclVar DefineNewobjFunc (TokenDeclVar ctorDeclFunc)
1114 {
1115 /*
1116 * Set up 'static classname $new(params-same-as-ctor) { }'.
1117 */
1118 TokenDeclVar newobjDeclFunc = new TokenDeclVar (ctorDeclFunc, null, tokenScript);
1119 newobjDeclFunc.name = new TokenName (newobjDeclFunc, "$new");
1120 newobjDeclFunc.retType = ctorDeclFunc.sdtClass.MakeRefToken (newobjDeclFunc);
1121 newobjDeclFunc.argDecl = ctorDeclFunc.argDecl;
1122 newobjDeclFunc.sdtClass = ctorDeclFunc.sdtClass;
1123 newobjDeclFunc.sdtFlags = ScriptReduce.SDT_STATIC | ctorDeclFunc.sdtFlags;
1124
1125 /*
1126 * Declare local variable named '$objptr' in a frame just under
1127 * what the '$new(...)' function's arguments are declared in.
1128 */
1129 TokenDeclVar objptrVar = new TokenDeclVar (newobjDeclFunc, newobjDeclFunc, tokenScript);
1130 objptrVar.type = newobjDeclFunc.retType;
1131 objptrVar.name = new TokenName (newobjDeclFunc, "$objptr");
1132 VarDict newFrame = new VarDict (false);
1133 newFrame.outerVarDict = ctorDeclFunc.argDecl.varDict;
1134 newFrame.AddEntry (objptrVar);
1135
1136 /*
1137 * Set up '$objptr.$ctor'
1138 */
1139 TokenLValName objptrLValName = new TokenLValName (objptrVar.name, newFrame);
1140 // ref a var by giving its name
1141 TokenLValIField objptrDotCtor = new TokenLValIField (newobjDeclFunc); // an instance member reference
1142 objptrDotCtor.baseRVal = objptrLValName; // '$objptr'
1143 objptrDotCtor.fieldName = ctorDeclFunc.name; // '.' '$ctor'
1144
1145 /*
1146 * Set up '$objptr.$ctor(arglist)' call for use in the '$new(...)' body.
1147 * Copy the arglist from the constructor declaration so triviality
1148 * processing will pick the correct overloaded constructor.
1149 */
1150 TokenRValCall callCtorRVal = new TokenRValCall (newobjDeclFunc); // doing a call of some sort
1151 callCtorRVal.meth = objptrDotCtor; // calling $objptr.$ctor()
1152 TokenDeclVar[] argList = newobjDeclFunc.argDecl.vars; // get args $new() was declared with
1153 callCtorRVal.nArgs = argList.Length; // ...that is nArgs we are passing to $objptr.$ctor()
1154 for (int i = argList.Length; -- i >= 0;) {
1155 TokenDeclVar arg = argList[i]; // find out about one of the args
1156 TokenLValName argLValName = new TokenLValName (arg.name, ctorDeclFunc.argDecl.varDict);
1157 // pass arg of that name to $objptr.$ctor()
1158 argLValName.nextToken = callCtorRVal.args; // link to list of args passed to $objptr.$ctor()
1159 callCtorRVal.args = argLValName;
1160 }
1161
1162 /*
1163 * Set up a funky call to the constructor for the code body.
1164 * This will let code generator know there is some craziness.
1165 * See GenerateStmtNewobj().
1166 *
1167 * This is in essence:
1168 * {
1169 * classname $objptr = newobj (classname);
1170 * $objptr.$ctor (...);
1171 * return $objptr;
1172 * }
1173 */
1174 TokenStmtNewobj newobjStmtBody = new TokenStmtNewobj (ctorDeclFunc);
1175 newobjStmtBody.objptrVar = objptrVar;
1176 newobjStmtBody.rValCall = callCtorRVal;
1177 TokenStmtBlock newobjBody = new TokenStmtBlock (ctorDeclFunc);
1178 newobjBody.statements = newobjStmtBody;
1179
1180 /*
1181 * Link that code as the body of the function.
1182 */
1183 newobjDeclFunc.body = newobjBody;
1184
1185 /*
1186 * Say the function calls '$objptr.$ctor(arglist)' so we will inherit ctor's triviality.
1187 */
1188 newobjDeclFunc.unknownTrivialityCalls.AddLast (callCtorRVal);
1189 return newobjDeclFunc;
1190 }
1191
1192 private class TokenStmtNewobj : TokenStmt {
1193 public TokenDeclVar objptrVar;
1194 public TokenRValCall rValCall;
1195 public TokenStmtNewobj (Token original) : base (original) { }
1196 }
1197
1198 /**
1199 * @brief Output function body (either event handler or script-defined method).
1200 */
1201 private void GenerateFuncBody ()
1202 {
1203 /*
1204 * We want to know if the function's code is trivial, ie,
1205 * if it doesn't have anything that might be an infinite
1206 * loop and that is doesn't call anything that might have
1207 * an infinite loop. If it is, we don't need any CheckRun()
1208 * stuff or any of the frame save/restore stuff.
1209 */
1210 bool isTrivial = curDeclFunc.IsFuncTrivial (this);
1211
1212 /*
1213 * Clear list of all call labels.
1214 * A call label is inserted just before every call that can possibly
1215 * call CheckRun(), including any direct calls to CheckRun().
1216 * Then, when restoring stack, we can just switch to this label to
1217 * resume at the correct spot.
1218 */
1219 actCallLabels.Clear ();
1220 allCallLabels.Clear ();
1221 openCallLabel = null;
1222
1223 /*
1224 * Alloc stack space for local vars.
1225 */
1226 AllocLocalVarStackSpace ();
1227
1228 /*
1229 * Any return statements inside function body jump to this label
1230 * after putting return value in __retval.
1231 */
1232 retLabel = ilGen.DefineLabel ("__retlbl");
1233 retValue = null;
1234 if (!(curDeclFunc.retType is TokenTypeVoid)) {
1235 retValue = ilGen.DeclareLocal (curDeclFunc.retType.ToSysType (), "__retval");
1236 }
1237
1238 /*
1239 * Output:
1240 * int __mainCallNo = -1;
1241 * try {
1242 * if (instance.callMode != CallMode_NORMAL) goto __cmRestore;
1243 */
1244 actCallNo = null;
1245 ScriptMyLabel cmRestore = null;
1246 if (!isTrivial) {
1247 actCallNo = ilGen.DeclareLocal (typeof (int), "__mainCallNo");
1248 SetCallNo (curDeclFunc, actCallNo, -1);
1249 cmRestore = ilGen.DefineLabel ("__cmRestore");
1250 ilGen.BeginExceptionBlock ();
1251 PushXMRInst ();
1252 ilGen.Emit (curDeclFunc, OpCodes.Ldfld, ScriptCodeGen.callModeFieldInfo);
1253 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_NORMAL);
1254 ilGen.Emit (curDeclFunc, OpCodes.Bne_Un, cmRestore);
1255 }
1256
1257 /*
1258 * Splice in the code optimizer for the body of the function.
1259 */
1260 ScriptCollector collector = new ScriptCollector ((ScriptObjWriter)ilGen);
1261 _ilGen = collector;
1262
1263 /*
1264 * If this is the default state_entry() handler, output code to set all global
1265 * variables to their initial values. Note that every script must have a
1266 * default state_entry() handler, we provide one if the script doesn't explicitly
1267 * define one.
1268 */
1269 string methname = ilGen.methName;
1270 if (methname == "default state_entry") {
1271
1272 // if (!doGblInit) goto skipGblInit;
1273 ScriptMyLabel skipGblInitLabel = ilGen.DefineLabel ("__skipGblInit");
1274 PushXMRInst (); // instance
1275 ilGen.Emit (curDeclFunc, OpCodes.Ldfld, doGblInitFieldInfo); // instance.doGblInit
1276 ilGen.Emit (curDeclFunc, OpCodes.Brfalse, skipGblInitLabel);
1277
1278 // $globalvarinit();
1279 TokenDeclVar gviFunc = tokenScript.globalVarInit;
1280 if (gviFunc.body.statements != null) {
1281 gviFunc.location.CallPre (this, gviFunc);
1282 gviFunc.location.CallPost (this, gviFunc);
1283 }
1284
1285 // various $staticfieldinit();
1286 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
1287 if (sdType is TokenDeclSDTypeClass) {
1288 TokenDeclVar sfiFunc = ((TokenDeclSDTypeClass)sdType).staticFieldInit;
1289 if ((sfiFunc != null) && (sfiFunc.body.statements != null)) {
1290 sfiFunc.location.CallPre (this, sfiFunc);
1291 sfiFunc.location.CallPost (this, sfiFunc);
1292 }
1293 }
1294 }
1295
1296 // doGblInit = 0;
1297 PushXMRInst (); // instance
1298 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4_0);
1299 ilGen.Emit (curDeclFunc, OpCodes.Stfld, doGblInitFieldInfo); // instance.doGblInit
1300
1301 //skipGblInit:
1302 ilGen.MarkLabel (skipGblInitLabel);
1303 }
1304
1305 /*
1306 * If this is a script-defined type constructor, call the base constructor and call
1307 * this class's $instfieldinit() method to initialize instance fields.
1308 */
1309 if ((curDeclFunc.sdtClass != null) && curDeclFunc.funcNameSig.val.StartsWith ("$ctor(")) {
1310 if (curDeclFunc.baseCtorCall != null) {
1311 GenerateFromRValCall (curDeclFunc.baseCtorCall);
1312 }
1313 TokenDeclVar ifiFunc = ((TokenDeclSDTypeClass)curDeclFunc.sdtClass).instFieldInit;
1314 if (ifiFunc.body.statements != null) {
1315 CompValu thisCompValu = new CompValuArg (ifiFunc.sdtClass.MakeRefToken (ifiFunc), 0);
1316 CompValu ifiFuncLocn = new CompValuInstMember (ifiFunc, thisCompValu, true);
1317 ifiFuncLocn.CallPre (this, ifiFunc);
1318 ifiFuncLocn.CallPost (this, ifiFunc);
1319 }
1320 }
1321
1322 /*
1323 * See if time to suspend in case they are doing a loop with recursion.
1324 */
1325 if (!isTrivial) EmitCallCheckRun (curDeclFunc, true);
1326
1327 /*
1328 * Output code body.
1329 */
1330 GenerateStmtBlock (curDeclFunc.body);
1331
1332 /*
1333 * If code falls through to this point, means they are missing
1334 * a return statement. And that is legal only if the function
1335 * returns 'void'.
1336 */
1337 if (mightGetHere) {
1338 if (!(curDeclFunc.retType is TokenTypeVoid)) {
1339 ErrorMsg (curDeclFunc.body, "missing final return statement");
1340 }
1341 ilGen.Emit (curDeclFunc, OpCodes.Leave, retLabel);
1342 }
1343
1344 /*
1345 * End of the code to be optimized.
1346 * Do optimizations then write it all out to object file.
1347 * After this, all code gets written directly to object file.
1348 * Optimization must be completed before we scan the allCallLabels
1349 * list below to look for active locals and temps.
1350 */
1351 collector.Optimize ();
1352 _ilGen = collector.WriteOutAll ();
1353 collector = null;
1354
1355 /*
1356 * Output code to restore stack frame from stream.
1357 * It jumps back to the call labels within the function body.
1358 */
1359 List<ScriptMyLocal> activeTemps = null;
1360 if (!isTrivial) {
1361
1362 /*
1363 * Build list of locals and temps active at all the call labels.
1364 */
1365 activeTemps = new List<ScriptMyLocal> ();
1366 foreach (CallLabel cl in allCallLabels) {
1367 foreach (ScriptMyLocal lcl in cl.callLabel.whereAmI.localsReadBeforeWritten) {
1368 if (!activeTemps.Contains (lcl)) {
1369 activeTemps.Add (lcl);
1370 }
1371 }
1372 }
1373
1374 /*
1375 * Output code to restore the args, locals and temps then jump to
1376 * the call label that we were interrupted at.
1377 */
1378 ilGen.MarkLabel (cmRestore);
1379 GenerateFrameRestoreCode (activeTemps);
1380 }
1381
1382 /*
1383 * Output epilog that saves stack frame state if CallMode_SAVE.
1384 *
1385 * finally {
1386 * if (instance.callMode != CallMode_SAVE) goto __endFin;
1387 * GenerateFrameCaptureCode();
1388 * __endFin:
1389 * }
1390 */
1391 ScriptMyLabel endFin = null;
1392 if (!isTrivial) {
1393 ilGen.BeginFinallyBlock ();
1394 endFin = ilGen.DefineLabel ("__endFin");
1395 PushXMRInst ();
1396 ilGen.Emit (curDeclFunc, OpCodes.Ldfld, callModeFieldInfo);
1397 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_SAVE);
1398 ilGen.Emit (curDeclFunc, OpCodes.Bne_Un, endFin);
1399 GenerateFrameCaptureCode (activeTemps);
1400 ilGen.MarkLabel (endFin);
1401 ilGen.Emit (curDeclFunc, OpCodes.Endfinally);
1402 ilGen.EndExceptionBlock ();
1403 }
1404
1405 /*
1406 * Output the 'real' return opcode.
1407 */
1408 ilGen.MarkLabel (retLabel);
1409 if (!(curDeclFunc.retType is TokenTypeVoid)) {
1410 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, retValue);
1411 }
1412 ilGen.Emit (curDeclFunc, OpCodes.Ret);
1413 retLabel = null;
1414 retValue = null;
1415
1416 /*
1417 * No more instructions for this method.
1418 */
1419 ((ScriptObjWriter)ilGen).EndMethod ();
1420 _ilGen = null;
1421
1422 /*
1423 * Not generating function code any more.
1424 */
1425 curBreakTarg = null;
1426 curContTarg = null;
1427 curDeclFunc = null;
1428 }
1429
1430 /**
1431 * @brief Allocate stack space for all local variables, regardless of
1432 * which { } statement block they are actually defined in.
1433 */
1434 private void AllocLocalVarStackSpace ()
1435 {
1436 foreach (TokenDeclVar localVar in curDeclFunc.localVars) {
1437
1438 /*
1439 * Skip all 'constant' vars as they were handled by the reducer.
1440 */
1441 if (localVar.constant) continue;
1442
1443 /*
1444 * Get a stack location for the local variable.
1445 */
1446 localVar.location = new CompValuLocalVar (localVar.type, localVar.name.val, this);
1447 }
1448 }
1449
1450 /**
1451 * @brief Generate code to write all arguments and locals to the capture stack frame.
1452 * This includes temp variables.
1453 * We only need to save what is active at the point of callLabels through because
1454 * those are the only points we will jump to on restore. This saves us from saving
1455 * all the little temp vars we create.
1456 * @param activeTemps = list of locals and temps that we care about, ie, which
1457 * ones get restored by GenerateFrameRestoreCode().
1458 */
1459 private void GenerateFrameCaptureCode (List<ScriptMyLocal> activeTemps)
1460 {
1461 /*
1462 * Compute total number of slots we need to save stuff.
1463 * Assume we need to save all call arguments.
1464 */
1465 int nSaves = curDeclFunc.argDecl.vars.Length + activeTemps.Count;
1466
1467 /*
1468 * Output code to allocate a stack frame object with an object array.
1469 * This also pushes the stack frame object on the instance.stackFrames list.
1470 * It returns a pointer to the object array it allocated.
1471 */
1472 PushXMRInst ();
1473 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName);
1474 GetCallNo (curDeclFunc, actCallNo);
1475 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, nSaves);
1476 ilGen.Emit (curDeclFunc, OpCodes.Call, captureStackFrameMethodInfo);
1477
1478 if (DEBUG_STACKCAPRES) {
1479 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName + "*: capture mainCallNo=");
1480 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1481 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, actCallNo);
1482 ilGen.Emit (curDeclFunc, OpCodes.Box, typeof (int));
1483 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1484 }
1485
1486 /*
1487 * Copy arg values to object array, boxing as needed.
1488 */
1489 int i = 0;
1490 foreach (TokenDeclVar argVar in curDeclFunc.argDecl.varDict) {
1491 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1492 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i);
1493 argVar.location.PushVal (this, argVar.name, tokenTypeObj);
1494 if (DEBUG_STACKCAPRES) {
1495 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n arg:" + argVar.name.val + "=");
1496 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1497 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1498 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1499 }
1500 ilGen.Emit (curDeclFunc, OpCodes.Stelem_Ref);
1501 i ++;
1502 }
1503
1504 /*
1505 * Copy local and temp values to object array, boxing as needed.
1506 */
1507 foreach (ScriptMyLocal lcl in activeTemps) {
1508 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1509 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i ++);
1510 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, lcl);
1511 Type t = lcl.type;
1512 if (t == typeof (HeapTrackerList)) {
1513 ilGen.Emit (curDeclFunc, OpCodes.Call, heapTrackerListPush);
1514 t = typeof (LSL_List);
1515 }
1516 if (t == typeof (HeapTrackerObject)) {
1517 ilGen.Emit (curDeclFunc, OpCodes.Call, heapTrackerObjectPush);
1518 t = typeof (object);
1519 }
1520 if (t == typeof (HeapTrackerString)) {
1521 ilGen.Emit (curDeclFunc, OpCodes.Call, heapTrackerStringPush);
1522 t = typeof (string);
1523 }
1524 if (t.IsValueType) {
1525 ilGen.Emit (curDeclFunc, OpCodes.Box, t);
1526 }
1527 if (DEBUG_STACKCAPRES) {
1528 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n lcl:" + lcl.name + "=");
1529 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1530 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1531 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1532 }
1533 ilGen.Emit (curDeclFunc, OpCodes.Stelem_Ref);
1534 }
1535 if (DEBUG_STACKCAPRES) {
1536 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n");
1537 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1538 }
1539
1540 ilGen.Emit (curDeclFunc, OpCodes.Pop);
1541 }
1542
1543 /**
1544 * @brief Generate code to restore all arguments and locals from the restore stack frame.
1545 * This includes temp variables.
1546 */
1547 private void GenerateFrameRestoreCode (List<ScriptMyLocal> activeTemps)
1548 {
1549 ScriptMyLocal objArray = ilGen.DeclareLocal (typeof (object[]), "__restObjArray");
1550
1551 /*
1552 * Output code to pop stack frame from instance.stackFrames.
1553 * It returns a pointer to the object array that contains values to be restored.
1554 */
1555 PushXMRInst ();
1556 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName);
1557 ilGen.Emit (curDeclFunc, OpCodes.Ldloca, actCallNo); // __mainCallNo
1558 ilGen.Emit (curDeclFunc, OpCodes.Call, restoreStackFrameMethodInfo);
1559 ilGen.Emit (curDeclFunc, OpCodes.Stloc, objArray);
1560 if (DEBUG_STACKCAPRES) {
1561 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName + "*: restore mainCallNo=");
1562 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1563 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, actCallNo);
1564 ilGen.Emit (curDeclFunc, OpCodes.Box, typeof (int));
1565 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1566 }
1567
1568 /*
1569 * Restore argument values from object array, unboxing as needed.
1570 * Although the caller has restored them to what it called us with, it's possible that this
1571 * function has modified them since, so we need to do our own restore.
1572 */
1573 int i = 0;
1574 foreach (TokenDeclVar argVar in curDeclFunc.argDecl.varDict) {
1575 CompValu argLoc = argVar.location;
1576 argLoc.PopPre (this, argVar.name);
1577 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, objArray);
1578 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i);
1579 ilGen.Emit (curDeclFunc, OpCodes.Ldelem_Ref);
1580 if (DEBUG_STACKCAPRES) {
1581 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n arg:" + argVar.name.val + "=");
1582 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1583 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1584 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1585 }
1586 TypeCast.CastTopOfStack (this, argVar.name, tokenTypeObj, argLoc.type, true);
1587 argLoc.PopPost (this, argVar.name);
1588 i ++;
1589 }
1590
1591 /*
1592 * Restore local and temp values from object array, unboxing as needed.
1593 */
1594 foreach (ScriptMyLocal lcl in activeTemps) {
1595 Type t = lcl.type;
1596 Type u = t;
1597 if (t == typeof (HeapTrackerList)) u = typeof (LSL_List);
1598 if (t == typeof (HeapTrackerObject)) u = typeof (object);
1599 if (t == typeof (HeapTrackerString)) u = typeof (string);
1600 if (u != t) {
1601 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, lcl);
1602 }
1603 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, objArray);
1604 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i ++);
1605 ilGen.Emit (curDeclFunc, OpCodes.Ldelem_Ref);
1606 if (DEBUG_STACKCAPRES) {
1607 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n lcl:" + lcl.name + "=");
1608 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1609 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1610 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1611 }
1612 if (u.IsValueType) {
1613 ilGen.Emit (curDeclFunc, OpCodes.Unbox_Any, u);
1614 } else if (u != typeof (object)) {
1615 ilGen.Emit (curDeclFunc, OpCodes.Castclass, u);
1616 }
1617 if (u != t) {
1618 ilGen.Emit (curDeclFunc, OpCodes.Call, t.GetMethod ("Pop", new Type[] { u }));
1619 } else {
1620 ilGen.Emit (curDeclFunc, OpCodes.Stloc, lcl);
1621 }
1622 }
1623 if (DEBUG_STACKCAPRES) {
1624 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n");
1625 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1626 }
1627
1628 OutputCallNoSwitchStmt ();
1629 }
1630
1631 /**
1632 * @brief Output a switch statement with a case for each possible
1633 * value of whatever callNo is currently active, either
1634 * __mainCallNo or one of the try/catch/finally's callNos.
1635 *
1636 * switch (callNo) {
1637 * case 0: goto __call_0;
1638 * case 1: goto __call_1;
1639 * ...
1640 * }
1641 * throw new ScriptBadCallNoException (callNo);
1642 */
1643 private void OutputCallNoSwitchStmt ()
1644 {
1645 ScriptMyLabel[] callLabels = new ScriptMyLabel[actCallLabels.Count];
1646 foreach (CallLabel cl in actCallLabels) {
1647 callLabels[cl.index] = cl.callLabel;
1648 }
1649 GetCallNo (curDeclFunc, actCallNo);
1650 ilGen.Emit (curDeclFunc, OpCodes.Switch, callLabels);
1651
1652 GetCallNo (curDeclFunc, actCallNo);
1653 ilGen.Emit (curDeclFunc, OpCodes.Newobj, scriptBadCallNoExceptionConstructorInfo);
1654 ilGen.Emit (curDeclFunc, OpCodes.Throw);
1655 }
1656
1657 /**
1658 * @brief There is one of these per call that can possibly call CheckRun(),
1659 * including direct calls to CheckRun().
1660 * They mark points that the stack capture/restore code will save & restore to.
1661 * All object-code level local vars active at the call label's point will
1662 * be saved & restored.
1663 *
1664 * callNo = 5;
1665 * __call_5:
1666 * push call arguments from temps
1667 * call SomethingThatCallsCheckRun()
1668 *
1669 * If SomethingThatCallsCheckRun() actually calls CheckRun(), our restore code
1670 * will restore our args, locals & temps, then jump to __call_5, which will then
1671 * call SomethingThatCallsCheckRun() again, which will restore its stuff likewise.
1672 * When eventually the actual CheckRun() call is restored, it will turn off restore
1673 * mode (by changing callMode from CallMode_RESTORE to CallMode_NORMAL) and return,
1674 * allowing the code to run normally from that point.
1675 */
1676 public class CallLabel {
1677 public int index; // sequential integer, starting at 0, within actCallLabels
1678 // - used for the switch statement
1679 public ScriptMyLabel callLabel; // the actual label token
1680
1681 public CallLabel (ScriptCodeGen scg, Token errorAt)
1682 {
1683 if (scg.openCallLabel != null) throw new Exception ("call label already open");
1684
1685 if (!scg.curDeclFunc.IsFuncTrivial (scg)) {
1686 this.index = scg.actCallLabels.Count;
1687 string name = "__call_" + index + "_" + scg.allCallLabels.Count;
1688
1689 /*
1690 * Make sure eval stack is empty because the frame capture/restore
1691 * code expects such (restore switch stmt has an empty stack).
1692 */
1693 int depth = ((ScriptCollector)scg.ilGen).stackDepth.Count;
1694 if (depth > 0) {
1695 // maybe need to call Trivialize()
1696 throw new Exception ("call label stack depth " + depth + " at " + errorAt.SrcLoc);
1697 }
1698
1699 /*
1700 * Eval stack is empty so the restore code can handle it.
1701 */
1702 this.index = scg.actCallLabels.Count;
1703 scg.actCallLabels.AddLast (this);
1704 scg.allCallLabels.AddLast (this);
1705 this.callLabel = scg.ilGen.DefineLabel (name);
1706 scg.SetCallNo (errorAt, scg.actCallNo, this.index);
1707 scg.ilGen.MarkLabel (this.callLabel);
1708 }
1709
1710 scg.openCallLabel = this;
1711 }
1712 };
1713
1714 /**
1715 * @brief generate code for an arbitrary statement.
1716 */
1717 private void GenerateStmt (TokenStmt stmt)
1718 {
1719 errorMessageToken = stmt;
1720 if (stmt is TokenDeclVar) { GenerateDeclVar ((TokenDeclVar)stmt); return; }
1721 if (stmt is TokenStmtBlock) { GenerateStmtBlock ((TokenStmtBlock)stmt); return; }
1722 if (stmt is TokenStmtBreak) { GenerateStmtBreak ((TokenStmtBreak)stmt); return; }
1723 if (stmt is TokenStmtCont) { GenerateStmtCont ((TokenStmtCont)stmt); return; }
1724 if (stmt is TokenStmtDo) { GenerateStmtDo ((TokenStmtDo)stmt); return; }
1725 if (stmt is TokenStmtFor) { GenerateStmtFor ((TokenStmtFor)stmt); return; }
1726 if (stmt is TokenStmtForEach) { GenerateStmtForEach ((TokenStmtForEach)stmt); return; }
1727 if (stmt is TokenStmtIf) { GenerateStmtIf ((TokenStmtIf)stmt); return; }
1728 if (stmt is TokenStmtJump) { GenerateStmtJump ((TokenStmtJump)stmt); return; }
1729 if (stmt is TokenStmtLabel) { GenerateStmtLabel ((TokenStmtLabel)stmt); return; }
1730 if (stmt is TokenStmtNewobj) { GenerateStmtNewobj ((TokenStmtNewobj)stmt); return; }
1731 if (stmt is TokenStmtNull) { return; }
1732 if (stmt is TokenStmtRet) { GenerateStmtRet ((TokenStmtRet)stmt); return; }
1733 if (stmt is TokenStmtRVal) { GenerateStmtRVal ((TokenStmtRVal)stmt); return; }
1734 if (stmt is TokenStmtState) { GenerateStmtState ((TokenStmtState)stmt); return; }
1735 if (stmt is TokenStmtSwitch) { GenerateStmtSwitch ((TokenStmtSwitch)stmt); return; }
1736 if (stmt is TokenStmtThrow) { GenerateStmtThrow ((TokenStmtThrow)stmt); return; }
1737 if (stmt is TokenStmtTry) { GenerateStmtTry ((TokenStmtTry)stmt); return; }
1738 if (stmt is TokenStmtVarIniDef) { GenerateStmtVarIniDef ((TokenStmtVarIniDef)stmt); return; }
1739 if (stmt is TokenStmtWhile) { GenerateStmtWhile ((TokenStmtWhile)stmt); return; }
1740 throw new Exception ("unknown TokenStmt type " + stmt.GetType ().ToString ());
1741 }
1742
1743 /**
1744 * @brief generate statement block (ie, with braces)
1745 */
1746 private void GenerateStmtBlock (TokenStmtBlock stmtBlock)
1747 {
1748 if (!mightGetHere) return;
1749
1750 /*
1751 * Push new current statement block pointer for anyone who cares.
1752 */
1753 TokenStmtBlock oldStmtBlock = curStmtBlock;
1754 curStmtBlock = stmtBlock;
1755
1756 /*
1757 * Output the statements that make up the block.
1758 */
1759 for (Token t = stmtBlock.statements; t != null; t = t.nextToken) {
1760 GenerateStmt ((TokenStmt)t);
1761 }
1762
1763 /*
1764 * Pop the current statement block.
1765 */
1766 curStmtBlock = oldStmtBlock;
1767 }
1768
1769 /**
1770 * @brief output code for a 'break' statement
1771 */
1772 private void GenerateStmtBreak (TokenStmtBreak breakStmt)
1773 {
1774 if (!mightGetHere) return;
1775
1776 /*
1777 * Make sure we are in a breakable situation.
1778 */
1779 if (curBreakTarg == null) {
1780 ErrorMsg (breakStmt, "not in a breakable situation");
1781 return;
1782 }
1783
1784 /*
1785 * Tell anyone who cares that the break target was actually used.
1786 */
1787 curBreakTarg.used = true;
1788
1789 /*
1790 * Output the instructions.
1791 */
1792 EmitJumpCode (curBreakTarg.label, curBreakTarg.block, breakStmt);
1793 }
1794
1795 /**
1796 * @brief output code for a 'continue' statement
1797 */
1798 private void GenerateStmtCont (TokenStmtCont contStmt)
1799 {
1800 if (!mightGetHere) return;
1801
1802 /*
1803 * Make sure we are in a contable situation.
1804 */
1805 if (curContTarg == null) {
1806 ErrorMsg (contStmt, "not in a continueable situation");
1807 return;
1808 }
1809
1810 /*
1811 * Tell anyone who cares that the continue target was actually used.
1812 */
1813 curContTarg.used = true;
1814
1815 /*
1816 * Output the instructions.
1817 */
1818 EmitJumpCode (curContTarg.label, curContTarg.block, contStmt);
1819 }
1820
1821 /**
1822 * @brief output code for a 'do' statement
1823 */
1824 private void GenerateStmtDo (TokenStmtDo doStmt)
1825 {
1826 if (!mightGetHere) return;
1827
1828 BreakContTarg oldBreakTarg = curBreakTarg;
1829 BreakContTarg oldContTarg = curContTarg;
1830 ScriptMyLabel loopLabel = ilGen.DefineLabel ("doloop_" + doStmt.Unique);
1831
1832 curBreakTarg = new BreakContTarg (this, "dobreak_" + doStmt.Unique);
1833 curContTarg = new BreakContTarg (this, "docont_" + doStmt.Unique);
1834
1835 ilGen.MarkLabel (loopLabel);
1836 GenerateStmt (doStmt.bodyStmt);
1837 if (curContTarg.used) {
1838 ilGen.MarkLabel (curContTarg.label);
1839 mightGetHere = true;
1840 }
1841
1842 if (mightGetHere) {
1843 EmitCallCheckRun (doStmt, false);
1844 CompValu testRVal = GenerateFromRVal (doStmt.testRVal);
1845 if (IsConstBoolExprTrue (testRVal)) {
1846
1847 /*
1848 * Unconditional looping, unconditional branch and
1849 * say we never fall through to next statement.
1850 */
1851 ilGen.Emit (doStmt, OpCodes.Br, loopLabel);
1852 mightGetHere = false;
1853 } else {
1854
1855 /*
1856 * Conditional looping, test and brach back to top of loop.
1857 */
1858 testRVal.PushVal (this, doStmt.testRVal, tokenTypeBool);
1859 ilGen.Emit (doStmt, OpCodes.Brtrue, loopLabel);
1860 }
1861 }
1862
1863 /*
1864 * If 'break' statement was used, output target label.
1865 * And assume that since a 'break' statement was used, it's possible for the code to get here.
1866 */
1867 if (curBreakTarg.used) {
1868 ilGen.MarkLabel (curBreakTarg.label);
1869 mightGetHere = true;
1870 }
1871
1872 curBreakTarg = oldBreakTarg;
1873 curContTarg = oldContTarg;
1874 }
1875
1876 /**
1877 * @brief output code for a 'for' statement
1878 */
1879 private void GenerateStmtFor (TokenStmtFor forStmt)
1880 {
1881 if (!mightGetHere) return;
1882
1883 BreakContTarg oldBreakTarg = curBreakTarg;
1884 BreakContTarg oldContTarg = curContTarg;
1885 ScriptMyLabel loopLabel = ilGen.DefineLabel ("forloop_" + forStmt.Unique);
1886
1887 curBreakTarg = new BreakContTarg (this, "forbreak_" + forStmt.Unique);
1888 curContTarg = new BreakContTarg (this, "forcont_" + forStmt.Unique);
1889
1890 if (forStmt.initStmt != null) {
1891 GenerateStmt (forStmt.initStmt);
1892 }
1893 ilGen.MarkLabel (loopLabel);
1894
1895 /*
1896 * See if we have a test expression that is other than a constant TRUE.
1897 * If so, test it and conditionally branch to end if false.
1898 */
1899 if (forStmt.testRVal != null) {
1900 CompValu testRVal = GenerateFromRVal (forStmt.testRVal);
1901 if (!IsConstBoolExprTrue (testRVal)) {
1902 testRVal.PushVal (this, forStmt.testRVal, tokenTypeBool);
1903 ilGen.Emit (forStmt, OpCodes.Brfalse, curBreakTarg.label);
1904 curBreakTarg.used = true;
1905 }
1906 }
1907
1908 /*
1909 * Output loop body.
1910 */
1911 GenerateStmt (forStmt.bodyStmt);
1912
1913 /*
1914 * Here's where a 'continue' statement jumps to.
1915 */
1916 if (curContTarg.used) {
1917 ilGen.MarkLabel (curContTarg.label);
1918 mightGetHere = true;
1919 }
1920
1921 if (mightGetHere) {
1922
1923 /*
1924 * After checking for excessive CPU time, output increment statement, if any.
1925 */
1926 EmitCallCheckRun (forStmt, false);
1927 if (forStmt.incrRVal != null) {
1928 GenerateFromRVal (forStmt.incrRVal);
1929 }
1930
1931 /*
1932 * Unconditional branch back to beginning of loop.
1933 */
1934 ilGen.Emit (forStmt, OpCodes.Br, loopLabel);
1935 }
1936
1937 /*
1938 * If test needs label, output label for it to jump to.
1939 * Otherwise, clear mightGetHere as we know loop never
1940 * falls out the bottom.
1941 */
1942 mightGetHere = curBreakTarg.used;
1943 if (mightGetHere) {
1944 ilGen.MarkLabel (curBreakTarg.label);
1945 }
1946
1947 curBreakTarg = oldBreakTarg;
1948 curContTarg = oldContTarg;
1949 }
1950
1951 private void GenerateStmtForEach (TokenStmtForEach forEachStmt)
1952 {
1953 if (!mightGetHere) return;
1954
1955 BreakContTarg oldBreakTarg = curBreakTarg;
1956 BreakContTarg oldContTarg = curContTarg;
1957 CompValu keyLVal = null;
1958 CompValu valLVal = null;
1959 CompValu arrayRVal = GenerateFromRVal (forEachStmt.arrayRVal);
1960
1961 if (forEachStmt.keyLVal != null) {
1962 keyLVal = GenerateFromLVal (forEachStmt.keyLVal);
1963 if (!(keyLVal.type is TokenTypeObject)) {
1964 ErrorMsg (forEachStmt.arrayRVal, "must be object");
1965 }
1966 }
1967 if (forEachStmt.valLVal != null) {
1968 valLVal = GenerateFromLVal (forEachStmt.valLVal);
1969 if (!(valLVal.type is TokenTypeObject)) {
1970 ErrorMsg (forEachStmt.arrayRVal, "must be object");
1971 }
1972 }
1973 if (!(arrayRVal.type is TokenTypeArray)) {
1974 ErrorMsg (forEachStmt.arrayRVal, "must be an array");
1975 }
1976
1977 curBreakTarg = new BreakContTarg (this, "foreachbreak_" + forEachStmt.Unique);
1978 curContTarg = new BreakContTarg (this, "foreachcont_" + forEachStmt.Unique);
1979
1980 CompValuTemp indexVar = new CompValuTemp (new TokenTypeInt (forEachStmt), this);
1981 ScriptMyLabel loopLabel = ilGen.DefineLabel ("foreachloop_" + forEachStmt.Unique);
1982
1983 // indexVar = 0
1984 ilGen.Emit (forEachStmt, OpCodes.Ldc_I4_0);
1985 indexVar.Pop (this, forEachStmt);
1986
1987 ilGen.MarkLabel (loopLabel);
1988
1989 // key = array.__pub_index (indexVar);
1990 // if (key == null) goto curBreakTarg;
1991 if (keyLVal != null) {
1992 keyLVal.PopPre (this, forEachStmt.keyLVal);
1993 arrayRVal.PushVal (this, forEachStmt.arrayRVal);
1994 indexVar.PushVal (this, forEachStmt);
1995 ilGen.Emit (forEachStmt, OpCodes.Call, xmrArrPubIndexMethod);
1996 keyLVal.PopPost (this, forEachStmt.keyLVal);
1997 keyLVal.PushVal (this, forEachStmt.keyLVal);
1998 ilGen.Emit (forEachStmt, OpCodes.Brfalse, curBreakTarg.label);
1999 curBreakTarg.used = true;
2000 }
2001
2002 // val = array._pub_value (indexVar);
2003 // if (val == null) goto curBreakTarg;
2004 if (valLVal != null) {
2005 valLVal.PopPre (this, forEachStmt.valLVal);
2006 arrayRVal.PushVal (this, forEachStmt.arrayRVal);
2007 indexVar.PushVal (this, forEachStmt);
2008 ilGen.Emit (forEachStmt, OpCodes.Call, xmrArrPubValueMethod);
2009 valLVal.PopPost (this, forEachStmt.valLVal);
2010 if (keyLVal == null) {
2011 valLVal.PushVal (this, forEachStmt.valLVal);
2012 ilGen.Emit (forEachStmt, OpCodes.Brfalse, curBreakTarg.label);
2013 curBreakTarg.used = true;
2014 }
2015 }
2016
2017 // indexVar ++;
2018 indexVar.PushVal (this, forEachStmt);
2019 ilGen.Emit (forEachStmt, OpCodes.Ldc_I4_1);
2020 ilGen.Emit (forEachStmt, OpCodes.Add);
2021 indexVar.Pop (this, forEachStmt);
2022
2023 // body statement
2024 GenerateStmt (forEachStmt.bodyStmt);
2025
2026 // continue label
2027 if (curContTarg.used) {
2028 ilGen.MarkLabel (curContTarg.label);
2029 mightGetHere = true;
2030 }
2031
2032 // call CheckRun()
2033 if (mightGetHere) {
2034 EmitCallCheckRun (forEachStmt, false);
2035 ilGen.Emit (forEachStmt, OpCodes.Br, loopLabel);
2036 }
2037
2038 // break label
2039 ilGen.MarkLabel (curBreakTarg.label);
2040 mightGetHere = true;
2041
2042 curBreakTarg = oldBreakTarg;
2043 curContTarg = oldContTarg;
2044 }
2045
2046 /**
2047 * @brief output code for an 'if' statement
2048 * Braces are necessary because what may be one statement for trueStmt or elseStmt in
2049 * the script may translate to more than one statement in the resultant C# code.
2050 */
2051 private void GenerateStmtIf (TokenStmtIf ifStmt)
2052 {
2053 if (!mightGetHere) return;
2054
2055 bool constVal;
2056
2057 /*
2058 * Test condition and see if constant test expression.
2059 */
2060 CompValu testRVal = GenerateFromRVal (ifStmt.testRVal);
2061 if (IsConstBoolExpr (testRVal, out constVal)) {
2062
2063 /*
2064 * Constant, output just either the true or else part.
2065 */
2066 if (constVal) {
2067 GenerateStmt (ifStmt.trueStmt);
2068 } else if (ifStmt.elseStmt != null) {
2069 GenerateStmt (ifStmt.elseStmt);
2070 }
2071 } else if (ifStmt.elseStmt == null) {
2072
2073 /*
2074 * This is an 'if' statement without an 'else' clause.
2075 */
2076 testRVal.PushVal (this, ifStmt.testRVal, tokenTypeBool);
2077 ScriptMyLabel doneLabel = ilGen.DefineLabel ("ifdone_" + ifStmt.Unique);
2078 ilGen.Emit (ifStmt, OpCodes.Brfalse, doneLabel); // brfalse doneLabel
2079 GenerateStmt (ifStmt.trueStmt); // generate true body code
2080 ilGen.MarkLabel (doneLabel);
2081 mightGetHere = true; // there's always a possibility of getting here
2082 } else {
2083
2084 /*
2085 * This is an 'if' statement with an 'else' clause.
2086 */
2087 testRVal.PushVal (this, ifStmt.testRVal, tokenTypeBool);
2088 ScriptMyLabel elseLabel = ilGen.DefineLabel ("ifelse_" + ifStmt.Unique);
2089 ilGen.Emit (ifStmt, OpCodes.Brfalse, elseLabel); // brfalse elseLabel
2090 GenerateStmt (ifStmt.trueStmt); // generate true body code
2091 bool trueMightGetHere = mightGetHere; // save whether or not true falls through
2092 ScriptMyLabel doneLabel = ilGen.DefineLabel ("ifdone_" + ifStmt.Unique);
2093 ilGen.Emit (ifStmt, OpCodes.Br, doneLabel); // branch to done
2094 ilGen.MarkLabel (elseLabel); // beginning of else code
2095 mightGetHere = true; // the top of the else might be executed
2096 GenerateStmt (ifStmt.elseStmt); // output else code
2097 ilGen.MarkLabel (doneLabel); // where end of true clause code branches to
2098 mightGetHere |= trueMightGetHere; // gets this far if either true or else falls through
2099 }
2100 }
2101
2102 /**
2103 * @brief output code for a 'jump' statement
2104 */
2105 private void GenerateStmtJump (TokenStmtJump jumpStmt)
2106 {
2107 if (!mightGetHere) return;
2108
2109 /*
2110 * Make sure the target label is defined somewhere in the function.
2111 */
2112 TokenStmtLabel stmtLabel;
2113 if (!curDeclFunc.labels.TryGetValue (jumpStmt.label.val, out stmtLabel)) {
2114 ErrorMsg (jumpStmt, "undefined label " + jumpStmt.label.val);
2115 return;
2116 }
2117 if (!stmtLabel.labelTagged) {
2118 stmtLabel.labelStruct = ilGen.DefineLabel ("jump_" + stmtLabel.name.val);
2119 stmtLabel.labelTagged = true;
2120 }
2121
2122 /*
2123 * Emit instructions to do the jump.
2124 */
2125 EmitJumpCode (stmtLabel.labelStruct, stmtLabel.block, jumpStmt);
2126 }
2127
2128 /**
2129 * @brief Emit code to jump to a label
2130 * @param target = label being jumped to
2131 * @param targetsBlock = { ... } the label is defined in
2132 */
2133 private void EmitJumpCode (ScriptMyLabel target, TokenStmtBlock targetsBlock, Token errorAt)
2134 {
2135 /*
2136 * Jumps never fall through.
2137 */
2138 mightGetHere = false;
2139
2140 /*
2141 * Find which block the target label is in. Must be in this or an outer block,
2142 * no laterals allowed. And if we exit a try/catch block, use Leave instead of Br.
2143 *
2144 * jump lateral;
2145 * {
2146 * @lateral;
2147 * }
2148 */
2149 bool useLeave = false;
2150 TokenStmtBlock stmtBlock;
2151 Stack<TokenStmtTry> finallyBlocksCalled = new Stack<TokenStmtTry> ();
2152 for (stmtBlock = curStmtBlock; stmtBlock != targetsBlock; stmtBlock = stmtBlock.outerStmtBlock) {
2153 if (stmtBlock == null) {
2154 ErrorMsg (errorAt, "no lateral jumps allowed");
2155 return;
2156 }
2157 if (stmtBlock.isFinally) {
2158 ErrorMsg (errorAt, "cannot jump out of finally");
2159 return;
2160 }
2161 if (stmtBlock.isTry || stmtBlock.isCatch) useLeave = true;
2162 if ((stmtBlock.tryStmt != null) && (stmtBlock.tryStmt.finallyStmt != null)) {
2163 finallyBlocksCalled.Push (stmtBlock.tryStmt);
2164 }
2165 }
2166
2167 /*
2168 * If popping through more than one finally block, we have to break it down for the stack
2169 * capture and restore code, one finally block at a time.
2170 *
2171 * try {
2172 * try {
2173 * try {
2174 * jump exit;
2175 * } finally {
2176 * llOwnerSay ("exiting inner");
2177 * }
2178 * } finally {
2179 * llOwnerSay ("exiting middle");
2180 * }
2181 * } finally {
2182 * llOwnerSay ("exiting outer");
2183 * }
2184 * @exit;
2185 *
2186 * try {
2187 * try {
2188 * try {
2189 * jump intr2_exit; <<< gets its own tryNo call label so inner try knows where to restore to
2190 * } finally {
2191 * llOwnerSay ("exiting inner");
2192 * }
2193 * jump outtry2;
2194 * @intr2_exit; jump intr1_exit; <<< gets its own tryNo call label so middle try knows where to restore to
2195 * @outtry2;
2196 * } finally {
2197 * llOwnerSay ("exiting middle");
2198 * }
2199 * jump outtry1;
2200 * @intr1_exit: jump exit; <<< gets its own tryNo call label so outer try knows where to restore to
2201 * @outtry1;
2202 * } finally {
2203 * llOwnerSay ("exiting outer");
2204 * }
2205 * @exit;
2206 */
2207 int level = 0;
2208 while (finallyBlocksCalled.Count > 1) {
2209 TokenStmtTry finallyBlock = finallyBlocksCalled.Pop ();
2210 string intername = "intr" + (++ level) + "_" + target.name;
2211 IntermediateLeave iLeave;
2212 if (!finallyBlock.iLeaves.TryGetValue (intername, out iLeave)) {
2213 iLeave = new IntermediateLeave ();
2214 iLeave.jumpIntoLabel = ilGen.DefineLabel (intername);
2215 iLeave.jumpAwayLabel = target;
2216 finallyBlock.iLeaves.Add (intername, iLeave);
2217 }
2218 target = iLeave.jumpIntoLabel;
2219 }
2220
2221 /*
2222 * Finally output the branch/leave opcode.
2223 * If using Leave, prefix with a call label in case the corresponding finally block
2224 * calls CheckRun() and that CheckRun() captures the stack, it will have a point to
2225 * restore to that will properly jump back into the finally block.
2226 */
2227 if (useLeave) {
2228 new CallLabel (this, errorAt);
2229 ilGen.Emit (errorAt, OpCodes.Leave, target);
2230 openCallLabel = null;
2231 } else {
2232 ilGen.Emit (errorAt, OpCodes.Br, target);
2233 }
2234 }
2235
2236 /**
2237 * @brief output code for a jump target label statement.
2238 * If there are any backward jumps to the label, do a CheckRun() also.
2239 */
2240 private void GenerateStmtLabel (TokenStmtLabel labelStmt)
2241 {
2242 if (!labelStmt.labelTagged) {
2243 labelStmt.labelStruct = ilGen.DefineLabel ("jump_" + labelStmt.name.val);
2244 labelStmt.labelTagged = true;
2245 }
2246 ilGen.MarkLabel (labelStmt.labelStruct);
2247 if (labelStmt.hasBkwdRefs) {
2248 EmitCallCheckRun (labelStmt, false);
2249 }
2250
2251 /*
2252 * We are going to say that the label falls through.
2253 * It would be nice if we could analyze all referencing
2254 * goto's to see if all of them are not used but we are
2255 * going to assume that if the script writer put a label
2256 * somewhere, it is probably going to be used.
2257 */
2258 mightGetHere = true;
2259 }
2260
2261 /**
2262 * @brief Generate code for a script-defined type's <typename>.$new(<argsig>) method.
2263 * It is used to malloc the object and initialize it.
2264 * It is defined as a script-defined type static method, so the object level
2265 * method gets the XMRInstance pointer passed as arg 0, and the method is
2266 * supposed to return the allocated and constructed XMRSDTypeClObj
2267 * object pointer.
2268 */
2269 private void GenerateStmtNewobj (TokenStmtNewobj newobjStmt)
2270 {
2271 /*
2272 * First off, malloc a new empty XMRSDTypeClObj object
2273 * then call the XMRSDTypeClObj()-level constructor.
2274 * Store the result in local var $objptr.
2275 */
2276 newobjStmt.objptrVar.location.PopPre (this, newobjStmt);
2277 ilGen.Emit (newobjStmt, OpCodes.Ldarg_0);
2278 ilGen.Emit (newobjStmt, OpCodes.Ldc_I4, curDeclFunc.sdtClass.sdTypeIndex);
2279 ilGen.Emit (newobjStmt, OpCodes.Newobj, sdtClassConstructorInfo);
2280 newobjStmt.objptrVar.location.PopPost (this, newobjStmt);
2281
2282 /*
2283 * Now call the script-level constructor.
2284 * Pass the object pointer in $objptr as it's 'this' argument.
2285 * The rest of the args are the script-visible args and are just copied from $new() call.
2286 */
2287 GenerateFromRValCall (newobjStmt.rValCall);
2288
2289 /*
2290 * Put object pointer in retval so it gets returned to caller.
2291 */
2292 newobjStmt.objptrVar.location.PushVal (this, newobjStmt);
2293 ilGen.Emit (newobjStmt, OpCodes.Stloc, retValue);
2294
2295 /*
2296 * Exit the function like a return statement.
2297 * And thus we don't fall through.
2298 */
2299 ilGen.Emit (newobjStmt, OpCodes.Leave, retLabel);
2300 mightGetHere = false;
2301 }
2302
2303 /**
2304 * @brief output code for a return statement.
2305 * @param retStmt = return statement token, including return value if any
2306 */
2307 private void GenerateStmtRet (TokenStmtRet retStmt)
2308 {
2309 if (!mightGetHere) return;
2310
2311 for (TokenStmtBlock stmtBlock = curStmtBlock; stmtBlock != null; stmtBlock = stmtBlock.outerStmtBlock) {
2312 if (stmtBlock.isFinally) {
2313 ErrorMsg (retStmt, "cannot return out of finally");
2314 return;
2315 }
2316 }
2317
2318 if (curDeclFunc.retType is TokenTypeVoid) {
2319 if (retStmt.rVal != null) {
2320 ErrorMsg (retStmt, "function returns void, no value allowed");
2321 return;
2322 }
2323 } else {
2324 if (retStmt.rVal == null) {
2325 ErrorMsg (retStmt, "function requires return value type " + curDeclFunc.retType.ToString ());
2326 return;
2327 }
2328 CompValu rVal = GenerateFromRVal (retStmt.rVal);
2329 rVal.PushVal (this, retStmt.rVal, curDeclFunc.retType);
2330 ilGen.Emit (retStmt, OpCodes.Stloc, retValue);
2331 }
2332
2333 /*
2334 * Use a OpCodes.Leave instruction to break out of any try { } blocks.
2335 * All Leave's inside script-defined try { } need call labels (see GenerateStmtTry()).
2336 */
2337 bool brokeOutOfTry = false;
2338 for (TokenStmtBlock stmtBlock = curStmtBlock; stmtBlock != null; stmtBlock = stmtBlock.outerStmtBlock) {
2339 if (stmtBlock.isTry) {
2340 brokeOutOfTry = true;
2341 break;
2342 }
2343 }
2344 if (brokeOutOfTry) new CallLabel (this, retStmt);
2345 ilGen.Emit (retStmt, OpCodes.Leave, retLabel);
2346 if (brokeOutOfTry) openCallLabel = null;
2347
2348 /*
2349 * 'return' statements never fall through.
2350 */
2351 mightGetHere = false;
2352 }
2353
2354 /**
2355 * @brief the statement is just an expression, most likely an assignment or a ++ or -- thing.
2356 */
2357 private void GenerateStmtRVal (TokenStmtRVal rValStmt)
2358 {
2359 if (!mightGetHere) return;
2360
2361 GenerateFromRVal (rValStmt.rVal);
2362 }
2363
2364 /**
2365 * @brief generate code for a 'state' statement that transitions state.
2366 * It sets the new state by throwing a ScriptChangeStateException.
2367 */
2368 private void GenerateStmtState (TokenStmtState stateStmt)
2369 {
2370 if (!mightGetHere) return;
2371
2372 int index = 0; // 'default' state
2373
2374 /*
2375 * Set new state value by throwing an exception.
2376 * These exceptions aren't catchable by script-level try { } catch { }.
2377 */
2378 if ((stateStmt.state != null) && !stateIndices.TryGetValue (stateStmt.state.val, out index)) {
2379 // The moron XEngine compiles scripts that reference undefined states.
2380 // So rather than produce a compile-time error, we'll throw an exception at runtime.
2381 // ErrorMsg (stateStmt, "undefined state " + stateStmt.state.val);
2382
2383 // throw new UndefinedStateException (stateStmt.state.val);
2384 ilGen.Emit (stateStmt, OpCodes.Ldstr, stateStmt.state.val);
2385 ilGen.Emit (stateStmt, OpCodes.Newobj, scriptUndefinedStateExceptionConstructorInfo);
2386 } else {
2387 ilGen.Emit (stateStmt, OpCodes.Ldc_I4, index); // new state's index
2388 ilGen.Emit (stateStmt, OpCodes.Newobj, scriptChangeStateExceptionConstructorInfo);
2389 }
2390 ilGen.Emit (stateStmt, OpCodes.Throw);
2391
2392 /*
2393 * 'state' statements never fall through.
2394 */
2395 mightGetHere = false;
2396 }
2397
2398 /**
2399 * @brief output code for a 'switch' statement
2400 */
2401 private void GenerateStmtSwitch (TokenStmtSwitch switchStmt)
2402 {
2403 if (!mightGetHere) return;
2404
2405 /*
2406 * Output code to calculate index.
2407 */
2408 CompValu testRVal = GenerateFromRVal (switchStmt.testRVal);
2409
2410 /*
2411 * Generate code based on string or integer index.
2412 */
2413 if ((testRVal.type is TokenTypeKey) || (testRVal.type is TokenTypeStr)) {
2414 GenerateStmtSwitchStr (testRVal, switchStmt);
2415 } else {
2416 GenerateStmtSwitchInt (testRVal, switchStmt);
2417 }
2418 }
2419
2420 private void GenerateStmtSwitchInt (CompValu testRVal, TokenStmtSwitch switchStmt)
2421 {
2422 testRVal.PushVal (this, switchStmt.testRVal, tokenTypeInt);
2423
2424 BreakContTarg oldBreakTarg = curBreakTarg;
2425 ScriptMyLabel defaultLabel = null;
2426 TokenSwitchCase sortedCases = null;
2427 TokenSwitchCase defaultCase = null;
2428
2429 curBreakTarg = new BreakContTarg (this, "switchbreak_" + switchStmt.Unique);
2430
2431 /*
2432 * Build list of cases sorted by ascending values.
2433 * There should not be any overlapping of values.
2434 */
2435 for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
2436 thisCase.label = ilGen.DefineLabel ("case_" + thisCase.Unique);
2437
2438 /*
2439 * The default case if any, goes in its own separate slot.
2440 */
2441 if (thisCase.rVal1 == null) {
2442 if (defaultCase != null) {
2443 ErrorMsg (thisCase, "only one default case allowed");
2444 ErrorMsg (defaultCase, "...prior default case");
2445 return;
2446 }
2447 defaultCase = thisCase;
2448 defaultLabel = thisCase.label;
2449 continue;
2450 }
2451
2452 /*
2453 * Evaluate case operands, they must be compile-time integer constants.
2454 */
2455 CompValu rVal = GenerateFromRVal (thisCase.rVal1);
2456 if (!IsConstIntExpr (rVal, out thisCase.val1)) {
2457 ErrorMsg (thisCase.rVal1, "must be compile-time char or integer constant");
2458 return;
2459 }
2460 thisCase.val2 = thisCase.val1;
2461 if (thisCase.rVal2 != null) {
2462 rVal = GenerateFromRVal (thisCase.rVal2);
2463 if (!IsConstIntExpr (rVal, out thisCase.val2)) {
2464 ErrorMsg (thisCase.rVal2, "must be compile-time char or integer constant");
2465 return;
2466 }
2467 }
2468 if (thisCase.val2 < thisCase.val1) {
2469 ErrorMsg (thisCase.rVal2, "must be .ge. first value for the case");
2470 return;
2471 }
2472
2473 /*
2474 * Insert into list, sorted by value.
2475 * Note that both limits are inclusive.
2476 */
2477 TokenSwitchCase lastCase = null;
2478 TokenSwitchCase nextCase;
2479 for (nextCase = sortedCases; nextCase != null; nextCase = nextCase.nextSortedCase) {
2480 if (nextCase.val1 > thisCase.val2) break;
2481 if (nextCase.val2 >= thisCase.val1) {
2482 ErrorMsg (thisCase, "value used by previous case");
2483 ErrorMsg (nextCase, "...previous case");
2484 return;
2485 }
2486 lastCase = nextCase;
2487 }
2488 thisCase.nextSortedCase = nextCase;
2489 if (lastCase == null) {
2490 sortedCases = thisCase;
2491 } else {
2492 lastCase.nextSortedCase = thisCase;
2493 }
2494 }
2495
2496 if (defaultLabel == null) {
2497 defaultLabel = ilGen.DefineLabel ("default_" + switchStmt.Unique);
2498 }
2499
2500 /*
2501 * Output code to jump to the case statement's labels based on integer index on stack.
2502 * Note that each case still has the integer index on stack when jumped to.
2503 */
2504 int offset = 0;
2505 for (TokenSwitchCase thisCase = sortedCases; thisCase != null;) {
2506
2507 /*
2508 * Scan through list of cases to find the maximum number of cases who's numvalues-to-case ratio
2509 * is from 0.5 to 2.0. If such a group is found, use a CIL switch for them. If not, just use a
2510 * compare-and-branch for the current case.
2511 */
2512 int numCases = 0;
2513 int numFound = 0;
2514 int lowValue = thisCase.val1;
2515 int numValues = 0;
2516 for (TokenSwitchCase scanCase = thisCase; scanCase != null; scanCase = scanCase.nextSortedCase) {
2517 int nVals = scanCase.val2 - thisCase.val1 + 1;
2518 double ratio = (double)nVals / (double)(++ numCases);
2519 if ((ratio >= 0.5) && (ratio <= 2.0)) {
2520 numFound = numCases;
2521 numValues = nVals;
2522 }
2523 }
2524 if (numFound > 1) {
2525
2526 /*
2527 * There is a group of case's, starting with thisCase, that fall within our criteria, ie,
2528 * that have a nice density of meaningful jumps.
2529 *
2530 * So first generate an array of jumps to the default label (explicit or implicit).
2531 */
2532 ScriptMyLabel[] labels = new ScriptMyLabel[numValues];
2533 for (int i = 0; i < numValues; i ++) {
2534 labels[i] = defaultLabel;
2535 }
2536
2537 /*
2538 * Next, for each case in that group, fill in the corresponding array entries to jump to
2539 * that case's label.
2540 */
2541 do {
2542 for (int i = thisCase.val1; i <= thisCase.val2; i ++) {
2543 labels[i-lowValue] = thisCase.label;
2544 }
2545 thisCase = thisCase.nextSortedCase;
2546 } while (-- numFound > 0);
2547
2548 /*
2549 * Subtract the low value and do the computed jump.
2550 * The OpCodes.Switch falls through if out of range (unsigned compare).
2551 */
2552 if (offset != lowValue) {
2553 ilGen.Emit (switchStmt, OpCodes.Ldc_I4, lowValue - offset);
2554 ilGen.Emit (switchStmt, OpCodes.Sub);
2555 offset = lowValue;
2556 }
2557 ilGen.Emit (switchStmt, OpCodes.Dup);
2558 ilGen.Emit (switchStmt, OpCodes.Switch, labels);
2559 } else {
2560
2561 /*
2562 * It's not economical to do with a computed jump, so output a subtract/compare/branch
2563 * for thisCase.
2564 */
2565 if (lowValue == thisCase.val2) {
2566 ilGen.Emit (switchStmt, OpCodes.Dup);
2567 ilGen.Emit (switchStmt, OpCodes.Ldc_I4, lowValue - offset);
2568 ilGen.Emit (switchStmt, OpCodes.Beq, thisCase.label);
2569 } else {
2570 if (offset != lowValue) {
2571 ilGen.Emit (switchStmt, OpCodes.Ldc_I4, lowValue - offset);
2572 ilGen.Emit (switchStmt, OpCodes.Sub);
2573 offset = lowValue;
2574 }
2575 ilGen.Emit (switchStmt, OpCodes.Dup);
2576 ilGen.Emit (switchStmt, OpCodes.Ldc_I4, thisCase.val2 - offset);
2577 ilGen.Emit (switchStmt, OpCodes.Ble_Un, thisCase.label);
2578 }
2579 thisCase = thisCase.nextSortedCase;
2580 }
2581 }
2582 ilGen.Emit (switchStmt, OpCodes.Br, defaultLabel);
2583
2584 /*
2585 * Output code for the cases themselves, in the order given by the programmer,
2586 * so they fall through as programmer wants. This includes the default case, if any.
2587 *
2588 * Each label is jumped to with the index still on the stack. So pop it off in case
2589 * the case body does a goto outside the switch or a return. If the case body might
2590 * fall through to the next case or the bottom of the switch, push a zero so the stack
2591 * matches in all cases.
2592 */
2593 for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
2594 ilGen.MarkLabel (thisCase.label); // the branch comes here
2595 ilGen.Emit (thisCase, OpCodes.Pop); // pop the integer index off stack
2596 mightGetHere = true; // it's possible to get here
2597 for (TokenStmt stmt = thisCase.stmts; stmt != null; stmt = (TokenStmt)(stmt.nextToken)) {
2598 GenerateStmt (stmt); // output the case/explicit default body
2599 }
2600 if (mightGetHere) {
2601 ilGen.Emit (thisCase, OpCodes.Ldc_I4_0);
2602 // in case we fall through, push a dummy integer index
2603 }
2604 }
2605
2606 /*
2607 * If no explicit default case, output the default label here.
2608 */
2609 if (defaultCase == null) {
2610 ilGen.MarkLabel (defaultLabel);
2611 mightGetHere = true;
2612 }
2613
2614 /*
2615 * If the last case of the switch falls through out the bottom,
2616 * we have to pop the index still on the stack.
2617 */
2618 if (mightGetHere) {
2619 ilGen.Emit (switchStmt, OpCodes.Pop);
2620 }
2621
2622 /*
2623 * Output the 'break' statement target label.
2624 * Note that the integer index is not on the stack at this point.
2625 */
2626 if (curBreakTarg.used) {
2627 ilGen.MarkLabel (curBreakTarg.label);
2628 mightGetHere = true;
2629 }
2630
2631 curBreakTarg = oldBreakTarg;
2632 }
2633
2634 private void GenerateStmtSwitchStr (CompValu testRVal, TokenStmtSwitch switchStmt)
2635 {
2636 BreakContTarg oldBreakTarg = curBreakTarg;
2637 ScriptMyLabel defaultLabel = null;
2638 TokenSwitchCase caseTreeTop = null;
2639 TokenSwitchCase defaultCase = null;
2640
2641 curBreakTarg = new BreakContTarg (this, "switchbreak_" + switchStmt.Unique);
2642
2643 /*
2644 * Make sure value is in a temp so we don't compute it more than once.
2645 */
2646 if (!(testRVal is CompValuTemp)) {
2647 CompValuTemp temp = new CompValuTemp (testRVal.type, this);
2648 testRVal.PushVal (this, switchStmt);
2649 temp.Pop (this, switchStmt);
2650 testRVal = temp;
2651 }
2652
2653 /*
2654 * Build tree of cases.
2655 * There should not be any overlapping of values.
2656 */
2657 for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
2658 thisCase.label = ilGen.DefineLabel ("case");
2659
2660 /*
2661 * The default case if any, goes in its own separate slot.
2662 */
2663 if (thisCase.rVal1 == null) {
2664 if (defaultCase != null) {
2665 ErrorMsg (thisCase, "only one default case allowed");
2666 ErrorMsg (defaultCase, "...prior default case");
2667 return;
2668 }
2669 defaultCase = thisCase;
2670 defaultLabel = thisCase.label;
2671 continue;
2672 }
2673
2674 /*
2675 * Evaluate case operands, they must be compile-time string constants.
2676 */
2677 CompValu rVal = GenerateFromRVal (thisCase.rVal1);
2678 if (!IsConstStrExpr (rVal, out thisCase.str1)) {
2679 ErrorMsg (thisCase.rVal1, "must be compile-time string constant");
2680 continue;
2681 }
2682 thisCase.str2 = thisCase.str1;
2683 if (thisCase.rVal2 != null) {
2684 rVal = GenerateFromRVal (thisCase.rVal2);
2685 if (!IsConstStrExpr (rVal, out thisCase.str2)) {
2686 ErrorMsg (thisCase.rVal2, "must be compile-time string constant");
2687 continue;
2688 }
2689 }
2690 if (String.Compare (thisCase.str2, thisCase.str1, StringComparison.Ordinal) < 0) {
2691 ErrorMsg (thisCase.rVal2, "must be .ge. first value for the case");
2692 continue;
2693 }
2694
2695 /*
2696 * Insert into list, sorted by value.
2697 * Note that both limits are inclusive.
2698 */
2699 caseTreeTop = InsertCaseInTree (caseTreeTop, thisCase);
2700 }
2701
2702 /*
2703 * Balance tree so we end up generating code that does O(log2 n) comparisons.
2704 */
2705 caseTreeTop = BalanceTree (caseTreeTop);
2706
2707 /*
2708 * Output compare and branch instructions in a tree-like fashion so we do O(log2 n) comparisons.
2709 */
2710 if (defaultLabel == null) {
2711 defaultLabel = ilGen.DefineLabel ("default");
2712 }
2713 OutputStrCase (testRVal, caseTreeTop, defaultLabel);
2714
2715 /*
2716 * Output code for the cases themselves, in the order given by the programmer,
2717 * so they fall through as programmer wants. This includes the default case, if any.
2718 */
2719 for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
2720 ilGen.MarkLabel (thisCase.label); // the branch comes here
2721 mightGetHere = true; // it's possible to get here
2722 for (TokenStmt stmt = thisCase.stmts; stmt != null; stmt = (TokenStmt)(stmt.nextToken)) {
2723 GenerateStmt (stmt); // output the case/explicit default body
2724 }
2725 }
2726
2727 /*
2728 * If no explicit default case, output the default label here.
2729 */
2730 if (defaultCase == null) {
2731 ilGen.MarkLabel (defaultLabel);
2732 mightGetHere = true;
2733 }
2734
2735 /*
2736 * Output the 'break' statement target label.
2737 */
2738 if (curBreakTarg.used) {
2739 ilGen.MarkLabel (curBreakTarg.label);
2740 mightGetHere = true;
2741 }
2742
2743 curBreakTarg = oldBreakTarg;
2744 }
2745
2746 /**
2747 * @brief Insert a case in a tree of cases
2748 * @param r = root of existing cases to insert into
2749 * @param n = new case being inserted
2750 * @returns new root with new case inserted
2751 */
2752 private TokenSwitchCase InsertCaseInTree (TokenSwitchCase r, TokenSwitchCase n)
2753 {
2754 if (r == null) return n;
2755
2756 TokenSwitchCase t = r;
2757 while (true) {
2758 if (String.Compare (n.str2, t.str1, StringComparison.Ordinal) < 0) {
2759 if (t.lowerCase == null) {
2760 t.lowerCase = n;
2761 break;
2762 }
2763 t = t.lowerCase;
2764 continue;
2765 }
2766 if (String.Compare (n.str1, t.str2, StringComparison.Ordinal) > 0) {
2767 if (t.higherCase == null) {
2768 t.higherCase = n;
2769 break;
2770 }
2771 t = t.higherCase;
2772 continue;
2773 }
2774 ErrorMsg (n, "duplicate case");
2775 ErrorMsg (r, "...duplicate of");
2776 break;
2777 }
2778 return r;
2779 }
2780
2781 /**
2782 * @brief Balance a tree so left & right halves contain same number within +-1
2783 * @param r = root of tree to balance
2784 * @returns new root
2785 */
2786 private static TokenSwitchCase BalanceTree (TokenSwitchCase r)
2787 {
2788 if (r == null) return r;
2789
2790 int lc = CountTree (r.lowerCase);
2791 int hc = CountTree (r.higherCase);
2792 TokenSwitchCase n, x;
2793
2794 /*
2795 * If lower side is heavy, move highest nodes from lower side to
2796 * higher side until balanced.
2797 */
2798 while (lc > hc + 1) {
2799 x = ExtractHighest (r.lowerCase, out n);
2800 n.lowerCase = x;
2801 n.higherCase = r;
2802 r.lowerCase = null;
2803 r = n;
2804 lc --;
2805 hc ++;
2806 }
2807
2808 /*
2809 * If higher side is heavy, move lowest nodes from higher side to
2810 * lower side until balanced.
2811 */
2812 while (hc > lc + 1) {
2813 x = ExtractLowest (r.higherCase, out n);
2814 n.higherCase = x;
2815 n.lowerCase = r;
2816 r.higherCase = null;
2817 r = n;
2818 lc ++;
2819 hc --;
2820 }
2821
2822 /*
2823 * Now balance each side because they can be lopsided individually.
2824 */
2825 r.lowerCase = BalanceTree (r.lowerCase);
2826 r.higherCase = BalanceTree (r.higherCase);
2827 return r;
2828 }
2829
2830 /**
2831 * @brief Get number of nodes in a tree
2832 * @param n = root of tree to count
2833 * @returns number of nodes including root
2834 */
2835 private static int CountTree (TokenSwitchCase n)
2836 {
2837 if (n == null) return 0;
2838 return 1 + CountTree (n.lowerCase) + CountTree (n.higherCase);
2839 }
2840
2841 // Extract highest node from a tree
2842 // @param r = root of tree to extract highest from
2843 // @returns new root after node has been extracted
2844 // n = node that was extracted from tree
2845 private static TokenSwitchCase ExtractHighest (TokenSwitchCase r, out TokenSwitchCase n)
2846 {
2847 if (r.higherCase == null) {
2848 n = r;
2849 return r.lowerCase;
2850 }
2851 r.higherCase = ExtractHighest (r.higherCase, out n);
2852 return r;
2853 }
2854
2855 // Extract lowest node from a tree
2856 // @param r = root of tree to extract lowest from
2857 // @returns new root after node has been extracted
2858 // n = node that was extracted from tree
2859 private static TokenSwitchCase ExtractLowest (TokenSwitchCase r, out TokenSwitchCase n)
2860 {
2861 if (r.lowerCase == null) {
2862 n = r;
2863 return r.higherCase;
2864 }
2865 r.lowerCase = ExtractLowest (r.lowerCase, out n);
2866 return r;
2867 }
2868
2869 /**
2870 * Output code for string-style case of a switch/case to jump to the script code associated with the case.
2871 * @param testRVal = value being switched on
2872 * @param thisCase = case that the code is being output for
2873 * @param defaultLabel = where the default clause is (or past all cases if none)
2874 * Note:
2875 * Outputs code for this case and the lowerCase and higherCases if any.
2876 * If no lowerCase or higherCase, outputs a br to defaultLabel so this code never falls through.
2877 */
2878 private void OutputStrCase (CompValu testRVal, TokenSwitchCase thisCase, ScriptMyLabel defaultLabel)
2879 {
2880 /*
2881 * If nothing lower on tree and there is a single case value,
2882 * just do one compare for equality.
2883 */
2884 if ((thisCase.lowerCase == null) && (thisCase.higherCase == null) && (thisCase.str1 == thisCase.str2)) {
2885 testRVal.PushVal (this, thisCase, tokenTypeStr);
2886 ilGen.Emit (thisCase, OpCodes.Ldstr, thisCase.str1);
2887 ilGen.Emit (thisCase, OpCodes.Ldc_I4, (int)StringComparison.Ordinal);
2888 ilGen.Emit (thisCase, OpCodes.Call, stringCompareMethodInfo);
2889 ilGen.Emit (thisCase, OpCodes.Brfalse, thisCase.label);
2890 ilGen.Emit (thisCase, OpCodes.Br, defaultLabel);
2891 return;
2892 }
2893
2894 /*
2895 * Determine where to jump if switch value is lower than lower case value.
2896 */
2897 ScriptMyLabel lowerLabel = defaultLabel;
2898 if (thisCase.lowerCase != null) {
2899 lowerLabel = ilGen.DefineLabel ("lower");
2900 }
2901
2902 /*
2903 * If single case value, put comparison result in this temp.
2904 */
2905 CompValuTemp cmpv1 = null;
2906 if (thisCase.str1 == thisCase.str2) {
2907 cmpv1 = new CompValuTemp (tokenTypeInt, this);
2908 }
2909
2910 /*
2911 * If switch value .lt. lower case value, jump to lower label.
2912 * Maybe save comparison result in a temp.
2913 */
2914 testRVal.PushVal (this, thisCase, tokenTypeStr);
2915 ilGen.Emit (thisCase, OpCodes.Ldstr, thisCase.str1);
2916 ilGen.Emit (thisCase, OpCodes.Ldc_I4, (int)StringComparison.Ordinal);
2917 ilGen.Emit (thisCase, OpCodes.Call, stringCompareMethodInfo);
2918 if (cmpv1 != null) {
2919 ilGen.Emit (thisCase, OpCodes.Dup);
2920 cmpv1.Pop (this, thisCase);
2921 }
2922 ilGen.Emit (thisCase, OpCodes.Ldc_I4_0);
2923 ilGen.Emit (thisCase, OpCodes.Blt, lowerLabel);
2924
2925 /*
2926 * If switch value .le. higher case value, jump to case code.
2927 * Maybe get comparison from the temp.
2928 */
2929 if (cmpv1 == null) {
2930 testRVal.PushVal (this, thisCase, tokenTypeStr);
2931 ilGen.Emit (thisCase, OpCodes.Ldstr, thisCase.str2);
2932 ilGen.Emit (thisCase, OpCodes.Ldc_I4, (int)StringComparison.Ordinal);
2933 ilGen.Emit (thisCase, OpCodes.Call, stringCompareMethodInfo);
2934 } else {
2935 cmpv1.PushVal (this, thisCase);
2936 }
2937 ilGen.Emit (thisCase, OpCodes.Ldc_I4_0);
2938 ilGen.Emit (thisCase, OpCodes.Ble, thisCase.label);
2939
2940 /*
2941 * Output code for higher comparison if any.
2942 */
2943 if (thisCase.higherCase == null) {
2944 ilGen.Emit (thisCase, OpCodes.Br, defaultLabel);
2945 } else {
2946 OutputStrCase (testRVal, thisCase.higherCase, defaultLabel);
2947 }
2948
2949 /*
2950 * Output code for lower comparison if any.
2951 */
2952 if (thisCase.lowerCase != null) {
2953 ilGen.MarkLabel (lowerLabel);
2954 OutputStrCase (testRVal, thisCase.lowerCase, defaultLabel);
2955 }
2956 }
2957
2958 /**
2959 * @brief output code for a throw statement.
2960 * @param throwStmt = throw statement token, including value to be thrown
2961 */
2962 private void GenerateStmtThrow (TokenStmtThrow throwStmt)
2963 {
2964 if (!mightGetHere) return;
2965
2966 /*
2967 * 'throw' statements never fall through.
2968 */
2969 mightGetHere = false;
2970
2971 /*
2972 * Output code for either a throw or a rethrow.
2973 */
2974 if (throwStmt.rVal == null) {
2975 for (TokenStmtBlock blk = curStmtBlock; blk != null; blk = blk.outerStmtBlock) {
2976 if (curStmtBlock.isCatch) {
2977 ilGen.Emit (throwStmt, OpCodes.Rethrow);
2978 return;
2979 }
2980 }
2981 ErrorMsg (throwStmt, "rethrow allowed only in catch clause");
2982 } else {
2983 CompValu rVal = GenerateFromRVal (throwStmt.rVal);
2984 rVal.PushVal (this, throwStmt.rVal, tokenTypeObj);
2985 ilGen.Emit (throwStmt, OpCodes.Call, thrownExceptionWrapMethodInfo);
2986 ilGen.Emit (throwStmt, OpCodes.Throw);
2987 }
2988 }
2989
2990 /**
2991 * @brief output code for a try/catch/finally block
2992 */
2993 private void GenerateStmtTry (TokenStmtTry tryStmt)
2994 {
2995 if (!mightGetHere) return;
2996
2997 /*
2998 * Reducer should make sure we have exactly one of catch or finally.
2999 */
3000 if ((tryStmt.catchStmt == null) && (tryStmt.finallyStmt == null)) {
3001 throw new Exception ("must have a catch or a finally on try");
3002 }
3003 if ((tryStmt.catchStmt != null) && (tryStmt.finallyStmt != null)) {
3004 throw new Exception ("can't have both catch and finally on same try");
3005 }
3006
3007 /*
3008 * Stack the call labels.
3009 * Try blocks have their own series of call labels.
3010 */
3011 ScriptMyLocal saveCallNo = actCallNo;
3012 LinkedList<CallLabel> saveCallLabels = actCallLabels;
3013
3014 /*
3015 * Generate code for either try { } catch { } or try { } finally { }.
3016 */
3017 if (tryStmt.catchStmt != null) GenerateStmtTryCatch (tryStmt);
3018 if (tryStmt.finallyStmt != null) GenerateStmtTryFinally (tryStmt);
3019
3020 /*
3021 * Restore call labels.
3022 */
3023 actCallNo = saveCallNo;
3024 actCallLabels = saveCallLabels;
3025 }
3026
3027
3028 /**
3029 * @brief output code for a try/catch block
3030 *
3031 * int __tryCallNo = -1; // call number within try { } subblock
3032 * int __catCallNo = -1; // call number within catch { } subblock
3033 * Exception __catThrown = null; // caught exception
3034 * <oldCallLabel>: // the outside world jumps here to restore us no matter ...
3035 * try { // ... where we actually were inside of try/catch
3036 * if (__tryCallNo >= 0) goto tryCallSw; // maybe go do restore
3037 * <try body using __tryCallNo> // execute script-defined code
3038 * // ...stack capture WILL run catch { } subblock
3039 * leave tryEnd; // exits
3040 * tryThrow:<tryCallLabel>:
3041 * throw new ScriptRestoreCatchException(__catThrown); // catch { } was running, jump to its beginning
3042 * tryCallSw: // restoring...
3043 * switch (__tryCallNo) back up into <try body> // not catching, jump back inside try
3044 * } catch (Exception exc) {
3045 * exc = ScriptRestoreCatchException.Unwrap(exc); // unwrap possible ScriptRestoreCatchException
3046 * if (exc == null) goto catchRetro; // rethrow if IXMRUncatchable (eg, StackCaptureException)
3047 * __catThrown = exc; // save what was thrown so restoring try { } will throw it again
3048 * catchVar = exc; // set up script-visible variable
3049 * __tryCallNo = tryThrow:<tryCallLabel>
3050 * if (__catCallNo >= 0) goto catchCallSw; // if restoring, go check below
3051 * <catch body using __catCallNo> // normal, execute script-defined code
3052 * leave tryEnd; // all done, exit catch { }
3053 * catchRetro:
3054 * rethrow;
3055 * catchCallSw:
3056 * switch (__catCallNo) back up into <catch body> // restart catch { } code wherever it was
3057 * }
3058 * tryEnd:
3059 */
3060 private void GenerateStmtTryCatch (TokenStmtTry tryStmt)
3061 {
3062 CompValuTemp tryCallNo = new CompValuTemp (tokenTypeInt, this);
3063 CompValuTemp catCallNo = new CompValuTemp (tokenTypeInt, this);
3064 CompValuTemp catThrown = new CompValuTemp (tokenTypeExc, this);
3065
3066 ScriptMyLabel tryCallSw = ilGen.DefineLabel ("__tryCallSw_" + tryStmt.Unique);
3067 ScriptMyLabel catchRetro = ilGen.DefineLabel ("__catchRetro_" + tryStmt.Unique);
3068 ScriptMyLabel catchCallSw = ilGen.DefineLabel ("__catchCallSw_" + tryStmt.Unique);
3069 ScriptMyLabel tryEnd = ilGen.DefineLabel ("__tryEnd_" + tryStmt.Unique);
3070
3071 SetCallNo (tryStmt, tryCallNo, -1);
3072 SetCallNo (tryStmt, catCallNo, -1);
3073 ilGen.Emit (tryStmt, OpCodes.Ldnull);
3074 catThrown.Pop (this, tryStmt);
3075
3076 new CallLabel (this, tryStmt); // <oldcalllabel>:
3077 ilGen.BeginExceptionBlock (); // try {
3078 openCallLabel = null;
3079 if (DEBUG_TRYSTMT) {
3080 ilGen.Emit (tryStmt, OpCodes.Ldstr, "enter try*: " + tryStmt.line + " callMode=");
3081 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3082 PushXMRInst ();
3083 ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
3084 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3085 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3086 ilGen.Emit (tryStmt, OpCodes.Ldstr, " tryCallNo=");
3087 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3088 tryCallNo.PushVal (this, tryStmt);
3089 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3090 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3091 ilGen.Emit (tryStmt, OpCodes.Ldstr, " catThrown.IsNull=");
3092 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3093 catThrown.PushVal (this, tryStmt);
3094 ilGen.Emit (tryStmt, OpCodes.Ldnull);
3095 ilGen.Emit (tryStmt, OpCodes.Ceq);
3096 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3097 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3098 ilGen.Emit (tryStmt, OpCodes.Ldstr, " catCallNo=");
3099 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3100 catCallNo.PushVal (this, tryStmt);
3101 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3102 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3103 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3104 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3105 }
3106
3107 GetCallNo (tryStmt, tryCallNo); // if (__tryCallNo >= 0) goto tryCallSw;
3108 ilGen.Emit (tryStmt, OpCodes.Ldc_I4_0);
3109 ilGen.Emit (tryStmt, OpCodes.Bge, tryCallSw);
3110
3111 actCallNo = tryCallNo.localBuilder; // set up __tryCallNo for call labels
3112 actCallLabels = new LinkedList<CallLabel> ();
3113
3114 GenerateStmtBlock (tryStmt.tryStmt); // output the try block statement subblock
3115
3116 bool tryBlockFallsOutBottom = mightGetHere;
3117 if (tryBlockFallsOutBottom) {
3118 new CallLabel (this, tryStmt); // <tryCallLabel>:
3119 ilGen.Emit (tryStmt, OpCodes.Leave, tryEnd); // leave tryEnd;
3120 openCallLabel = null;
3121 }
3122
3123 CallLabel tryThrow = new CallLabel (this, tryStmt); // tryThrow:<tryCallLabel>:
3124 if (DEBUG_TRYSTMT) {
3125 ilGen.Emit (tryStmt, OpCodes.Ldstr, "tryThrow*: " + tryStmt.line + " catThrown=");
3126 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3127 catThrown.PushVal (this, tryStmt);
3128 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3129 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3130 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3131 }
3132 catThrown.PushVal (this, tryStmt); // throw new ScriptRestoreCatchException (__catThrown);
3133 ilGen.Emit (tryStmt, OpCodes.Newobj, scriptRestoreCatchExceptionConstructorInfo);
3134 ilGen.Emit (tryStmt, OpCodes.Throw);
3135 openCallLabel = null;
3136
3137 ilGen.MarkLabel (tryCallSw); // tryCallSw:
3138 if (DEBUG_TRYSTMT) {
3139 ilGen.Emit (tryStmt, OpCodes.Ldstr, "tryCallSw*: " + tryStmt.line + " tryCallNo=");
3140 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3141 tryCallNo.PushVal (this, tryStmt);
3142 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3143 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3144 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3145 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3146 }
3147 OutputCallNoSwitchStmt (); // switch (tryCallNo) ...
3148
3149 CompValuLocalVar catchVarLocExc = null;
3150 CompValuTemp catchVarLocStr = null;
3151
3152 if (tryStmt.catchVar.type.ToSysType () == typeof (Exception)) {
3153 catchVarLocExc = new CompValuLocalVar (tryStmt.catchVar.type, tryStmt.catchVar.name.val, this);
3154 } else if (tryStmt.catchVar.type.ToSysType () == typeof (String)) {
3155 catchVarLocStr = new CompValuTemp (tryStmt.catchVar.type, this);
3156 }
3157
3158 ScriptMyLocal excLocal = ilGen.DeclareLocal (typeof (String), "catchstr_" + tryStmt.Unique);
3159
3160 ilGen.BeginCatchBlock (typeof (Exception)); // start of the catch block that can catch any exception
3161 if (DEBUG_TRYSTMT) {
3162 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, "enter catch*: " + tryStmt.line + " callMode=");
3163 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3164 PushXMRInst ();
3165 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldfld, callModeFieldInfo);
3166 ilGen.Emit (tryStmt.catchStmt, OpCodes.Box, typeof (int));
3167 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3168 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, " catCallNo=");
3169 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3170 catCallNo.PushVal (this, tryStmt);
3171 ilGen.Emit (tryStmt.catchStmt, OpCodes.Box, typeof (int));
3172 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3173 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, " exc=");
3174 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3175 ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup);
3176 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3177 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, "\n");
3178 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3179 }
3180 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, scriptRestoreCatchExceptionUnwrap);
3181 // exc = ScriptRestoreCatchException.Unwrap (exc);
3182 ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup); // rethrow if IXMRUncatchable (eg, StackCaptureException)
3183 ilGen.Emit (tryStmt.catchStmt, OpCodes.Brfalse, catchRetro);
3184 if (tryStmt.catchVar.type.ToSysType () == typeof (Exception)) {
3185 tryStmt.catchVar.location = catchVarLocExc;
3186 ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup);
3187 catThrown.Pop (this, tryStmt); // store exception object in catThrown
3188 catchVarLocExc.Pop (this, tryStmt.catchVar.name); // also store in script-visible variable
3189 } else if (tryStmt.catchVar.type.ToSysType () == typeof (String)) {
3190 tryStmt.catchVar.location = catchVarLocStr;
3191 ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup);
3192 catThrown.Pop (this, tryStmt); // store exception object in catThrown
3193 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, catchExcToStrMethodInfo);
3194
3195 ilGen.Emit (tryStmt.catchStmt, OpCodes.Stloc, excLocal);
3196 catchVarLocStr.PopPre (this, tryStmt.catchVar.name);
3197 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldloc, excLocal);
3198 catchVarLocStr.PopPost (this, tryStmt.catchVar.name, tokenTypeStr);
3199 } else {
3200 throw new Exception ("bad catch var type " + tryStmt.catchVar.type.ToString ());
3201 }
3202
3203 SetCallNo (tryStmt, tryCallNo, tryThrow.index); // __tryCallNo = tryThrow so it knows to do 'throw catThrown' on restore
3204
3205 GetCallNo (tryStmt, catCallNo); // if (__catCallNo >= 0) goto catchCallSw;
3206 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldc_I4_0);
3207 ilGen.Emit (tryStmt.catchStmt, OpCodes.Bge, catchCallSw);
3208
3209 actCallNo = catCallNo.localBuilder; // set up __catCallNo for call labels
3210 actCallLabels.Clear ();
3211 mightGetHere = true; // if we can get to the 'try' assume we can get to the 'catch'
3212 GenerateStmtBlock (tryStmt.catchStmt); // output catch clause statement subblock
3213
3214 if (mightGetHere) {
3215 new CallLabel (this, tryStmt.catchStmt);
3216 ilGen.Emit (tryStmt.catchStmt, OpCodes.Leave, tryEnd);
3217 openCallLabel = null;
3218 }
3219
3220 ilGen.MarkLabel (catchRetro); // not a script-visible exception, rethrow it
3221 ilGen.Emit (tryStmt.catchStmt, OpCodes.Pop);
3222 ilGen.Emit (tryStmt.catchStmt, OpCodes.Rethrow);
3223
3224 ilGen.MarkLabel (catchCallSw);
3225 OutputCallNoSwitchStmt (); // restoring, jump back inside script-defined body
3226
3227 ilGen.EndExceptionBlock ();
3228 ilGen.MarkLabel (tryEnd);
3229
3230 mightGetHere |= tryBlockFallsOutBottom; // also get here if try body falls out bottom
3231 }
3232
3233 /**
3234 * @brief output code for a try/finally block
3235 *
3236 * This is such a mess because there is hidden state for the finally { } that we have to recreate.
3237 * The finally { } can be entered either via an exception being thrown in the try { } or a leave
3238 * being executed in the try { } whose target is outside the try { } finally { }.
3239 *
3240 * For the thrown exception case, we slip in a try { } catch { } wrapper around the original try { }
3241 * body. This will sense any thrown exception that would execute the finally { }. Then we have our
3242 * try { } throw the exception on restore which gets the finally { } called and on its way again.
3243 *
3244 * For the leave case, we prefix all leave instructions with a call label and we explicitly chain
3245 * all leaves through each try { } that has an associated finally { } that the leave would unwind
3246 * through. This gets each try { } to simply jump to the correct leave instruction which immediately
3247 * invokes the corresponding finally { } and then chains to the next leave instruction on out until
3248 * it gets to its target.
3249 *
3250 * int __finCallNo = -1; // call number within finally { } subblock
3251 * int __tryCallNo = -1; // call number within try { } subblock
3252 * Exception __catThrown = null; // caught exception
3253 * <oldCallLabel>: // the outside world jumps here to restore us no matter ...
3254 * try { // ... where we actually were inside of try/finally
3255 * try {
3256 * if (__tryCallNo >= 0) goto tryCallSw; // maybe go do restore
3257 * <try body using __tryCallNo> // execute script-defined code
3258 * // ...stack capture WILL run catch/finally { } subblock
3259 * leave tryEnd; // executes finally { } subblock and exits
3260 * tryThrow:<tryCallLabel>:
3261 * throw new ScriptRestoreCatchException(__catThrown); // catch { } was running, jump to its beginning
3262 * tryCallSw: // restoring...
3263 * switch (__tryCallNo) back up into <try body> // jump back inside try, ...
3264 * // ... maybe to a leave if we were doing finally { } subblock
3265 * } catch (Exception exc) { // in case we're getting to finally { } via a thrown exception:
3266 * exc = ScriptRestoreCatchException.Unwrap(exc); // unwrap possible ScriptRestoreCatchException
3267 * if (callMode == CallMode_SAVE) goto catchRetro; // don't touch anything if capturing stack
3268 * __catThrown = exc; // save exception so try { } can throw it on restore
3269 * __tryCallNo = tryThrow:<tryCallLabel>; // tell try { } to throw it on restore
3270 * catchRetro:
3271 * rethrow; // in any case, go on to finally { } subblock now
3272 * }
3273 * } finally {
3274 * if (callMode == CallMode_SAVE) goto finEnd; // don't touch anything if capturing stack
3275 * if (__finCallNo >= 0) goto finCallSw; // maybe go do restore
3276 * <finally body using __finCallNo> // normal, execute script-defined code
3277 * finEnd:
3278 * endfinally // jump to leave/throw target or next outer finally { }
3279 * finCallSw:
3280 * switch (__finCallNo) back up into <finally body> // restoring, restart finally { } code wherever it was
3281 * }
3282 * tryEnd:
3283 */
3284 private void GenerateStmtTryFinally (TokenStmtTry tryStmt)
3285 {
3286 CompValuTemp finCallNo = new CompValuTemp (tokenTypeInt, this);
3287 CompValuTemp tryCallNo = new CompValuTemp (tokenTypeInt, this);
3288 CompValuTemp catThrown = new CompValuTemp (tokenTypeExc, this);
3289
3290 ScriptMyLabel tryCallSw = ilGen.DefineLabel ( "__tryCallSw_" + tryStmt.Unique);
3291 ScriptMyLabel catchRetro = ilGen.DefineLabel ( "__catchRetro_" + tryStmt.Unique);
3292 ScriptMyLabel finCallSw = ilGen.DefineLabel ( "__finCallSw_" + tryStmt.Unique);
3293 BreakContTarg finEnd = new BreakContTarg (this, "__finEnd_" + tryStmt.Unique);
3294 ScriptMyLabel tryEnd = ilGen.DefineLabel ( "__tryEnd_" + tryStmt.Unique);
3295
3296 SetCallNo (tryStmt, finCallNo, -1);
3297 SetCallNo (tryStmt, tryCallNo, -1);
3298 ilGen.Emit (tryStmt, OpCodes.Ldnull);
3299 catThrown.Pop (this, tryStmt);
3300
3301 new CallLabel (this, tryStmt); // <oldcalllabel>:
3302 ilGen.BeginExceptionBlock (); // try {
3303 ilGen.BeginExceptionBlock (); // try {
3304 openCallLabel = null;
3305 if (DEBUG_TRYSTMT) {
3306 ilGen.Emit (tryStmt, OpCodes.Ldstr, "enter try*: " + tryStmt.line + " callMode=");
3307 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3308 PushXMRInst ();
3309 ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
3310 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3311 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3312 ilGen.Emit (tryStmt, OpCodes.Ldstr, " tryCallNo=");
3313 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3314 tryCallNo.PushVal (this, tryStmt);
3315 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3316 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3317 ilGen.Emit (tryStmt, OpCodes.Ldstr, " finCallNo=");
3318 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3319 finCallNo.PushVal (this, tryStmt);
3320 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3321 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3322 ilGen.Emit (tryStmt, OpCodes.Ldstr, " catThrown.IsNull=");
3323 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3324 catThrown.PushVal (this, tryStmt);
3325 ilGen.Emit (tryStmt, OpCodes.Ldnull);
3326 ilGen.Emit (tryStmt, OpCodes.Ceq);
3327 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3328 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3329 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3330 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3331 }
3332
3333 GetCallNo (tryStmt, tryCallNo); // if (__tryCallNo >= 0) goto tryCallSw;
3334 ilGen.Emit (tryStmt, OpCodes.Ldc_I4_0);
3335 ilGen.Emit (tryStmt, OpCodes.Bge, tryCallSw);
3336
3337 actCallNo = tryCallNo.localBuilder; // set up __tryCallNo for call labels
3338 actCallLabels = new LinkedList<CallLabel> ();
3339
3340 GenerateStmtBlock (tryStmt.tryStmt); // output the try block statement subblock
3341
3342 if (mightGetHere) {
3343 new CallLabel (this, tryStmt); // <newCallLabel>:
3344 ilGen.Emit (tryStmt, OpCodes.Leave, tryEnd); // leave tryEnd;
3345 openCallLabel = null;
3346 }
3347
3348 foreach (IntermediateLeave iLeave in tryStmt.iLeaves.Values) {
3349 ilGen.MarkLabel (iLeave.jumpIntoLabel); // intr2_exit:
3350 new CallLabel (this, tryStmt); // tryCallNo = n;
3351 ilGen.Emit (tryStmt, OpCodes.Leave, iLeave.jumpAwayLabel); // __callNo_n_: leave int1_exit;
3352 openCallLabel = null;
3353 }
3354
3355 CallLabel tryThrow = new CallLabel (this, tryStmt); // tryThrow:<tryCallLabel>:
3356 if (DEBUG_TRYSTMT) {
3357 ilGen.Emit (tryStmt, OpCodes.Ldstr, "tryThrow*: " + tryStmt.line + " catThrown=");
3358 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3359 catThrown.PushVal (this, tryStmt);
3360 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3361 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3362 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3363 }
3364 catThrown.PushVal (this, tryStmt); // throw new ScriptRestoreCatchException (__catThrown);
3365 ilGen.Emit (tryStmt, OpCodes.Newobj, scriptRestoreCatchExceptionConstructorInfo);
3366 ilGen.Emit (tryStmt, OpCodes.Throw);
3367 openCallLabel = null;
3368
3369 ilGen.MarkLabel (tryCallSw); // tryCallSw:
3370 OutputCallNoSwitchStmt (); // switch (tryCallNo) ...
3371 // }
3372
3373 ilGen.BeginCatchBlock (typeof (Exception)); // start of the catch block that can catch any exception
3374 if (DEBUG_TRYSTMT) {
3375 ilGen.Emit (tryStmt, OpCodes.Ldstr, "enter catch*: " + tryStmt.line + " callMode=");
3376 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3377 PushXMRInst ();
3378 ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
3379 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3380 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3381 ilGen.Emit (tryStmt, OpCodes.Ldstr, " exc=");
3382 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3383 ilGen.Emit (tryStmt, OpCodes.Dup);
3384 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3385 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3386 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3387 }
3388 ilGen.Emit (tryStmt, OpCodes.Call, scriptRestoreCatchExceptionUnwrap); // exc = ScriptRestoreCatchException.Unwrap (exc);
3389 PushXMRInst (); // if (callMode == CallMode_SAVE) goto catchRetro;
3390 ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
3391 ilGen.Emit (tryStmt, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_SAVE);
3392 ilGen.Emit (tryStmt, OpCodes.Beq, catchRetro);
3393
3394 catThrown.Pop (this, tryStmt); // __catThrown = exc;
3395 SetCallNo (tryStmt, tryCallNo, tryThrow.index); // __tryCallNo = tryThrow:<tryCallLabel>;
3396 ilGen.Emit (tryStmt, OpCodes.Rethrow);
3397
3398 ilGen.MarkLabel (catchRetro); // catchRetro:
3399 ilGen.Emit (tryStmt, OpCodes.Pop);
3400 ilGen.Emit (tryStmt, OpCodes.Rethrow); // rethrow;
3401
3402 ilGen.EndExceptionBlock (); // }
3403
3404 ilGen.BeginFinallyBlock (); // start of the finally block
3405
3406 PushXMRInst (); // if (callMode == CallMode_SAVE) goto finEnd;
3407 ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
3408 ilGen.Emit (tryStmt, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_SAVE);
3409 ilGen.Emit (tryStmt, OpCodes.Beq, finEnd.label);
3410
3411 GetCallNo (tryStmt, finCallNo); // if (__finCallNo >= 0) goto finCallSw;
3412 ilGen.Emit (tryStmt, OpCodes.Ldc_I4_0);
3413 ilGen.Emit (tryStmt, OpCodes.Bge, finCallSw);
3414
3415 actCallNo = finCallNo.localBuilder; // set up __finCallNo for call labels
3416 actCallLabels.Clear ();
3417 mightGetHere = true; // if we can get to the 'try' assume we can get to the 'finally'
3418 GenerateStmtBlock (tryStmt.finallyStmt); // output finally clause statement subblock
3419
3420 ilGen.MarkLabel (finEnd.label); // finEnd:
3421 ilGen.Emit (tryStmt, OpCodes.Endfinally); // return out to next finally { } or catch { } or leave target
3422
3423 ilGen.MarkLabel (finCallSw); // restore mode, switch (finCallNo) ...
3424 OutputCallNoSwitchStmt ();
3425
3426 ilGen.EndExceptionBlock ();
3427 ilGen.MarkLabel (tryEnd);
3428
3429 mightGetHere |= finEnd.used; // get here if finally body falls through or has a break statement
3430 }
3431
3432 /**
3433 * @brief Generate code to initialize a variable to its default value.
3434 */
3435 private void GenerateStmtVarIniDef (TokenStmtVarIniDef varIniDefStmt)
3436 {
3437 if (!mightGetHere) return;
3438
3439 CompValu left = GenerateFromLVal (varIniDefStmt.var);
3440 left.PopPre (this, varIniDefStmt);
3441 PushDefaultValue (left.type);
3442 left.PopPost (this, varIniDefStmt);
3443 }
3444
3445 /**
3446 * @brief generate code for a 'while' statement including the loop body.
3447 */
3448 private void GenerateStmtWhile (TokenStmtWhile whileStmt)
3449 {
3450 if (!mightGetHere) return;
3451
3452 BreakContTarg oldBreakTarg = curBreakTarg;
3453 BreakContTarg oldContTarg = curContTarg;
3454 ScriptMyLabel loopLabel = ilGen.DefineLabel ("whileloop_" + whileStmt.Unique);
3455
3456 curBreakTarg = new BreakContTarg (this, "whilebreak_" + whileStmt.Unique);
3457 curContTarg = new BreakContTarg (this, "whilecont_" + whileStmt.Unique);
3458
3459 ilGen.MarkLabel (loopLabel); // loop:
3460 CompValu testRVal = GenerateFromRVal (whileStmt.testRVal); // testRVal = while test expression
3461 if (!IsConstBoolExprTrue (testRVal)) {
3462 testRVal.PushVal (this, whileStmt.testRVal, tokenTypeBool); // if (!testRVal)
3463 ilGen.Emit (whileStmt, OpCodes.Brfalse, curBreakTarg.label); // goto break
3464 curBreakTarg.used = true;
3465 }
3466 GenerateStmt (whileStmt.bodyStmt); // while body statement
3467 if (curContTarg.used) {
3468 ilGen.MarkLabel (curContTarg.label); // cont:
3469 mightGetHere = true;
3470 }
3471 if (mightGetHere) {
3472 EmitCallCheckRun (whileStmt, false); // __sw.CheckRun()
3473 ilGen.Emit (whileStmt, OpCodes.Br, loopLabel); // goto loop
3474 }
3475 mightGetHere = curBreakTarg.used;
3476 if (mightGetHere) {
3477 ilGen.MarkLabel (curBreakTarg.label); // done:
3478 }
3479
3480 curBreakTarg = oldBreakTarg;
3481 curContTarg = oldContTarg;
3482 }
3483
3484 /**
3485 * @brief process a local variable declaration statement, possibly with initialization expression.
3486 * Note that the function header processing allocated stack space (CompValuTemp) for the
3487 * variable and now all we do is write its initialization value.
3488 */
3489 private void GenerateDeclVar (TokenDeclVar declVar)
3490 {
3491 /*
3492 * Script gave us an initialization value, so just store init value in var like an assignment statement.
3493 * If no init given, set it to its default value.
3494 */
3495 CompValu local = declVar.location;
3496 if (declVar.init != null) {
3497 CompValu rVal = GenerateFromRVal (declVar.init, local.GetArgTypes ());
3498 local.PopPre (this, declVar);
3499 rVal.PushVal (this, declVar.init, declVar.type);
3500 local.PopPost (this, declVar);
3501 } else {
3502 local.PopPre (this, declVar);
3503 PushDefaultValue (declVar.type);
3504 local.PopPost (this, declVar);
3505 }
3506 }
3507
3508 /**
3509 * @brief Get the type and location of an L-value (eg, variable)
3510 * @param lVal = L-value expression to evaluate
3511 * @param argsig = null: it's a field/property
3512 * else: select overload method that fits these arg types
3513 */
3514 private CompValu GenerateFromLVal (TokenLVal lVal)
3515 {
3516 return GenerateFromLVal (lVal, null);
3517 }
3518 private CompValu GenerateFromLVal (TokenLVal lVal, TokenType[] argsig)
3519 {
3520 if (lVal is TokenLValArEle) return GenerateFromLValArEle ((TokenLValArEle)lVal);
3521 if (lVal is TokenLValBaseField) return GenerateFromLValBaseField ((TokenLValBaseField)lVal, argsig);
3522 if (lVal is TokenLValIField) return GenerateFromLValIField ((TokenLValIField)lVal, argsig);
3523 if (lVal is TokenLValName) return GenerateFromLValName ((TokenLValName)lVal, argsig);
3524 if (lVal is TokenLValSField) return GenerateFromLValSField ((TokenLValSField)lVal, argsig);
3525 throw new Exception ("bad lval class");
3526 }
3527
3528 /**
3529 * @brief we have an L-value token that is an element within an array.
3530 * @returns a CompValu giving the type and location of the element of the array.
3531 */
3532 private CompValu GenerateFromLValArEle (TokenLValArEle lVal)
3533 {
3534 CompValu subCompValu;
3535
3536 /*
3537 * Compute location of array itself.
3538 */
3539 CompValu baseCompValu = GenerateFromRVal (lVal.baseRVal);
3540
3541 /*
3542 * Maybe it is a fixed array access.
3543 */
3544 string basetypestring = baseCompValu.type.ToString ();
3545 if (basetypestring.EndsWith ("]")) {
3546 TokenRVal subRVal = lVal.subRVal;
3547 int nSubs = 1;
3548 if (subRVal is TokenRValList) {
3549 nSubs = ((TokenRValList)subRVal).nItems;
3550 subRVal = ((TokenRValList)subRVal).rVal;
3551 }
3552
3553 int rank = basetypestring.IndexOf (']') - basetypestring.IndexOf ('[');
3554 if (nSubs != rank) {
3555 ErrorMsg (lVal.baseRVal, "expect " + rank + " subscript" + ((rank == 1) ? "" : "s") + " but have " + nSubs);
3556 }
3557 CompValu[] subCompValus = new CompValu[rank];
3558 int i;
3559 for (i = 0; (subRVal != null) && (i < rank); i ++) {
3560 subCompValus[i] = GenerateFromRVal (subRVal);
3561 subRVal = (TokenRVal)subRVal.nextToken;
3562 }
3563 while (i < rank) subCompValus[i++] = new CompValuInteger (new TokenTypeInt (lVal.subRVal), 0);
3564 return new CompValuFixArEl (this, baseCompValu, subCompValus);
3565 }
3566
3567 /*
3568 * Maybe it is accessing the $idxprop property of a script-defined class.
3569 */
3570 if (baseCompValu.type is TokenTypeSDTypeClass) {
3571 TokenName name = new TokenName (lVal, "$idxprop");
3572 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseCompValu.type;
3573 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
3574 TokenDeclVar idxProp = FindThisMember (sdtDecl, name, null);
3575 if (idxProp == null) {
3576 ErrorMsg (lVal, "no index property in class " + sdtDecl.longName.val);
3577 return new CompValuVoid (lVal);
3578 }
3579 if ((idxProp.sdtFlags & ScriptReduce.SDT_STATIC) != 0) {
3580 ErrorMsg (lVal, "non-static reference to static member " + idxProp.name.val);
3581 return new CompValuVoid (idxProp);
3582 }
3583 CheckAccess (idxProp, name);
3584
3585 TokenType[] argTypes = IdxPropArgTypes (idxProp);
3586 CompValu[] compValus = IdxPropCompValus (lVal, argTypes.Length);
3587 return new CompValuIdxProp (idxProp, baseCompValu, argTypes, compValus);
3588
3589 }
3590
3591 /*
3592 * Maybe they are accessing $idxprop property of a script-defined interface.
3593 */
3594 if (baseCompValu.type is TokenTypeSDTypeInterface) {
3595 TokenName name = new TokenName (lVal, "$idxprop");
3596 TokenTypeSDTypeInterface sdtType = (TokenTypeSDTypeInterface)baseCompValu.type;
3597 TokenDeclVar idxProp = FindInterfaceMember (sdtType, name, null, ref baseCompValu);
3598 if (idxProp == null) {
3599 ErrorMsg (lVal, "no index property defined for interface " + sdtType.decl.longName.val);
3600 return baseCompValu;
3601 }
3602
3603 TokenType[] argTypes = IdxPropArgTypes (idxProp);
3604 CompValu[] compValus = IdxPropCompValus (lVal, argTypes.Length);
3605 return new CompValuIdxProp (idxProp, baseCompValu, argTypes, compValus);
3606 }
3607
3608 /*
3609 * Maybe it is extracting a character from a string.
3610 */
3611 if ((baseCompValu.type is TokenTypeKey) || (baseCompValu.type is TokenTypeStr)) {
3612 subCompValu = GenerateFromRVal (lVal.subRVal);
3613 return new CompValuStrChr (new TokenTypeChar (lVal), baseCompValu, subCompValu);
3614 }
3615
3616 /*
3617 * Maybe it is extracting an element from a list.
3618 */
3619 if (baseCompValu.type is TokenTypeList) {
3620 subCompValu = GenerateFromRVal (lVal.subRVal);
3621 return new CompValuListEl (new TokenTypeObject (lVal), baseCompValu, subCompValu);
3622 }
3623
3624 /*
3625 * Access should be to XMR_Array otherwise.
3626 */
3627 if (!(baseCompValu.type is TokenTypeArray)) {
3628 ErrorMsg (lVal, "taking subscript of non-array");
3629 return baseCompValu;
3630 }
3631 subCompValu = GenerateFromRVal (lVal.subRVal);
3632 return new CompValuArEle (new TokenTypeObject (lVal), baseCompValu, subCompValu);
3633 }
3634
3635 /**
3636 * @brief Get number and type of arguments required by an index property.
3637 */
3638 private static TokenType[] IdxPropArgTypes (TokenDeclVar idxProp)
3639 {
3640 TokenType[] argTypes;
3641 if (idxProp.getProp != null) {
3642 int nArgs = idxProp.getProp.argDecl.varDict.Count;
3643 argTypes = new TokenType[nArgs];
3644 foreach (TokenDeclVar var in idxProp.getProp.argDecl.varDict) {
3645 argTypes[var.vTableIndex] = var.type;
3646 }
3647 } else {
3648 int nArgs = idxProp.setProp.argDecl.varDict.Count - 1;
3649 argTypes = new TokenType[nArgs];
3650 foreach (TokenDeclVar var in idxProp.setProp.argDecl.varDict) {
3651 if (var.vTableIndex < nArgs) {
3652 argTypes[var.vTableIndex] = var.type;
3653 }
3654 }
3655 }
3656 return argTypes;
3657 }
3658
3659 /**
3660 * @brief Get number and computed value of index property arguments.
3661 * @param lVal = list of arguments
3662 * @param nArgs = number of arguments required
3663 * @returns null: argument count mismatch
3664 * else: array of index property argument values
3665 */
3666 private CompValu[] IdxPropCompValus (TokenLValArEle lVal, int nArgs)
3667 {
3668 TokenRVal subRVal = lVal.subRVal;
3669 int nSubs = 1;
3670 if (subRVal is TokenRValList) {
3671 nSubs = ((TokenRValList)subRVal).nItems;
3672 subRVal = ((TokenRValList)subRVal).rVal;
3673 }
3674
3675 if (nSubs != nArgs) {
3676 ErrorMsg (lVal, "index property requires " + nArgs + " subscript(s)");
3677 return null;
3678 }
3679
3680 CompValu[] subCompValus = new CompValu[nArgs];
3681 for (int i = 0; i < nArgs; i ++) {
3682 subCompValus[i] = GenerateFromRVal (subRVal);
3683 subRVal = (TokenRVal)subRVal.nextToken;
3684 }
3685 return subCompValus;
3686 }
3687
3688 /**
3689 * @brief using 'base' within a script-defined instance method to refer to an instance field/method
3690 * of the class being extended.
3691 */
3692 private CompValu GenerateFromLValBaseField (TokenLValBaseField baseField, TokenType[] argsig)
3693 {
3694 string fieldName = baseField.fieldName.val;
3695
3696 TokenDeclSDType sdtDecl = curDeclFunc.sdtClass;
3697 if ((sdtDecl == null) || ((curDeclFunc.sdtFlags & ScriptReduce.SDT_STATIC) != 0)) {
3698 ErrorMsg (baseField, "cannot use 'base' outside instance method body");
3699 return new CompValuVoid (baseField);
3700 }
3701 if (!IsSDTInstMethod ()) {
3702 ErrorMsg (baseField, "cannot access instance member of base class from static method");
3703 return new CompValuVoid (baseField);
3704 }
3705
3706 TokenDeclVar declVar = FindThisMember (sdtDecl.extends, baseField.fieldName, argsig);
3707 if (declVar != null) {
3708 CheckAccess (declVar, baseField.fieldName);
3709 TokenType baseType = declVar.sdtClass.MakeRefToken (baseField);
3710 CompValu basePtr = new CompValuArg (baseType, 0);
3711 return AccessInstanceMember (declVar, basePtr, baseField, true);
3712 }
3713
3714 ErrorMsg (baseField, "no member " + fieldName + ArgSigString (argsig) + " rootward of " + sdtDecl.longName.val);
3715 return new CompValuVoid (baseField);
3716 }
3717
3718 /**
3719 * @brief We have an L-value token that is an instance field/method within a struct.
3720 * @returns a CompValu giving the type and location of the field/method in the struct.
3721 */
3722 private CompValu GenerateFromLValIField (TokenLValIField lVal, TokenType[] argsig)
3723 {
3724 CompValu baseRVal = GenerateFromRVal (lVal.baseRVal);
3725 string fieldName = lVal.fieldName.val + ArgSigString (argsig);
3726
3727 /*
3728 * Maybe they are accessing an instance field, method or property of a script-defined class.
3729 */
3730 if (baseRVal.type is TokenTypeSDTypeClass) {
3731 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseRVal.type;
3732 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
3733 TokenDeclVar declVar = FindThisMember (sdtDecl, lVal.fieldName, argsig);
3734 if (declVar != null) {
3735 CheckAccess (declVar, lVal.fieldName);
3736 return AccessInstanceMember (declVar, baseRVal, lVal, false);
3737 }
3738 ErrorMsg (lVal.fieldName, "no member " + fieldName + " in class " + sdtDecl.longName.val);
3739 return new CompValuVoid (lVal.fieldName);
3740 }
3741
3742 /*
3743 * Maybe they are accessing a method or property of a script-defined interface.
3744 */
3745 if (baseRVal.type is TokenTypeSDTypeInterface) {
3746 TokenTypeSDTypeInterface sdtType = (TokenTypeSDTypeInterface)baseRVal.type;
3747 TokenDeclVar declVar = FindInterfaceMember (sdtType, lVal.fieldName, argsig, ref baseRVal);
3748 if (declVar != null) {
3749 return new CompValuIntfMember (declVar, baseRVal);
3750 }
3751 ErrorMsg (lVal.fieldName, "no member " + fieldName + " in interface " + sdtType.decl.longName.val);
3752 return new CompValuVoid (lVal.fieldName);
3753 }
3754
3755 /*
3756 * Since we only have a few built-in types with fields, just pound them out.
3757 */
3758 if (baseRVal.type is TokenTypeArray) {
3759
3760 // no arguments, no parentheses, just the field name, returning integer
3761 // but internally, it is a call to a method()
3762 if (fieldName == "count") {
3763 return new CompValuIntInstROProp (tokenTypeInt, baseRVal, arrayCountMethodInfo);
3764 }
3765
3766 // no arguments but with the parentheses, returning void
3767 if (fieldName == "clear()") {
3768 return new CompValuIntInstMeth (XMR_Array.clearDelegate, baseRVal, arrayClearMethodInfo);
3769 }
3770
3771 // single integer argument, returning an object
3772 if (fieldName == "index(integer)") {
3773 return new CompValuIntInstMeth (XMR_Array.indexDelegate, baseRVal, arrayIndexMethodInfo);
3774 }
3775 if (fieldName == "value(integer)") {
3776 return new CompValuIntInstMeth (XMR_Array.valueDelegate, baseRVal, arrayValueMethodInfo);
3777 }
3778 }
3779 if (baseRVal.type is TokenTypeRot) {
3780 FieldInfo fi = null;
3781 if (fieldName == "x") fi = rotationXFieldInfo;
3782 if (fieldName == "y") fi = rotationYFieldInfo;
3783 if (fieldName == "z") fi = rotationZFieldInfo;
3784 if (fieldName == "s") fi = rotationSFieldInfo;
3785 if (fi != null) {
3786 return new CompValuField (new TokenTypeFloat (lVal), baseRVal, fi);
3787 }
3788 }
3789 if (baseRVal.type is TokenTypeVec) {
3790 FieldInfo fi = null;
3791 if (fieldName == "x") fi = vectorXFieldInfo;
3792 if (fieldName == "y") fi = vectorYFieldInfo;
3793 if (fieldName == "z") fi = vectorZFieldInfo;
3794 if (fi != null) {
3795 return new CompValuField (new TokenTypeFloat (lVal), baseRVal, fi);
3796 }
3797 }
3798
3799 ErrorMsg (lVal, "type " + baseRVal.type.ToString () + " does not define member " + fieldName);
3800 return baseRVal;
3801 }
3802
3803 /**
3804 * @brief We have an L-value token that is a function, method or variable name.
3805 * @param lVal = name we are looking for
3806 * @param argsig = null: just look for name as a variable
3807 * else: look for name as a function/method being called with the given argument types
3808 * eg, "(string,integer,list)"
3809 * @returns a CompValu giving the type and location of the function, method or variable.
3810 */
3811 private CompValu GenerateFromLValName (TokenLValName lVal, TokenType[] argsig)
3812 {
3813 /*
3814 * Look in variable stack then look for built-in constants and functions.
3815 */
3816 TokenDeclVar var = FindNamedVar (lVal, argsig);
3817 if (var == null) {
3818 ErrorMsg (lVal, "undefined constant/function/variable " + lVal.name.val + ArgSigString (argsig));
3819 return new CompValuVoid (lVal);
3820 }
3821
3822 /*
3823 * Maybe it has an implied 'this.' on the front.
3824 */
3825 if ((var.sdtClass != null) && ((var.sdtFlags & ScriptReduce.SDT_STATIC) == 0)) {
3826
3827 if (!IsSDTInstMethod ()) {
3828 ErrorMsg (lVal, "cannot access instance member of class from static method");
3829 return new CompValuVoid (lVal);
3830 }
3831
3832 /*
3833 * Don't allow something such as:
3834 *
3835 * class A {
3836 * integer I;
3837 * class B {
3838 * Print ()
3839 * {
3840 * llOwnerSay ("I=" + (string)I); <- access to I not allowed inside class B.
3841 * explicit reference required as we don't
3842 * have a valid reference to class A.
3843 * }
3844 * }
3845 * }
3846 *
3847 * But do allow something such as:
3848 *
3849 * class A {
3850 * integer I;
3851 * }
3852 * class B : A {
3853 * Print ()
3854 * {
3855 * llOwnerSay ("I=" + (string)I);
3856 * }
3857 * }
3858 */
3859 for (TokenDeclSDType c = curDeclFunc.sdtClass; c != var.sdtClass; c = c.extends) {
3860 if (c == null) {
3861 // our arg0 points to an instance of curDeclFunc.sdtClass, not var.sdtClass
3862 ErrorMsg (lVal, "cannot access instance member of outer class with implied 'this'");
3863 break;
3864 }
3865 }
3866
3867 CompValu thisCompValu = new CompValuArg (var.sdtClass.MakeRefToken (lVal), 0);
3868 return AccessInstanceMember (var, thisCompValu, lVal, false);
3869 }
3870
3871 /*
3872 * It's a local variable, static field, global, constant, etc.
3873 */
3874 return var.location;
3875 }
3876
3877 /**
3878 * @brief Access a script-defined type's instance member
3879 * @param declVar = which member (field,method,property) to access
3880 * @param basePtr = points to particular object instance
3881 * @param ignoreVirt = true: access declVar's method directly; else: maybe use vTable
3882 * @returns where the field/method/property is located
3883 */
3884 private CompValu AccessInstanceMember (TokenDeclVar declVar, CompValu basePtr, Token errorAt, bool ignoreVirt)
3885 {
3886 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) {
3887 ErrorMsg (errorAt, "non-static reference to static member " + declVar.name.val);
3888 return new CompValuVoid (declVar);
3889 }
3890 return new CompValuInstMember (declVar, basePtr, ignoreVirt);
3891 }
3892
3893 /**
3894 * @brief we have an L-value token that is a static member within a struct.
3895 * @returns a CompValu giving the type and location of the member in the struct.
3896 */
3897 private CompValu GenerateFromLValSField (TokenLValSField lVal, TokenType[] argsig)
3898 {
3899 TokenType stType = lVal.baseType;
3900 string fieldName = lVal.fieldName.val + ArgSigString (argsig);
3901
3902 /*
3903 * Maybe they are accessing a static member of a script-defined class.
3904 */
3905 if (stType is TokenTypeSDTypeClass) {
3906 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)stType;
3907 TokenDeclVar declVar = FindThisMember (sdtType.decl, lVal.fieldName, argsig);
3908 if (declVar != null) {
3909 CheckAccess (declVar, lVal.fieldName);
3910 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) == 0) {
3911 ErrorMsg (lVal.fieldName, "static reference to non-static member " + fieldName);
3912 return new CompValuVoid (lVal.fieldName);
3913 }
3914 return declVar.location;
3915 }
3916 }
3917
3918 ErrorMsg (lVal.fieldName, "no member " + fieldName + " in " + stType.ToString ());
3919 return new CompValuVoid (lVal.fieldName);
3920 }
3921
3922 /**
3923 * @brief generate code from an RVal expression and return its type and where the result is stored.
3924 * For anything that has side-effects, statements are generated that perform the computation then
3925 * the result it put in a temp var and the temp var name is returned.
3926 * For anything without side-effects, they are returned as an equivalent sequence of Emits.
3927 * @param rVal = rVal token to be evaluated
3928 * @param argsig = null: not being used in an function/method context
3929 * else: string giving argument types, eg, "(string,integer,list,vector)"
3930 * that can be used to select among overloaded methods
3931 * @returns resultant type and location
3932 */
3933 private CompValu GenerateFromRVal (TokenRVal rVal)
3934 {
3935 return GenerateFromRVal (rVal, null);
3936 }
3937 private CompValu GenerateFromRVal (TokenRVal rVal, TokenType[] argsig)
3938 {
3939 errorMessageToken = rVal;
3940
3941 /*
3942 * Maybe the expression can be converted to a constant.
3943 */
3944 bool didOne;
3945 do {
3946 didOne = false;
3947 rVal = rVal.TryComputeConstant (LookupBodyConstants, ref didOne);
3948 } while (didOne);
3949
3950 /*
3951 * Generate code for the computation and return resulting type and location.
3952 */
3953 CompValu cVal = null;
3954 if (rVal is TokenRValAsnPost) cVal = GenerateFromRValAsnPost ((TokenRValAsnPost)rVal);
3955 if (rVal is TokenRValAsnPre) cVal = GenerateFromRValAsnPre ((TokenRValAsnPre)rVal);
3956 if (rVal is TokenRValCall) cVal = GenerateFromRValCall ((TokenRValCall)rVal);
3957 if (rVal is TokenRValCast) cVal = GenerateFromRValCast ((TokenRValCast)rVal);
3958 if (rVal is TokenRValCondExpr) cVal = GenerateFromRValCondExpr ((TokenRValCondExpr)rVal);
3959 if (rVal is TokenRValConst) cVal = GenerateFromRValConst ((TokenRValConst)rVal);
3960 if (rVal is TokenRValInitDef) cVal = GenerateFromRValInitDef ((TokenRValInitDef)rVal);
3961 if (rVal is TokenRValIsType) cVal = GenerateFromRValIsType ((TokenRValIsType)rVal);
3962 if (rVal is TokenRValList) cVal = GenerateFromRValList ((TokenRValList)rVal);
3963 if (rVal is TokenRValNewArIni) cVal = GenerateFromRValNewArIni ((TokenRValNewArIni)rVal);
3964 if (rVal is TokenRValOpBin) cVal = GenerateFromRValOpBin ((TokenRValOpBin)rVal);
3965 if (rVal is TokenRValOpUn) cVal = GenerateFromRValOpUn ((TokenRValOpUn)rVal);
3966 if (rVal is TokenRValParen) cVal = GenerateFromRValParen ((TokenRValParen)rVal);
3967 if (rVal is TokenRValRot) cVal = GenerateFromRValRot ((TokenRValRot)rVal);
3968 if (rVal is TokenRValThis) cVal = GenerateFromRValThis ((TokenRValThis)rVal);
3969 if (rVal is TokenRValUndef) cVal = GenerateFromRValUndef ((TokenRValUndef)rVal);
3970 if (rVal is TokenRValVec) cVal = GenerateFromRValVec ((TokenRValVec)rVal);
3971 if (rVal is TokenLVal) cVal = GenerateFromLVal ((TokenLVal)rVal, argsig);
3972
3973 if (cVal == null) throw new Exception ("bad rval class " + rVal.GetType ().ToString ());
3974
3975 /*
3976 * Sanity check.
3977 */
3978 if (!youveAnError) {
3979 if (cVal.type == null) throw new Exception ("cVal has no type " + cVal.GetType ());
3980 string cValType = cVal.type.ToString ();
3981 string rValType = rVal.GetRValType (this, argsig).ToString ();
3982 if (cValType == "bool") cValType = "integer";
3983 if (rValType == "bool") rValType = "integer";
3984 if (cValType != rValType) {
3985 throw new Exception ("cVal.type " + cValType + " != rVal.type " + rValType +
3986 " (" + rVal.GetType ().Name + " " + rVal.SrcLoc + ")");
3987 }
3988 }
3989
3990 return cVal;
3991 }
3992
3993 /**
3994 * @brief compute the result of a binary operator (eg, add, subtract, multiply, lessthan)
3995 * @param token = binary operator token, includes the left and right operands
3996 * @returns where the resultant R-value is as something that doesn't have side effects
3997 */
3998 private CompValu GenerateFromRValOpBin (TokenRValOpBin token)
3999 {
4000 CompValu left, right;
4001 string opcodeIndex = token.opcode.ToString ();
4002
4003 /*
4004 * Comma operators are special, as they say to compute the left-hand value and
4005 * discard it, then compute the right-hand argument and that is the result.
4006 */
4007 if (opcodeIndex == ",") {
4008
4009 /*
4010 * Compute left-hand operand but throw away result.
4011 */
4012 GenerateFromRVal (token.rValLeft);
4013
4014 /*
4015 * Compute right-hand operand and that is the value of the expression.
4016 */
4017 return GenerateFromRVal (token.rValRight);
4018 }
4019
4020 /*
4021 * Simple overwriting assignments are their own special case,
4022 * as we want to cast the R-value to the type of the L-value.
4023 * And in the case of delegates, we want to use the arg signature
4024 * of the delegate to select which overloaded method to use.
4025 */
4026 if (opcodeIndex == "=") {
4027 if (!(token.rValLeft is TokenLVal)) {
4028 ErrorMsg (token, "invalid L-value for =");
4029 return GenerateFromRVal (token.rValLeft);
4030 }
4031 left = GenerateFromLVal ((TokenLVal)token.rValLeft);
4032 right = Trivialize (GenerateFromRVal (token.rValRight, left.GetArgTypes ()), token.rValRight);
4033 left.PopPre (this, token.rValLeft);
4034 right.PushVal (this, token.rValRight, left.type); // push (left.type)right
4035 left.PopPost (this, token.rValLeft); // pop to left
4036 return left;
4037 }
4038
4039 /*
4040 * There are String.Concat() methods available for 2, 3 and 4 operands.
4041 * So see if we have a string concat op and optimize if so.
4042 */
4043 if ((opcodeIndex == "+") ||
4044 ((opcodeIndex == "+=") &&
4045 (token.rValLeft is TokenLVal) &&
4046 (token.rValLeft.GetRValType (this, null) is TokenTypeStr))) {
4047
4048 /*
4049 * We are adding something. Maybe it's a bunch of strings together.
4050 */
4051 List<TokenRVal> scorvs = new List<TokenRVal> ();
4052 if (StringConcatOperands (token.rValLeft, token.rValRight, scorvs, token.opcode)) {
4053
4054 /*
4055 * Evaluate all the operands, right-to-left on purpose per LSL scripting.
4056 */
4057 int i;
4058 int n = scorvs.Count;
4059 CompValu[] scocvs = new CompValu[n];
4060 for (i = n; -- i >= 0;) {
4061 scocvs[i] = GenerateFromRVal (scorvs[i]);
4062 if (i > 0) scocvs[i] = Trivialize (scocvs[i], scorvs[i]);
4063 }
4064
4065 /*
4066 * Figure out where to put the result.
4067 * A temp if '+', or back in original L-value if '+='.
4068 */
4069 CompValu retcv;
4070 if (opcodeIndex == "+") {
4071 retcv = new CompValuTemp (new TokenTypeStr (token.opcode), this);
4072 } else {
4073 retcv = GenerateFromLVal ((TokenLVal)token.rValLeft);
4074 }
4075 retcv.PopPre (this, token);
4076
4077 /*
4078 * Call the String.Concat() methods, passing operands in left-to-right order.
4079 * Force a cast to string (retcv.type) for each operand.
4080 */
4081 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4082 while (i + 3 < n) {
4083 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4084 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4085 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4086 ilGen.Emit (scorvs[i], OpCodes.Call, stringConcat4MethodInfo);
4087 }
4088 if (i + 2 < n) {
4089 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4090 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4091 ilGen.Emit (scorvs[i], OpCodes.Call, stringConcat3MethodInfo);
4092 }
4093 if (i + 1 < n) {
4094 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4095 ilGen.Emit (scorvs[i], OpCodes.Call, stringConcat2MethodInfo);
4096 }
4097
4098 /*
4099 * Put the result where we want it and return where we put it.
4100 */
4101 retcv.PopPost (this, token);
4102 return retcv;
4103 }
4104 }
4105
4106 /*
4107 * If "&&&", it is a short-circuiting AND.
4108 * Compute left-hand operand and if true, compute right-hand operand.
4109 */
4110 if (opcodeIndex == "&&&") {
4111 bool leftVal, rightVal;
4112 left = GenerateFromRVal (token.rValLeft);
4113 if (!IsConstBoolExpr (left, out leftVal)) {
4114 ScriptMyLabel falseLabel = ilGen.DefineLabel ("ssandfalse");
4115 left.PushVal (this, tokenTypeBool);
4116 ilGen.Emit (token, OpCodes.Brfalse, falseLabel);
4117 right = GenerateFromRVal (token.rValRight);
4118 if (!IsConstBoolExpr (right, out rightVal)) {
4119 right.PushVal (this, tokenTypeBool);
4120 goto donessand;
4121 }
4122 if (!rightVal) {
4123 ilGen.MarkLabel (falseLabel);
4124 return new CompValuInteger (new TokenTypeInt (token.rValLeft), 0);
4125 }
4126 ilGen.Emit (token, OpCodes.Ldc_I4_1);
4127 donessand:
4128 ScriptMyLabel doneLabel = ilGen.DefineLabel ("ssanddone");
4129 ilGen.Emit (token, OpCodes.Br, doneLabel);
4130 ilGen.MarkLabel (falseLabel);
4131 ilGen.Emit (token, OpCodes.Ldc_I4_0);
4132 ilGen.MarkLabel (doneLabel);
4133 CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
4134 retRVal.Pop (this, token);
4135 return retRVal;
4136 }
4137
4138 if (!leftVal) {
4139 return new CompValuInteger (new TokenTypeInt (token.rValLeft), 0);
4140 }
4141
4142 right = GenerateFromRVal (token.rValRight);
4143 if (!IsConstBoolExpr (right, out rightVal)) {
4144 right.PushVal (this, tokenTypeBool);
4145 CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
4146 retRVal.Pop (this, token);
4147 return retRVal;
4148 }
4149 return new CompValuInteger (new TokenTypeInt (token), rightVal ? 1 : 0);
4150 }
4151
4152 /*
4153 * If "|||", it is a short-circuiting OR.
4154 * Compute left-hand operand and if false, compute right-hand operand.
4155 */
4156 if (opcodeIndex == "|||") {
4157 bool leftVal, rightVal;
4158 left = GenerateFromRVal (token.rValLeft);
4159 if (!IsConstBoolExpr (left, out leftVal)) {
4160 ScriptMyLabel trueLabel = ilGen.DefineLabel ("ssortrue");
4161 left.PushVal (this, tokenTypeBool);
4162 ilGen.Emit (token, OpCodes.Brtrue, trueLabel);
4163 right = GenerateFromRVal (token.rValRight);
4164 if (!IsConstBoolExpr (right, out rightVal)) {
4165 right.PushVal (this, tokenTypeBool);
4166 goto donessor;
4167 }
4168 if (rightVal) {
4169 ilGen.MarkLabel (trueLabel);
4170 return new CompValuInteger (new TokenTypeInt (token.rValLeft), 1);
4171 }
4172 ilGen.Emit (token, OpCodes.Ldc_I4_0);
4173 donessor:
4174 ScriptMyLabel doneLabel = ilGen.DefineLabel ("ssanddone");
4175 ilGen.Emit (token, OpCodes.Br, doneLabel);
4176 ilGen.MarkLabel (trueLabel);
4177 ilGen.Emit (token, OpCodes.Ldc_I4_1);
4178 ilGen.MarkLabel (doneLabel);
4179 CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
4180 retRVal.Pop (this, token);
4181 return retRVal;
4182 }
4183
4184 if (leftVal) {
4185 return new CompValuInteger (new TokenTypeInt (token.rValLeft), 1);
4186 }
4187
4188 right = GenerateFromRVal (token.rValRight);
4189 if (!IsConstBoolExpr (right, out rightVal)) {
4190 right.PushVal (this, tokenTypeBool);
4191 CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
4192 retRVal.Pop (this, token);
4193 return retRVal;
4194 }
4195 return new CompValuInteger (new TokenTypeInt (token), rightVal ? 1 : 0);
4196 }
4197
4198 /*
4199 * Computation of some sort, compute right-hand operand value then left-hand value
4200 * because LSL is supposed to be right-to-left evaluation.
4201 */
4202 right = Trivialize (GenerateFromRVal (token.rValRight), token.rValRight);
4203
4204 /*
4205 * If left is a script-defined class and there is a method with the operator's name,
4206 * convert this to a call to that method with the right value as its single parameter.
4207 * Except don't if the right value is 'undef' so they can always compare to undef.
4208 */
4209 TokenType leftType = token.rValLeft.GetRValType (this, null);
4210 if ((leftType is TokenTypeSDTypeClass) && !(right.type is TokenTypeUndef)) {
4211 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)leftType;
4212 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
4213 TokenType[] argsig = new TokenType[] { right.type };
4214 TokenName funcName = new TokenName (token.opcode, "$op" + opcodeIndex);
4215 TokenDeclVar declFunc = FindThisMember (sdtDecl, funcName, argsig);
4216 if (declFunc != null) {
4217 CheckAccess (declFunc, funcName);
4218 left = GenerateFromRVal (token.rValLeft);
4219 CompValu method = AccessInstanceMember (declFunc, left, token, false);
4220 CompValu[] argRVals = new CompValu[] { right };
4221 return GenerateACall (method, argRVals, token);
4222 }
4223 }
4224
4225 /*
4226 * Formulate key string for binOpStrings = (lefttype)(operator)(righttype)
4227 */
4228 string leftIndex = leftType.ToString ();
4229 string rightIndex = right.type.ToString ();
4230 string key = leftIndex + opcodeIndex + rightIndex;
4231
4232 /*
4233 * If that key exists in table, then the operation is defined between those types
4234 * ... and it produces an R-value of type as given in the table.
4235 */
4236 BinOpStr binOpStr;
4237 if (BinOpStr.defined.TryGetValue (key, out binOpStr)) {
4238
4239 /*
4240 * If table contained an explicit assignment type like +=, output the statement without
4241 * casting the L-value, then return the L-value as the resultant value.
4242 *
4243 * Make sure we don't include comparisons (such as ==, >=, etc).
4244 * Nothing like +=, -=, %=, etc, generate a boolean, only the comparisons.
4245 */
4246 if ((binOpStr.outtype != typeof (bool)) && opcodeIndex.EndsWith ("=") && (opcodeIndex != "!=")) {
4247 if (!(token.rValLeft is TokenLVal)) {
4248 ErrorMsg (token.rValLeft, "invalid L-value");
4249 return GenerateFromRVal (token.rValLeft);
4250 }
4251 left = GenerateFromLVal ((TokenLVal)token.rValLeft);
4252 binOpStr.emitBO (this, token, left, right, left);
4253 return left;
4254 }
4255
4256 /*
4257 * It's of the form left binop right.
4258 * Compute left, perform operation then put result in a temp.
4259 */
4260 left = GenerateFromRVal (token.rValLeft);
4261 CompValu retRVal = new CompValuTemp (TokenType.FromSysType (token.opcode, binOpStr.outtype), this);
4262 binOpStr.emitBO (this, token, left, right, retRVal);
4263 return retRVal;
4264 }
4265
4266 /*
4267 * Nothing in the table, check for comparing object pointers because of the myriad of types possible.
4268 * This will compare list pointers, null pointers, script-defined type pointers, array pointers, etc.
4269 * It will show equal iff the memory addresses are equal and that is good enough.
4270 */
4271 if (!leftType.ToSysType().IsValueType && !right.type.ToSysType().IsValueType && ((opcodeIndex == "==") || (opcodeIndex == "!="))) {
4272 CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
4273 left = GenerateFromRVal (token.rValLeft);
4274 left.PushVal (this, token.rValLeft);
4275 right.PushVal (this, token.rValRight);
4276 ilGen.Emit (token, OpCodes.Ceq);
4277 if (opcodeIndex == "!=") {
4278 ilGen.Emit (token, OpCodes.Ldc_I4_1);
4279 ilGen.Emit (token, OpCodes.Xor);
4280 }
4281 retRVal.Pop (this, token);
4282 return retRVal;
4283 }
4284
4285 /*
4286 * If the opcode ends with "=", it may be something like "+=".
4287 * So look up the key as if we didn't have the "=" to tell us if the operation is legal.
4288 * Also, the binary operation's output type must be the same as the L-value type.
4289 * Likewise, integer += float not allowed because result is float, but float += integer is ok.
4290 */
4291 if (opcodeIndex.EndsWith ("=")) {
4292 key = leftIndex + opcodeIndex.Substring (0, opcodeIndex.Length - 1) + rightIndex;
4293 if (BinOpStr.defined.TryGetValue (key, out binOpStr)) {
4294 if (!(token.rValLeft is TokenLVal)) {
4295 ErrorMsg (token, "invalid L-value for <op>=");
4296 return GenerateFromRVal (token.rValLeft);
4297 }
4298 if (!binOpStr.rmwOK) {
4299 ErrorMsg (token, "<op>= not allowed: " + leftIndex + " " + opcodeIndex + " " + rightIndex);
4300 return new CompValuVoid (token);
4301 }
4302
4303 /*
4304 * Now we know for something like %= that left%right is legal for the types given.
4305 */
4306 left = GenerateFromLVal ((TokenLVal)token.rValLeft);
4307 if (binOpStr.outtype == leftType.ToSysType ()) {
4308 binOpStr.emitBO (this, token, left, right, left);
4309 } else {
4310 CompValu temp = new CompValuTemp (TokenType.FromSysType (token, binOpStr.outtype), this);
4311 binOpStr.emitBO (this, token, left, right, temp);
4312 left.PopPre (this, token);
4313 temp.PushVal (this, token, leftType);
4314 left.PopPost (this, token);
4315 }
4316 return left;
4317 }
4318 }
4319
4320 /*
4321 * Can't find it, oh well.
4322 */
4323 ErrorMsg (token, "op not defined: " + leftIndex + " " + opcodeIndex + " " + rightIndex);
4324 return new CompValuVoid (token);
4325 }
4326
4327 /**
4328 * @brief Queue the given operands to the end of the scos list.
4329 * If it can be broken down into more string concat operands, do so.
4330 * Otherwise, just push it as one operand.
4331 * @param leftRVal = left-hand operand of a '+' operation
4332 * @param rightRVal = right-hand operand of a '+' operation
4333 * @param scos = left-to-right list of operands for the string concat so far
4334 * @param addop = the add operator token (either '+' or '+=')
4335 * @returns false: neither operand is a string, nothing added to scos
4336 * true: scos = updated with leftRVal then rightRVal added onto the end, possibly broken down further
4337 */
4338 private bool StringConcatOperands (TokenRVal leftRVal, TokenRVal rightRVal, List<TokenRVal> scos, TokenKw addop)
4339 {
4340 /*
4341 * If neither operand is a string (eg, float+integer), then the result isn't going to be a string.
4342 */
4343 TokenType leftType = leftRVal.GetRValType (this, null);
4344 TokenType rightType = rightRVal.GetRValType (this, null);
4345 if (!(leftType is TokenTypeStr) && !(rightType is TokenTypeStr)) return false;
4346
4347 /*
4348 * Also, list+string => list so reject that too.
4349 * Also, string+list => list so reject that too.
4350 */
4351 if (leftType is TokenTypeList) return false;
4352 if (rightType is TokenTypeList) return false;
4353
4354 /*
4355 * Append values to the end of the list in left-to-right order.
4356 * If value is formed from a something+something => string,
4357 * push them as separate values, otherwise push as one value.
4358 */
4359 StringConcatOperand (leftType, leftRVal, scos);
4360 StringConcatOperand (rightType, rightRVal, scos);
4361
4362 /*
4363 * Maybe constant strings can be concatted.
4364 */
4365 try {
4366 int len;
4367 while (((len = scos.Count) >= 2) &&
4368 ((leftRVal = scos[len-2]) is TokenRValConst) &&
4369 ((rightRVal = scos[len-1]) is TokenRValConst)) {
4370 object sum = addop.binOpConst (((TokenRValConst)leftRVal).val,
4371 ((TokenRValConst)rightRVal).val);
4372 scos[len-2] = new TokenRValConst (addop, sum);
4373 scos.RemoveAt (len - 1);
4374 }
4375 } catch {
4376 }
4377
4378 /*
4379 * We pushed some string stuff.
4380 */
4381 return true;
4382 }
4383
4384 /**
4385 * @brief Queue the given operand to the end of the scos list.
4386 * If it can be broken down into more string concat operands, do so.
4387 * Otherwise, just push it as one operand.
4388 * @param type = rVal's resultant type
4389 * @param rVal = operand to examine
4390 * @param scos = left-to-right list of operands for the string concat so far
4391 * @returns with scos = updated with rVal added onto the end, possibly broken down further
4392 */
4393 private void StringConcatOperand (TokenType type, TokenRVal rVal, List<TokenRVal> scos)
4394 {
4395 bool didOne;
4396 do {
4397 didOne = false;
4398 rVal = rVal.TryComputeConstant (LookupBodyConstants, ref didOne);
4399 } while (didOne);
4400
4401 if (!(type is TokenTypeStr)) goto pushasis;
4402 if (!(rVal is TokenRValOpBin)) goto pushasis;
4403 TokenRValOpBin rValOpBin = (TokenRValOpBin)rVal;
4404 if (!(rValOpBin.opcode is TokenKwAdd)) goto pushasis;
4405 if (StringConcatOperands (rValOpBin.rValLeft, rValOpBin.rValRight, scos, rValOpBin.opcode)) return;
4406 pushasis:
4407 scos.Add (rVal);
4408 }
4409
4410 /**
4411 * @brief compute the result of an unary operator
4412 * @param token = unary operator token, includes the operand
4413 * @returns where the resultant R-value is
4414 */
4415 private CompValu GenerateFromRValOpUn (TokenRValOpUn token)
4416 {
4417 CompValu inRVal = GenerateFromRVal (token.rVal);
4418
4419 /*
4420 * Script-defined types can define their own methods to handle unary operators.
4421 */
4422 if (inRVal.type is TokenTypeSDTypeClass) {
4423 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)inRVal.type;
4424 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
4425 TokenName funcName = new TokenName (token.opcode, "$op" + token.opcode.ToString ());
4426 TokenDeclVar declFunc = FindThisMember (sdtDecl, funcName, zeroArgs);
4427 if (declFunc != null) {
4428 CheckAccess (declFunc, funcName);
4429 CompValu method = AccessInstanceMember (declFunc, inRVal, token, false);
4430 return GenerateACall (method, zeroCompValus, token);
4431 }
4432 }
4433
4434 /*
4435 * Otherwise use the default.
4436 */
4437 return UnOpGenerate (inRVal, token.opcode);
4438 }
4439
4440 /**
4441 * @brief postfix operator -- this returns the type and location of the resultant value
4442 */
4443 private CompValu GenerateFromRValAsnPost (TokenRValAsnPost asnPost)
4444 {
4445 CompValu lVal = GenerateFromLVal (asnPost.lVal);
4446
4447 /*
4448 * Make up a temp to save original value in.
4449 */
4450 CompValuTemp result = new CompValuTemp (lVal.type, this);
4451
4452 /*
4453 * Prepare to pop incremented value back into variable being incremented.
4454 */
4455 lVal.PopPre (this, asnPost.lVal);
4456
4457 /*
4458 * Copy original value to temp and leave value on stack.
4459 */
4460 lVal.PushVal (this, asnPost.lVal);
4461 ilGen.Emit (asnPost.lVal, OpCodes.Dup);
4462 result.Pop (this, asnPost.lVal);
4463
4464 /*
4465 * Perform the ++/--.
4466 */
4467 if ((lVal.type is TokenTypeChar) || (lVal.type is TokenTypeInt)) {
4468 ilGen.Emit (asnPost, OpCodes.Ldc_I4_1);
4469 } else if (lVal.type is TokenTypeFloat) {
4470 ilGen.Emit (asnPost, OpCodes.Ldc_R4, 1.0f);
4471 } else {
4472 lVal.PopPost (this, asnPost.lVal);
4473 ErrorMsg (asnPost, "invalid type for " + asnPost.postfix.ToString ());
4474 return lVal;
4475 }
4476 switch (asnPost.postfix.ToString ()) {
4477 case "++": {
4478 ilGen.Emit (asnPost, OpCodes.Add);
4479 break;
4480 }
4481 case "--": {
4482 ilGen.Emit (asnPost, OpCodes.Sub);
4483 break;
4484 }
4485 default: throw new Exception ("unknown asnPost op");
4486 }
4487
4488 /*
4489 * Store new value in original variable.
4490 */
4491 lVal.PopPost (this, asnPost.lVal);
4492
4493 return result;
4494 }
4495
4496 /**
4497 * @brief prefix operator -- this returns the type and location of the resultant value
4498 */
4499 private CompValu GenerateFromRValAsnPre (TokenRValAsnPre asnPre)
4500 {
4501 CompValu lVal = GenerateFromLVal (asnPre.lVal);
4502
4503 /*
4504 * Make up a temp to put result in.
4505 */
4506 CompValuTemp result = new CompValuTemp (lVal.type, this);
4507
4508 /*
4509 * Prepare to pop incremented value back into variable being incremented.
4510 */
4511 lVal.PopPre (this, asnPre.lVal);
4512
4513 /*
4514 * Push original value.
4515 */
4516 lVal.PushVal (this, asnPre.lVal);
4517
4518 /*
4519 * Perform the ++/--.
4520 */
4521 if ((lVal.type is TokenTypeChar) || (lVal.type is TokenTypeInt)) {
4522 ilGen.Emit (asnPre, OpCodes.Ldc_I4_1);
4523 } else if (lVal.type is TokenTypeFloat) {
4524 ilGen.Emit (asnPre, OpCodes.Ldc_R4, 1.0f);
4525 } else {
4526 lVal.PopPost (this, asnPre.lVal);
4527 ErrorMsg (asnPre, "invalid type for " + asnPre.prefix.ToString ());
4528 return lVal;
4529 }
4530 switch (asnPre.prefix.ToString ()) {
4531 case "++": {
4532 ilGen.Emit (asnPre, OpCodes.Add);
4533 break;
4534 }
4535 case "--": {
4536 ilGen.Emit (asnPre, OpCodes.Sub);
4537 break;
4538 }
4539 default: throw new Exception ("unknown asnPre op");
4540 }
4541
4542 /*
4543 * Store new value in temp variable, keeping new value on stack.
4544 */
4545 ilGen.Emit (asnPre.lVal, OpCodes.Dup);
4546 result.Pop (this, asnPre.lVal);
4547
4548 /*
4549 * Store new value in original variable.
4550 */
4551 lVal.PopPost (this, asnPre.lVal);
4552
4553 return result;
4554 }
4555
4556 /**
4557 * @brief Generate code that calls a function or object's method.
4558 * @returns where the call's return value is stored (a TokenTypeVoid if void)
4559 */
4560 private CompValu GenerateFromRValCall (TokenRValCall call)
4561 {
4562 CompValu method;
4563 CompValu[] argRVals;
4564 int i, nargs;
4565 TokenRVal arg;
4566 TokenType[] argTypes;
4567
4568 /*
4569 * Compute the values of all the function's call arguments.
4570 * Save where the computation results are in the argRVals[] array.
4571 * Might as well build the argument signature from the argument types, too.
4572 */
4573 nargs = call.nArgs;
4574 argRVals = new CompValu[nargs];
4575 argTypes = new TokenType[nargs];
4576 if (nargs > 0) {
4577 i = 0;
4578 for (arg = call.args; arg != null; arg = (TokenRVal)arg.nextToken) {
4579 argRVals[i] = GenerateFromRVal (arg);
4580 argTypes[i] = argRVals[i].type;
4581 i ++;
4582 }
4583 }
4584
4585 /*
4586 * Get function/method's entrypoint that matches the call argument types.
4587 */
4588 method = GenerateFromRVal (call.meth, argTypes);
4589 if (method == null) return null;
4590
4591 return GenerateACall (method, argRVals, call);
4592 }
4593
4594 /**
4595 * @brief Generate call to a function/method.
4596 * @param method = function/method being called
4597 * @param argVRVals = its call parameters (zero length if none)
4598 * @param call = where in source code call is being made from (for error messages)
4599 * @returns type and location of return value (CompValuVoid if none)
4600 */
4601 private CompValu GenerateACall (CompValu method, CompValu[] argRVals, Token call)
4602 {
4603 CompValuTemp result;
4604 int i, nArgs;
4605 TokenType retType;
4606 TokenType[] argTypes;
4607
4608 /*
4609 * Must be some kind of callable.
4610 */
4611 retType = method.GetRetType (); // TokenTypeVoid if void; null means a variable
4612 if (retType == null) {
4613 ErrorMsg (call, "must be a delegate, function or method");
4614 return new CompValuVoid (call);
4615 }
4616
4617 /*
4618 * Get a location for return value.
4619 */
4620 if (retType is TokenTypeVoid) {
4621 result = new CompValuVoid (call);
4622 } else {
4623 result = new CompValuTemp (retType, this);
4624 }
4625
4626 /*
4627 * Make sure all arguments are trivial, ie, don't involve their own call labels.
4628 * For any that aren't, output code to calculate the arg and put in a temporary.
4629 */
4630 nArgs = argRVals.Length;
4631 for (i = 0; i < nArgs; i ++) {
4632 if (!argRVals[i].IsReadTrivial (this, call)) {
4633 argRVals[i] = Trivialize (argRVals[i], call);
4634 }
4635 }
4636
4637 /*
4638 * Inline functions know how to generate their own call.
4639 */
4640 if (method is CompValuInline) {
4641 CompValuInline inline = (CompValuInline)method;
4642 inline.declInline.CodeGen (this, call, result, argRVals);
4643 return result;
4644 }
4645
4646 /*
4647 * Push whatever the function/method needs as a this argument, if anything.
4648 */
4649 method.CallPre (this, call);
4650
4651 /*
4652 * Push the script-visible args, left-to-right.
4653 */
4654 argTypes = method.GetArgTypes ();
4655 for (i = 0; i < nArgs; i ++) {
4656 if (argTypes == null) {
4657 argRVals[i].PushVal (this, call);
4658 } else {
4659 argRVals[i].PushVal (this, call, argTypes[i]);
4660 }
4661 }
4662
4663 /*
4664 * Now output call instruction.
4665 */
4666 method.CallPost (this, call);
4667
4668 /*
4669 * Deal with the return value (if any), by putting it in 'result'.
4670 */
4671 result.Pop (this, call, retType);
4672 return result;
4673 }
4674
4675 /**
4676 * @brief This is needed to avoid nesting call labels around non-trivial properties.
4677 * It should be used for the second (and later) operands.
4678 * Note that a 'call' is considered an operator, so all arguments of a call
4679 * should be trivialized, but the method itself does not need to be.
4680 */
4681 public CompValu Trivialize (CompValu operand, Token errorAt)
4682 {
4683 if (operand.IsReadTrivial (this, errorAt)) return operand;
4684 CompValuTemp temp = new CompValuTemp (operand.type, this);
4685 operand.PushVal (this, errorAt);
4686 temp.Pop (this, errorAt);
4687 return temp;
4688 }
4689
4690 /**
4691 * @brief Generate code that casts a value to a particular type.
4692 * @returns where the result of the conversion is stored.
4693 */
4694 private CompValu GenerateFromRValCast (TokenRValCast cast)
4695 {
4696 /*
4697 * If casting to a delegate type, use the argment signature
4698 * of the delegate to help select the function/method, eg,
4699 * '(delegate string(integer))ToString'
4700 * will select 'string ToString(integer x)'
4701 * instaead of 'string ToString(float x)' or anything else
4702 */
4703 TokenType[] argsig = null;
4704 TokenType outType = cast.castTo;
4705 if (outType is TokenTypeSDTypeDelegate) {
4706 argsig = ((TokenTypeSDTypeDelegate)outType).decl.GetArgTypes ();
4707 }
4708
4709 /*
4710 * Generate the value that is being cast.
4711 * If the value is already the requested type, just use it as is.
4712 */
4713 CompValu inRVal = GenerateFromRVal (cast.rVal, argsig);
4714 if (inRVal.type == outType) return inRVal;
4715
4716 /*
4717 * Different type, generate casting code, putting the result in a temp of the output type.
4718 */
4719 CompValu outRVal = new CompValuTemp (outType, this);
4720 outRVal.PopPre (this, cast);
4721 inRVal.PushVal (this, cast, outType, true);
4722 outRVal.PopPost (this, cast);
4723 return outRVal;
4724 }
4725
4726 /**
4727 * @brief Compute conditional expression value.
4728 * @returns type and location of computed value.
4729 */
4730 private CompValu GenerateFromRValCondExpr (TokenRValCondExpr rValCondExpr)
4731 {
4732 bool condVal;
4733 CompValu condValu = GenerateFromRVal (rValCondExpr.condExpr);
4734 if (IsConstBoolExpr (condValu, out condVal)) {
4735 return GenerateFromRVal (condVal ? rValCondExpr.trueExpr : rValCondExpr.falseExpr);
4736 }
4737
4738 ScriptMyLabel falseLabel = ilGen.DefineLabel ("condexfalse");
4739 ScriptMyLabel doneLabel = ilGen.DefineLabel ("condexdone");
4740
4741 condValu.PushVal (this, rValCondExpr.condExpr, tokenTypeBool);
4742 ilGen.Emit (rValCondExpr, OpCodes.Brfalse, falseLabel);
4743
4744 CompValu trueValu = GenerateFromRVal (rValCondExpr.trueExpr);
4745 trueValu.PushVal (this, rValCondExpr.trueExpr);
4746 ilGen.Emit (rValCondExpr, OpCodes.Br, doneLabel);
4747
4748 ilGen.MarkLabel (falseLabel);
4749 CompValu falseValu = GenerateFromRVal (rValCondExpr.falseExpr);
4750 falseValu.PushVal (this, rValCondExpr.falseExpr);
4751
4752 if (trueValu.type.GetType () != falseValu.type.GetType ()) {
4753 ErrorMsg (rValCondExpr, "? operands " + trueValu.type.ToString () + " : " +
4754 falseValu.type.ToString () + " must be of same type");
4755 }
4756
4757 ilGen.MarkLabel (doneLabel);
4758 CompValuTemp retRVal = new CompValuTemp (trueValu.type, this);
4759 retRVal.Pop (this, rValCondExpr);
4760 return retRVal;
4761 }
4762
4763 /**
4764 * @brief Constant in the script somewhere
4765 * @returns where the constants value is stored
4766 */
4767 private CompValu GenerateFromRValConst (TokenRValConst rValConst)
4768 {
4769 switch (rValConst.type) {
4770 case TokenRValConstType.CHAR: {
4771 return new CompValuChar (new TokenTypeChar (rValConst), (char)(rValConst.val));
4772 }
4773 case TokenRValConstType.FLOAT: {
4774 return new CompValuFloat (new TokenTypeFloat (rValConst), (double)(rValConst.val));
4775 }
4776 case TokenRValConstType.INT: {
4777 return new CompValuInteger (new TokenTypeInt (rValConst), (int)(rValConst.val));
4778 }
4779 case TokenRValConstType.KEY: {
4780 return new CompValuString (new TokenTypeKey (rValConst), (string)(rValConst.val));
4781 }
4782 case TokenRValConstType.STRING: {
4783 return new CompValuString (new TokenTypeStr (rValConst), (string)(rValConst.val));
4784 }
4785 }
4786 throw new Exception ("unknown constant type " + rValConst.val.GetType ());
4787 }
4788
4789 /**
4790 * @brief generate a new list object
4791 * @param rValList = an rVal to create it from
4792 */
4793 private CompValu GenerateFromRValList (TokenRValList rValList)
4794 {
4795 /*
4796 * Compute all element values and remember where we put them.
4797 * Do it right-to-left as customary for LSL scripts.
4798 */
4799 int i = 0;
4800 TokenRVal lastRVal = null;
4801 for (TokenRVal val = rValList.rVal; val != null; val = (TokenRVal)val.nextToken) {
4802 i ++;
4803 val.prevToken = lastRVal;
4804 lastRVal = val;
4805 }
4806 CompValu[] vals = new CompValu[i];
4807 for (TokenRVal val = lastRVal; val != null; val = (TokenRVal)val.prevToken) {
4808 vals[--i] = GenerateFromRVal (val);
4809 }
4810
4811 /*
4812 * This is the temp that will hold the created list.
4813 */
4814 CompValuTemp newList = new CompValuTemp (new TokenTypeList (rValList.rVal), this);
4815
4816 /*
4817 * Create a temp object[] array to hold all the initial values.
4818 */
4819 ilGen.Emit (rValList, OpCodes.Ldc_I4, rValList.nItems);
4820 ilGen.Emit (rValList, OpCodes.Newarr, typeof (object));
4821
4822 /*
4823 * Populate the array.
4824 */
4825 i = 0;
4826 for (TokenRVal val = rValList.rVal; val != null; val = (TokenRVal)val.nextToken) {
4827
4828 /*
4829 * Get pointer to temp array object.
4830 */
4831 ilGen.Emit (rValList, OpCodes.Dup);
4832
4833 /*
4834 * Get index in that array.
4835 */
4836 ilGen.Emit (rValList, OpCodes.Ldc_I4, i);
4837
4838 /*
4839 * Store initialization value in array location.
4840 * However, floats and ints need to be converted to LSL_Float and LSL_Integer,
4841 * or things like llSetPayPrice() will puque when they try to cast the elements
4842 * to LSL_Float or LSL_Integer. Likewise with string/LSL_String.
4843 *
4844 * Maybe it's already LSL-boxed so we don't do anything with it except make sure
4845 * it is an object, not a struct.
4846 */
4847 CompValu eRVal = vals[i++];
4848 eRVal.PushVal (this, val);
4849 if (eRVal.type.ToLSLWrapType () == null) {
4850 if (eRVal.type is TokenTypeFloat) {
4851 ilGen.Emit (val, OpCodes.Newobj, lslFloatConstructorInfo);
4852 ilGen.Emit (val, OpCodes.Box, typeof (LSL_Float));
4853 } else if (eRVal.type is TokenTypeInt) {
4854 ilGen.Emit (val, OpCodes.Newobj, lslIntegerConstructorInfo);
4855 ilGen.Emit (val, OpCodes.Box, typeof (LSL_Integer));
4856 } else if ((eRVal.type is TokenTypeKey) || (eRVal.type is TokenTypeStr)) {
4857 ilGen.Emit (val, OpCodes.Newobj, lslStringConstructorInfo);
4858 ilGen.Emit (val, OpCodes.Box, typeof (LSL_String));
4859 } else if (eRVal.type.ToSysType ().IsValueType) {
4860 ilGen.Emit (val, OpCodes.Box, eRVal.type.ToSysType ());
4861 }
4862 } else if (eRVal.type.ToLSLWrapType ().IsValueType) {
4863
4864 // Convert the LSL value structs to an object of the LSL-boxed type
4865 ilGen.Emit (val, OpCodes.Box, eRVal.type.ToLSLWrapType ());
4866 }
4867 ilGen.Emit (val, OpCodes.Stelem, typeof (object));
4868 }
4869
4870 /*
4871 * Create new list object from temp initial value array (whose ref is still on the stack).
4872 */
4873 ilGen.Emit (rValList, OpCodes.Newobj, lslListConstructorInfo);
4874 newList.Pop (this, rValList);
4875 return newList;
4876 }
4877
4878 /**
4879 * @brief New array allocation with initializer expressions.
4880 */
4881 private CompValu GenerateFromRValNewArIni (TokenRValNewArIni rValNewArIni)
4882 {
4883 return MallocAndInitArray (rValNewArIni.arrayType, rValNewArIni.valueList);
4884 }
4885
4886 /**
4887 * @brief Mallocate and initialize an array from its initialization list.
4888 * @param arrayType = type of the array to be allocated and initialized
4889 * @param values = initialization value list used to size and initialize the array.
4890 * @returns memory location of the resultant initialized array.
4891 */
4892 private CompValu MallocAndInitArray (TokenType arrayType, TokenList values)
4893 {
4894 TokenDeclSDTypeClass arrayDecl = ((TokenTypeSDTypeClass)arrayType).decl;
4895 TokenType eleType = arrayDecl.arrayOfType;
4896 int rank = arrayDecl.arrayOfRank;
4897
4898 // Get size of each of the dimensions by scanning the initialization value list
4899 int[] dimSizes = new int[rank];
4900 FillInDimSizes (dimSizes, 0, rank, values);
4901
4902 // Figure out where the array's $new() method is
4903 TokenType[] newargsig = new TokenType[rank];
4904 for (int k = 0; k < rank; k ++) {
4905 newargsig[k] = tokenTypeInt;
4906 }
4907 TokenDeclVar newMeth = FindThisMember (arrayDecl, new TokenName (null, "$new"), newargsig);
4908
4909 // Output a call to malloc the array with all default values
4910 // array = ArrayType.$new (dimSizes[0], dimSizes[1], ...)
4911 CompValuTemp array = new CompValuTemp (arrayType, this);
4912 PushXMRInst ();
4913 for (int k = 0; k < rank; k ++) {
4914 ilGen.Emit (values, OpCodes.Ldc_I4, dimSizes[k]);
4915 }
4916 ilGen.Emit (values, OpCodes.Call, newMeth.ilGen);
4917 array.Pop (this, arrayType);
4918
4919 // Figure out where the array's Set() method is
4920 TokenType[] setargsig = new TokenType[rank+1];
4921 for (int k = 0; k < rank; k ++) {
4922 setargsig[k] = tokenTypeInt;
4923 }
4924 setargsig[rank] = eleType;
4925 TokenDeclVar setMeth = FindThisMember (arrayDecl, new TokenName (null, "Set"), setargsig);
4926
4927 // Fill in the array with the initializer values
4928 FillInInitVals (array, setMeth, dimSizes, 0, rank, values, eleType);
4929
4930 // The array is our resultant value
4931 return array;
4932 }
4933
4934 /**
4935 * @brief Compute an array's dimensions given its initialization value list
4936 * @param dimSizes = filled in with array's dimensions
4937 * @param dimNo = what dimension the 'values' list applies to
4938 * @param rank = total number of dimensions of the array
4939 * @param values = list of values to initialize the array's 'dimNo' dimension with
4940 * @returns with dimSizes[dimNo..rank-1] filled in
4941 */
4942 private static void FillInDimSizes (int[] dimSizes, int dimNo, int rank, TokenList values)
4943 {
4944 // the size of a dimension is the largest number of initializer elements at this level
4945 // for dimNo 0, this is the number of elements in the top-level list
4946 if (dimSizes[dimNo] < values.tl.Count) dimSizes[dimNo] = values.tl.Count;
4947
4948 // see if there is another dimension to calculate
4949 if (++ dimNo < rank) {
4950
4951 // its size is the size of the largest initializer list at the next inner level
4952 foreach (Token val in values.tl) {
4953 if (val is TokenList) {
4954 TokenList subvals = (TokenList)val;
4955 FillInDimSizes (dimSizes, dimNo, rank, subvals);
4956 }
4957 }
4958 }
4959 }
4960
4961 /**
4962 * @brief Output code to fill in array's initialization values
4963 * @param array = array to be filled in
4964 * @param setMeth = the array's Set() method
4965 * @param subscripts = holds subscripts being built
4966 * @param dimNo = which dimension the 'values' are for
4967 * @param values = list of initialization values for dimension 'dimNo'
4968 * @param rank = number of dimensions of 'array'
4969 * @param values = list of values to initialize the array's 'dimNo' dimension with
4970 * @param eleType = the element's type
4971 * @returns with code emitted to initialize array's [subscripts[0], ..., subscripts[dimNo-1], *, *, ...]
4972 * dimNo and up completely filled ---^
4973 */
4974 private void FillInInitVals (CompValu array, TokenDeclVar setMeth, int[] subscripts, int dimNo, int rank, TokenList values, TokenType eleType)
4975 {
4976 subscripts[dimNo] = 0;
4977 foreach (Token val in values.tl) {
4978 CompValu initValue = null;
4979
4980 /*
4981 * If it is a sublist, process it.
4982 * If we don't have enough subscripts yet, hopefully that sublist will have enough.
4983 * If we already have enough subscripts, then that sublist can be for an element of this supposedly jagged array.
4984 */
4985 if (val is TokenList) {
4986 TokenList sublist = (TokenList)val;
4987 if (dimNo + 1 < rank) {
4988
4989 /*
4990 * We don't have enough subscripts yet, hopefully the sublist has the rest.
4991 */
4992 FillInInitVals (array, setMeth, subscripts, dimNo + 1, rank, sublist, eleType);
4993 } else if ((eleType is TokenTypeSDTypeClass) && (((TokenTypeSDTypeClass)eleType).decl.arrayOfType == null)) {
4994
4995 /*
4996 * If we aren't a jagged array either, we can't do anything with the sublist.
4997 */
4998 ErrorMsg (val, "too many brace levels");
4999 } else {
5000
5001 /*
5002 * We are a jagged array, so malloc a subarray and initialize it with the sublist.
5003 * Then we can use that subarray to fill this array's element.
5004 */
5005 initValue = MallocAndInitArray (eleType, sublist);
5006 }
5007 }
5008
5009 /*
5010 * If it is a value expression, then output code to compute the value.
5011 */
5012 if (val is TokenRVal) {
5013 if (dimNo + 1 < rank) {
5014 ErrorMsg ((Token)val, "not enough brace levels");
5015 } else {
5016 initValue = GenerateFromRVal ((TokenRVal)val);
5017 }
5018 }
5019
5020 /*
5021 * If there is an initValue, output "array.Set (subscript[0], subscript[1], ..., initValue)"
5022 */
5023 if (initValue != null) {
5024 array.PushVal (this, val);
5025 for (int i = 0; i <= dimNo; i ++) {
5026 ilGen.Emit (val, OpCodes.Ldc_I4, subscripts[i]);
5027 }
5028 initValue.PushVal (this, val, eleType);
5029 ilGen.Emit (val, OpCodes.Call, setMeth.ilGen);
5030 }
5031
5032 /*
5033 * That subscript is processed one way or another, on to the next.
5034 */
5035 subscripts[dimNo] ++;
5036 }
5037 }
5038
5039 /**
5040 * @brief parenthesized expression
5041 * @returns type and location of the result of the computation.
5042 */
5043 private CompValu GenerateFromRValParen (TokenRValParen rValParen)
5044 {
5045 return GenerateFromRVal (rValParen.rVal);
5046 }
5047
5048 /**
5049 * @brief create a rotation object from the x,y,z,w value expressions.
5050 */
5051 private CompValu GenerateFromRValRot (TokenRValRot rValRot)
5052 {
5053 CompValu xRVal, yRVal, zRVal, wRVal;
5054
5055 xRVal = Trivialize (GenerateFromRVal (rValRot.xRVal), rValRot);
5056 yRVal = Trivialize (GenerateFromRVal (rValRot.yRVal), rValRot);
5057 zRVal = Trivialize (GenerateFromRVal (rValRot.zRVal), rValRot);
5058 wRVal = Trivialize (GenerateFromRVal (rValRot.wRVal), rValRot);
5059 return new CompValuRot (new TokenTypeRot (rValRot), xRVal, yRVal, zRVal, wRVal);
5060 }
5061
5062 /**
5063 * @brief Using 'this' as a pointer to the current script-defined instance object.
5064 * The value is located in arg #0 of the current instance method.
5065 */
5066 private CompValu GenerateFromRValThis (TokenRValThis zhis)
5067 {
5068 if (!IsSDTInstMethod ()) {
5069 ErrorMsg (zhis, "cannot access instance member of class from static method");
5070 return new CompValuVoid (zhis);
5071 }
5072 return new CompValuArg (curDeclFunc.sdtClass.MakeRefToken (zhis), 0);
5073 }
5074
5075 /**
5076 * @brief 'undefined' constant.
5077 * If this constant gets written to an array element, it will delete that element from the array.
5078 * If the script retrieves an element by key that is not defined, it will get this value.
5079 * This value can be stored in and retrieved from variables of type 'object' or script-defined classes.
5080 * It is a runtime error to cast this value to any other type, eg,
5081 * we don't allow list or string variables to be null pointers.
5082 */
5083 private CompValu GenerateFromRValUndef (TokenRValUndef rValUndef)
5084 {
5085 return new CompValuNull (new TokenTypeUndef (rValUndef));
5086 }
5087
5088 /**
5089 * @brief create a vector object from the x,y,z value expressions.
5090 */
5091 private CompValu GenerateFromRValVec (TokenRValVec rValVec)
5092 {
5093 CompValu xRVal, yRVal, zRVal;
5094
5095 xRVal = Trivialize (GenerateFromRVal (rValVec.xRVal), rValVec);
5096 yRVal = Trivialize (GenerateFromRVal (rValVec.yRVal), rValVec);
5097 zRVal = Trivialize (GenerateFromRVal (rValVec.zRVal), rValVec);
5098 return new CompValuVec (new TokenTypeVec (rValVec), xRVal, yRVal, zRVal);
5099 }
5100
5101 /**
5102 * @brief Generate code to get the default initialization value for a variable.
5103 */
5104 private CompValu GenerateFromRValInitDef (TokenRValInitDef rValInitDef)
5105 {
5106 TokenType type = rValInitDef.type;
5107
5108 if (type is TokenTypeChar) {
5109 return new CompValuChar (type, (char)0);
5110 }
5111 if (type is TokenTypeRot) {
5112 CompValuFloat x = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.x);
5113 CompValuFloat y = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.y);
5114 CompValuFloat z = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.z);
5115 CompValuFloat s = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.s);
5116 return new CompValuRot (type, x, y, z, s);
5117 }
5118 if ((type is TokenTypeKey) || (type is TokenTypeStr)) {
5119 return new CompValuString (type, "");
5120 }
5121 if (type is TokenTypeVec) {
5122 CompValuFloat x = new CompValuFloat (type, ScriptBaseClass.ZERO_VECTOR.x);
5123 CompValuFloat y = new CompValuFloat (type, ScriptBaseClass.ZERO_VECTOR.y);
5124 CompValuFloat z = new CompValuFloat (type, ScriptBaseClass.ZERO_VECTOR.z);
5125 return new CompValuVec (type, x, y, z);
5126 }
5127 if (type is TokenTypeInt) {
5128 return new CompValuInteger (type, 0);
5129 }
5130 if (type is TokenTypeFloat) {
5131 return new CompValuFloat (type, 0);
5132 }
5133 if (type is TokenTypeVoid) {
5134 return new CompValuVoid (type);
5135 }
5136
5137 /*
5138 * Default for 'object' type is 'undef'.
5139 * Likewise for script-defined classes and interfaces.
5140 */
5141 if ((type is TokenTypeObject) || (type is TokenTypeSDTypeClass) || (type is TokenTypeSDTypeDelegate) ||
5142 (type is TokenTypeSDTypeInterface) || (type is TokenTypeExc)) {
5143 return new CompValuNull (type);
5144 }
5145
5146 /*
5147 * array and list
5148 */
5149 CompValuTemp temp = new CompValuTemp (type, this);
5150 PushDefaultValue (type);
5151 temp.Pop (this, rValInitDef, type);
5152 return temp;
5153 }
5154
5155 /**
5156 * @brief Generate code to process an <rVal> is <type> expression, and produce a boolean value.
5157 */
5158 private CompValu GenerateFromRValIsType (TokenRValIsType rValIsType)
5159 {
5160 /*
5161 * Expression we want to know the type of.
5162 */
5163 CompValu val = GenerateFromRVal (rValIsType.rValExp);
5164
5165 /*
5166 * Pass it in to top-level type expression decoder.
5167 */
5168 return GenerateFromTypeExp (val, rValIsType.typeExp);
5169 }
5170
5171 /**
5172 * @brief See if the type of the given value matches the type expression.
5173 * @param val = where the value to be evaluated is stored
5174 * @param typeExp = script tokens representing type expression
5175 * @returns location where the boolean result is stored
5176 */
5177 private CompValu GenerateFromTypeExp (CompValu val, TokenTypeExp typeExp)
5178 {
5179 if (typeExp is TokenTypeExpBinOp) {
5180 CompValu left = GenerateFromTypeExp (val, ((TokenTypeExpBinOp)typeExp).leftOp);
5181 CompValu right = GenerateFromTypeExp (val, ((TokenTypeExpBinOp)typeExp).rightOp);
5182 CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
5183 Token op = ((TokenTypeExpBinOp)typeExp).binOp;
5184 left.PushVal (this, ((TokenTypeExpBinOp)typeExp).leftOp);
5185 right.PushVal (this, ((TokenTypeExpBinOp)typeExp).rightOp);
5186 if (op is TokenKwAnd) {
5187 ilGen.Emit (typeExp, OpCodes.And);
5188 } else if (op is TokenKwOr) {
5189 ilGen.Emit (typeExp, OpCodes.Or);
5190 } else {
5191 throw new Exception ("unknown TokenTypeExpBinOp " + op.GetType ());
5192 }
5193 result.Pop (this, typeExp);
5194 return result;
5195 }
5196 if (typeExp is TokenTypeExpNot) {
5197 CompValu interm = GenerateFromTypeExp (val, ((TokenTypeExpNot)typeExp).typeExp);
5198 CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
5199 interm.PushVal (this, ((TokenTypeExpNot)typeExp).typeExp, tokenTypeBool);
5200 ilGen.Emit (typeExp, OpCodes.Ldc_I4_1);
5201 ilGen.Emit (typeExp, OpCodes.Xor);
5202 result.Pop (this, typeExp);
5203 return result;
5204 }
5205 if (typeExp is TokenTypeExpPar) {
5206 return GenerateFromTypeExp (val, ((TokenTypeExpPar)typeExp).typeExp);
5207 }
5208 if (typeExp is TokenTypeExpType) {
5209 CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
5210 val.PushVal (this, typeExp);
5211 ilGen.Emit (typeExp, OpCodes.Isinst, ((TokenTypeExpType)typeExp).typeToken.ToSysType ());
5212 ilGen.Emit (typeExp, OpCodes.Ldnull);
5213 ilGen.Emit (typeExp, OpCodes.Ceq);
5214 ilGen.Emit (typeExp, OpCodes.Ldc_I4_1);
5215 ilGen.Emit (typeExp, OpCodes.Xor);
5216 result.Pop (this, typeExp);
5217 return result;
5218 }
5219 if (typeExp is TokenTypeExpUndef) {
5220 CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
5221 val.PushVal (this, typeExp);
5222 ilGen.Emit (typeExp, OpCodes.Ldnull);
5223 ilGen.Emit (typeExp, OpCodes.Ceq);
5224 result.Pop (this, typeExp);
5225 return result;
5226 }
5227 throw new Exception ("unknown TokenTypeExp type " + typeExp.GetType ());
5228 }
5229
5230 /**
5231 * @brief Push the default (null) value for a particular variable
5232 * @param var = variable to get the default value for
5233 * @returns with value pushed on stack
5234 */
5235 public void PushVarDefaultValue (TokenDeclVar var)
5236 {
5237 PushDefaultValue (var.type);
5238 }
5239 public void PushDefaultValue (TokenType type)
5240 {
5241 if (type is TokenTypeArray) {
5242 PushXMRInst (); // instance
5243 ilGen.Emit (type, OpCodes.Newobj, xmrArrayConstructorInfo);
5244 return;
5245 }
5246 if (type is TokenTypeChar) {
5247 ilGen.Emit (type, OpCodes.Ldc_I4_0);
5248 return;
5249 }
5250 if (type is TokenTypeList) {
5251 ilGen.Emit (type, OpCodes.Ldc_I4_0);
5252 ilGen.Emit (type, OpCodes.Newarr, typeof (object));
5253 ilGen.Emit (type, OpCodes.Newobj, lslListConstructorInfo);
5254 return;
5255 }
5256 if (type is TokenTypeRot) {
5257 // Mono is tOO stOOpid to allow: ilGen.Emit (OpCodes.Ldsfld, zeroRotationFieldInfo);
5258 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.x);
5259 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.y);
5260 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.z);
5261 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.s);
5262 ilGen.Emit (type, OpCodes.Newobj, lslRotationConstructorInfo);
5263 return;
5264 }
5265 if ((type is TokenTypeKey) || (type is TokenTypeStr)) {
5266 ilGen.Emit (type, OpCodes.Ldstr, "");
5267 return;
5268 }
5269 if (type is TokenTypeVec) {
5270 // Mono is tOO stOOpid to allow: ilGen.Emit (OpCodes.Ldsfld, zeroVectorFieldInfo);
5271 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_VECTOR.x);
5272 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_VECTOR.y);
5273 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_VECTOR.z);
5274 ilGen.Emit (type, OpCodes.Newobj, lslVectorConstructorInfo);
5275 return;
5276 }
5277 if (type is TokenTypeInt) {
5278 ilGen.Emit (type, OpCodes.Ldc_I4_0);
5279 return;
5280 }
5281 if (type is TokenTypeFloat) {
5282 ilGen.Emit (type, OpCodes.Ldc_R4, 0.0f);
5283 return;
5284 }
5285
5286 /*
5287 * Default for 'object' type is 'undef'.
5288 * Likewise for script-defined classes and interfaces.
5289 */
5290 if ((type is TokenTypeObject) || (type is TokenTypeSDTypeClass) || (type is TokenTypeSDTypeInterface) || (type is TokenTypeExc)) {
5291 ilGen.Emit (type, OpCodes.Ldnull);
5292 return;
5293 }
5294
5295 /*
5296 * Void is pushed as the default return value of a void function.
5297 * So just push nothing as expected of void functions.
5298 */
5299 if (type is TokenTypeVoid) {
5300 return;
5301 }
5302
5303 /*
5304 * Default for 'delegate' type is 'undef'.
5305 */
5306 if (type is TokenTypeSDTypeDelegate) {
5307 ilGen.Emit (type, OpCodes.Ldnull);
5308 return;
5309 }
5310
5311 throw new Exception ("unknown type " + type.GetType ().ToString ());
5312 }
5313
5314 /**
5315 * @brief Determine if the expression has a constant boolean value
5316 * and if so, if the value is true or false.
5317 * @param expr = expression to evaluate
5318 * @returns true: expression is contant and has boolean value true
5319 * false: otherwise
5320 */
5321 private bool IsConstBoolExprTrue (CompValu expr)
5322 {
5323 bool constVal;
5324 return IsConstBoolExpr (expr, out constVal) && constVal;
5325 }
5326
5327 private bool IsConstBoolExpr (CompValu expr, out bool constVal)
5328 {
5329 if (expr is CompValuChar) {
5330 constVal = ((CompValuChar)expr).x != 0;
5331 return true;
5332 }
5333 if (expr is CompValuFloat) {
5334 constVal = ((CompValuFloat)expr).x != (double)0;
5335 return true;
5336 }
5337 if (expr is CompValuInteger) {
5338 constVal = ((CompValuInteger)expr).x != 0;
5339 return true;
5340 }
5341 if (expr is CompValuString) {
5342 string s = ((CompValuString)expr).x;
5343 constVal = s != "";
5344 if (constVal && (expr.type is TokenTypeKey)) {
5345 constVal = s != ScriptBaseClass.NULL_KEY;
5346 }
5347 return true;
5348 }
5349
5350 constVal = false;
5351 return false;
5352 }
5353
5354 /**
5355 * @brief Determine if the expression has a constant integer value
5356 * and if so, return the integer value.
5357 * @param expr = expression to evaluate
5358 * @returns true: expression is contant and has integer value
5359 * false: otherwise
5360 */
5361 private bool IsConstIntExpr (CompValu expr, out int constVal)
5362 {
5363 if (expr is CompValuChar) {
5364 constVal = (int)((CompValuChar)expr).x;
5365 return true;
5366 }
5367 if (expr is CompValuInteger) {
5368 constVal = ((CompValuInteger)expr).x;
5369 return true;
5370 }
5371
5372 constVal = 0;
5373 return false;
5374 }
5375
5376 /**
5377 * @brief Determine if the expression has a constant string value
5378 * and if so, return the string value.
5379 * @param expr = expression to evaluate
5380 * @returns true: expression is contant and has string value
5381 * false: otherwise
5382 */
5383 private bool IsConstStrExpr (CompValu expr, out string constVal)
5384 {
5385 if (expr is CompValuString) {
5386 constVal = ((CompValuString)expr).x;
5387 return true;
5388 }
5389 constVal = "";
5390 return false;
5391 }
5392
5393 /**
5394 * @brief create table of legal event handler prototypes.
5395 * This is used to make sure script's event handler declrations are valid.
5396 */
5397 private static VarDict CreateLegalEventHandlers ()
5398 {
5399 /*
5400 * Get handler prototypes with full argument lists.
5401 */
5402 VarDict leh = new InternalFuncDict (typeof (IEventHandlers), false);
5403
5404 /*
5405 * We want the scripts to be able to declare their handlers with
5406 * fewer arguments than the full argument lists. So define additional
5407 * prototypes with fewer arguments.
5408 */
5409 TokenDeclVar[] fullArgProtos = new TokenDeclVar[leh.Count];
5410 int i = 0;
5411 foreach (TokenDeclVar fap in leh) fullArgProtos[i++] = fap;
5412
5413 foreach (TokenDeclVar fap in fullArgProtos) {
5414 TokenArgDecl fal = fap.argDecl;
5415 int fullArgCount = fal.vars.Length;
5416 for (i = 0; i < fullArgCount; i ++) {
5417 TokenArgDecl shortArgList = new TokenArgDecl (null);
5418 for (int j = 0; j < i; j ++) {
5419 TokenDeclVar var = fal.vars[j];
5420 shortArgList.AddArg (var.type, var.name);
5421 }
5422 TokenDeclVar shortArgProto = new TokenDeclVar (null, null, null);
5423 shortArgProto.name = new TokenName (null, fap.GetSimpleName ());
5424 shortArgProto.retType = fap.retType;
5425 shortArgProto.argDecl = shortArgList;
5426 leh.AddEntry (shortArgProto);
5427 }
5428 }
5429
5430 return leh;
5431 }
5432
5433 /**
5434 * @brief Emit a call to CheckRun(), (voluntary multitasking switch)
5435 */
5436 public void EmitCallCheckRun (Token errorAt, bool stack)
5437 {
5438 if (curDeclFunc.IsFuncTrivial (this)) throw new Exception (curDeclFunc.fullName + " is supposed to be trivial");
5439 new CallLabel (this, errorAt); // jump here when stack restored
5440 PushXMRInst (); // instance
5441 ilGen.Emit (errorAt, OpCodes.Call, stack ? checkRunStackMethInfo : checkRunQuickMethInfo);
5442 openCallLabel = null;
5443 }
5444
5445 /**
5446 * @brief Emit code to push a callNo var on the stack.
5447 */
5448 public void GetCallNo (Token errorAt, ScriptMyLocal callNoVar)
5449 {
5450 ilGen.Emit (errorAt, OpCodes.Ldloc, callNoVar);
5451 //ilGen.Emit (errorAt, OpCodes.Ldloca, callNoVar);
5452 //ilGen.Emit (errorAt, OpCodes.Volatile);
5453 //ilGen.Emit (errorAt, OpCodes.Ldind_I4);
5454 }
5455 public void GetCallNo (Token errorAt, CompValu callNoVar)
5456 {
5457 callNoVar.PushVal (this, errorAt);
5458 //callNoVar.PushRef (this, errorAt);
5459 //ilGen.Emit (errorAt, OpCodes.Volatile);
5460 //ilGen.Emit (errorAt, OpCodes.Ldind_I4);
5461 }
5462
5463 /**
5464 * @brief Emit code to set a callNo var to a given constant.
5465 */
5466 public void SetCallNo (Token errorAt, ScriptMyLocal callNoVar, int val)
5467 {
5468 ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
5469 ilGen.Emit (errorAt, OpCodes.Stloc, callNoVar);
5470 //ilGen.Emit (errorAt, OpCodes.Ldloca, callNoVar);
5471 //ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
5472 //ilGen.Emit (errorAt, OpCodes.Volatile);
5473 //ilGen.Emit (errorAt, OpCodes.Stind_I4);
5474 }
5475 public void SetCallNo (Token errorAt, CompValu callNoVar, int val)
5476 {
5477 callNoVar.PopPre (this, errorAt);
5478 ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
5479 callNoVar.PopPost (this, errorAt);
5480 //callNoVar.PushRef (this, errorAt);
5481 //ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
5482 //ilGen.Emit (errorAt, OpCodes.Volatile);
5483 //ilGen.Emit (errorAt, OpCodes.Stind_I4);
5484 }
5485
5486 /**
5487 * @brief handle a unary operator, such as -x.
5488 */
5489 private CompValu UnOpGenerate (CompValu inRVal, Token opcode)
5490 {
5491 /*
5492 * - Negate
5493 */
5494 if (opcode is TokenKwSub) {
5495 if (inRVal.type is TokenTypeFloat) {
5496 CompValuTemp outRVal = new CompValuTemp (new TokenTypeFloat (opcode), this);
5497 inRVal.PushVal (this, opcode, outRVal.type); // push value to negate, make sure not LSL-boxed
5498 ilGen.Emit (opcode, OpCodes.Neg); // compute the negative
5499 outRVal.Pop (this, opcode); // pop into result
5500 return outRVal; // tell caller where we put it
5501 }
5502 if (inRVal.type is TokenTypeInt) {
5503 CompValuTemp outRVal = new CompValuTemp (new TokenTypeInt (opcode), this);
5504 inRVal.PushVal (this, opcode, outRVal.type); // push value to negate, make sure not LSL-boxed
5505 ilGen.Emit (opcode, OpCodes.Neg); // compute the negative
5506 outRVal.Pop (this, opcode); // pop into result
5507 return outRVal; // tell caller where we put it
5508 }
5509 if (inRVal.type is TokenTypeRot) {
5510 CompValuTemp outRVal = new CompValuTemp (inRVal.type, this);
5511 inRVal.PushVal (this, opcode); // push rotation, then call negate routine
5512 ilGen.Emit (opcode, OpCodes.Call, lslRotationNegateMethodInfo);
5513 outRVal.Pop (this, opcode); // pop into result
5514 return outRVal; // tell caller where we put it
5515 }
5516 if (inRVal.type is TokenTypeVec) {
5517 CompValuTemp outRVal = new CompValuTemp (inRVal.type, this);
5518 inRVal.PushVal (this, opcode); // push vector, then call negate routine
5519 ilGen.Emit (opcode, OpCodes.Call, lslVectorNegateMethodInfo);
5520 outRVal.Pop (this, opcode); // pop into result
5521 return outRVal; // tell caller where we put it
5522 }
5523 ErrorMsg (opcode, "can't negate a " + inRVal.type.ToString ());
5524 return inRVal;
5525 }
5526
5527 /*
5528 * ~ Complement (bitwise integer)
5529 */
5530 if (opcode is TokenKwTilde) {
5531 if (inRVal.type is TokenTypeInt) {
5532 CompValuTemp outRVal = new CompValuTemp (new TokenTypeInt (opcode), this);
5533 inRVal.PushVal (this, opcode, outRVal.type); // push value to negate, make sure not LSL-boxed
5534 ilGen.Emit (opcode, OpCodes.Not); // compute the complement
5535 outRVal.Pop (this, opcode); // pop into result
5536 return outRVal; // tell caller where we put it
5537 }
5538 ErrorMsg (opcode, "can't complement a " + inRVal.type.ToString ());
5539 return inRVal;
5540 }
5541
5542 /*
5543 * ! Not (boolean)
5544 *
5545 * We stuff the 0/1 result in an int because I've seen x+!y in scripts
5546 * and we don't want to have to create tables to handle int+bool and
5547 * everything like that.
5548 */
5549 if (opcode is TokenKwExclam) {
5550 CompValuTemp outRVal = new CompValuTemp (new TokenTypeInt (opcode), this);
5551 inRVal.PushVal (this, opcode, tokenTypeBool); // anything converts to boolean
5552 ilGen.Emit (opcode, OpCodes.Ldc_I4_1); // then XOR with 1 to flip it
5553 ilGen.Emit (opcode, OpCodes.Xor);
5554 outRVal.Pop (this, opcode); // pop into result
5555 return outRVal; // tell caller where we put it
5556 }
5557
5558 throw new Exception ("unhandled opcode " + opcode.ToString ());
5559 }
5560
5561 /**
5562 * @brief This is called while trying to compute the value of constant initializers.
5563 * It is passed a name and that name is looked up in the constant tables.
5564 */
5565 private TokenRVal LookupInitConstants (TokenRVal rVal, ref bool didOne)
5566 {
5567 /*
5568 * If it is a static field of a script-defined type, look it up and hopefully we find a constant there.
5569 */
5570 TokenDeclVar gblVar;
5571 if (rVal is TokenLValSField) {
5572 TokenLValSField lvsf = (TokenLValSField)rVal;
5573 if (lvsf.baseType is TokenTypeSDTypeClass) {
5574 TokenDeclSDTypeClass sdtClass = ((TokenTypeSDTypeClass)lvsf.baseType).decl;
5575 gblVar = sdtClass.members.FindExact (lvsf.fieldName.val, null);
5576 if (gblVar != null) {
5577 if (gblVar.constant && (gblVar.init is TokenRValConst)) {
5578 didOne = true;
5579 return gblVar.init;
5580 }
5581 }
5582 }
5583 return rVal;
5584 }
5585
5586 /*
5587 * Only other thing we handle is stand-alone names.
5588 */
5589 if (!(rVal is TokenLValName)) return rVal;
5590 string name = ((TokenLValName)rVal).name.val;
5591
5592 /*
5593 * If we are doing the initializations for a script-defined type,
5594 * look for the constant among the fields for that type.
5595 */
5596 if (currentSDTClass != null) {
5597 gblVar = currentSDTClass.members.FindExact (name, null);
5598 if (gblVar != null) {
5599 if (gblVar.constant && (gblVar.init is TokenRValConst)) {
5600 didOne = true;
5601 return gblVar.init;
5602 }
5603 return rVal;
5604 }
5605 }
5606
5607 /*
5608 * Look it up as a script-defined global variable.
5609 * Then if the variable is defined as a constant and has a constant value,
5610 * we are successful. If it is defined as something else, return failure.
5611 */
5612 gblVar = tokenScript.variablesStack.FindExact (name, null);
5613 if (gblVar != null) {
5614 if (gblVar.constant && (gblVar.init is TokenRValConst)) {
5615 didOne = true;
5616 return gblVar.init;
5617 }
5618 return rVal;
5619 }
5620
5621 /*
5622 * Maybe it is a built-in symbolic constant.
5623 */
5624 ScriptConst scriptConst = ScriptConst.Lookup (name);
5625 if (scriptConst != null) {
5626 rVal = CompValuConst2RValConst (scriptConst.rVal, rVal);
5627 if (rVal is TokenRValConst) {
5628 didOne = true;
5629 return rVal;
5630 }
5631 }
5632
5633 /*
5634 * Don't know what it is, return failure.
5635 */
5636 return rVal;
5637 }
5638
5639 /**
5640 * @brief This is called while trying to compute the value of constant expressions.
5641 * It is passed a name and that name is looked up in the constant tables.
5642 */
5643 private TokenRVal LookupBodyConstants (TokenRVal rVal, ref bool didOne)
5644 {
5645 /*
5646 * If it is a static field of a script-defined type, look it up and hopefully we find a constant there.
5647 */
5648 TokenDeclVar gblVar;
5649 if (rVal is TokenLValSField) {
5650 TokenLValSField lvsf = (TokenLValSField)rVal;
5651 if (lvsf.baseType is TokenTypeSDTypeClass) {
5652 TokenDeclSDTypeClass sdtClass = ((TokenTypeSDTypeClass)lvsf.baseType).decl;
5653 gblVar = sdtClass.members.FindExact (lvsf.fieldName.val, null);
5654 if ((gblVar != null) && gblVar.constant && (gblVar.init is TokenRValConst)) {
5655 didOne = true;
5656 return gblVar.init;
5657 }
5658 }
5659 return rVal;
5660 }
5661
5662 /*
5663 * Only other thing we handle is stand-alone names.
5664 */
5665 if (!(rVal is TokenLValName)) return rVal;
5666 string name = ((TokenLValName)rVal).name.val;
5667
5668 /*
5669 * Scan through the variable stack and hopefully we find a constant there.
5670 * But we stop as soon as we get a match because that's what the script is referring to.
5671 */
5672 CompValu val;
5673 for (VarDict vars = ((TokenLValName)rVal).stack; vars != null; vars = vars.outerVarDict) {
5674 TokenDeclVar var = vars.FindExact (name, null);
5675 if (var != null) {
5676 val = var.location;
5677 goto foundit;
5678 }
5679
5680 TokenDeclSDTypeClass baseClass = vars.thisClass;
5681 if (baseClass != null) {
5682 while ((baseClass = baseClass.extends) != null) {
5683 var = baseClass.members.FindExact (name, null);
5684 if (var != null) {
5685 val = var.location;
5686 goto foundit;
5687 }
5688 }
5689 }
5690 }
5691
5692 /*
5693 * Maybe it is a built-in symbolic constant.
5694 */
5695 ScriptConst scriptConst = ScriptConst.Lookup (name);
5696 if (scriptConst != null) {
5697 val = scriptConst.rVal;
5698 goto foundit;
5699 }
5700
5701 /*
5702 * Don't know what it is, return failure.
5703 */
5704 return rVal;
5705
5706 /*
5707 * Found a CompValu. If it's a simple constant, then use it.
5708 * Otherwise tell caller we failed to simplify.
5709 */
5710 foundit:
5711 rVal = CompValuConst2RValConst (val, rVal);
5712 if (rVal is TokenRValConst) {
5713 didOne = true;
5714 }
5715 return rVal;
5716 }
5717
5718 private static TokenRVal CompValuConst2RValConst (CompValu val, TokenRVal rVal)
5719 {
5720 if (val is CompValuChar) rVal = new TokenRValConst (rVal, ((CompValuChar)val).x);
5721 if (val is CompValuFloat) rVal = new TokenRValConst (rVal, ((CompValuFloat)val).x);
5722 if (val is CompValuInteger) rVal = new TokenRValConst (rVal, ((CompValuInteger)val).x);
5723 if (val is CompValuString) rVal = new TokenRValConst (rVal, ((CompValuString)val).x);
5724 return rVal;
5725 }
5726
5727 /**
5728 * @brief Generate code to push XMRInstanceSuperType pointer on stack.
5729 */
5730 public void PushXMRInst ()
5731 {
5732 if (instancePointer == null) {
5733 ilGen.Emit (null, OpCodes.Ldarg_0);
5734 } else {
5735 ilGen.Emit (null, OpCodes.Ldloc, instancePointer);
5736 }
5737 }
5738
5739 /**
5740 * @returns true: Ldarg_0 gives XMRSDTypeClObj pointer
5741 * - this is the case for instance methods
5742 * false: Ldarg_0 gives XMR_Instance pointer
5743 * - this is the case for both global functions and static methods
5744 */
5745 public bool IsSDTInstMethod ()
5746 {
5747 return (curDeclFunc.sdtClass != null) &&
5748 ((curDeclFunc.sdtFlags & ScriptReduce.SDT_STATIC) == 0);
5749 }
5750
5751 /**
5752 * @brief Look for a simply named function or variable (not a field or method)
5753 */
5754 public TokenDeclVar FindNamedVar (TokenLValName lValName, TokenType[] argsig)
5755 {
5756 /*
5757 * Look in variable stack for the given name.
5758 */
5759 for (VarDict vars = lValName.stack; vars != null; vars = vars.outerVarDict) {
5760
5761 // first look for it possibly with an argument signature
5762 // so we pick the correct overloaded method
5763 TokenDeclVar var = FindSingleMember (vars, lValName.name, argsig);
5764 if (var != null) return var;
5765
5766 // if that fails, try it without the argument signature.
5767 // delegates get entered like any other variable, ie,
5768 // no signature on their name.
5769 if (argsig != null) {
5770 var = FindSingleMember (vars, lValName.name, null);
5771 if (var != null) return var;
5772 }
5773
5774 // if this is the frame for some class members, try searching base class members too
5775 TokenDeclSDTypeClass baseClass = vars.thisClass;
5776 if (baseClass != null) {
5777 while ((baseClass = baseClass.extends) != null) {
5778 var = FindSingleMember (baseClass.members, lValName.name, argsig);
5779 if (var != null) return var;
5780 if (argsig != null) {
5781 var = FindSingleMember (baseClass.members, lValName.name, null);
5782 if (var != null) return var;
5783 }
5784 }
5785 }
5786 }
5787
5788 /*
5789 * If not found, try one of the built-in constants or functions.
5790 */
5791 if (argsig == null) {
5792 ScriptConst scriptConst = ScriptConst.Lookup (lValName.name.val);
5793 if (scriptConst != null) {
5794 TokenDeclVar var = new TokenDeclVar (lValName.name, null, tokenScript);
5795 var.name = lValName.name;
5796 var.type = scriptConst.rVal.type;
5797 var.location = scriptConst.rVal;
5798 return var;
5799 }
5800 } else {
5801 TokenDeclVar inline = FindSingleMember (TokenDeclInline.inlineFunctions, lValName.name, argsig);
5802 if (inline != null) return inline;
5803 }
5804
5805 return null;
5806 }
5807
5808
5809 /**
5810 * @brief Find a member of an interface.
5811 * @param sdType = interface type
5812 * @param name = name of member to find
5813 * @param argsig = null: field/property; else: script-visible method argument types
5814 * @param baseRVal = pointer to interface object
5815 * @returns null: no such member
5816 * else: pointer to member
5817 * baseRVal = possibly modified to point to type-casted interface object
5818 */
5819 private TokenDeclVar FindInterfaceMember (TokenTypeSDTypeInterface sdtType, TokenName name, TokenType[] argsig, ref CompValu baseRVal)
5820 {
5821 TokenDeclSDTypeInterface sdtDecl = sdtType.decl;
5822 TokenDeclSDTypeInterface impl;
5823 TokenDeclVar declVar = sdtDecl.FindIFaceMember (this, name, argsig, out impl);
5824 if ((declVar != null) && (impl != sdtDecl)) {
5825
5826 /*
5827 * Accessing a method or propterty of another interface that the primary interface says it implements.
5828 * In this case, we have to cast from the primary interface to that secondary interface.
5829 *
5830 * interface IEnumerable {
5831 * IEnumerator GetEnumerator ();
5832 * }
5833 * interface ICountable : IEnumerable {
5834 * integer GetCount ();
5835 * }
5836 * class List : ICountable {
5837 * public GetCount () : ICountable { ... }
5838 * public GetEnumerator () : IEnumerable { ... }
5839 * }
5840 *
5841 * ICountable aList = new List ();
5842 * IEnumerator anEnumer = aList.GetEnumerator (); << we are here
5843 * << baseRVal = aList
5844 * << sdtDecl = ICountable
5845 * << impl = IEnumerable
5846 * << name = GetEnumerator
5847 * << argsig = ()
5848 * So we have to cast aList from ICountable to IEnumerable.
5849 */
5850
5851 // make type token for the secondary interface type
5852 TokenType subIntfType = impl.MakeRefToken (name);
5853
5854 // make a temp variable of the secondary interface type
5855 CompValuTemp castBase = new CompValuTemp (subIntfType, this);
5856
5857 // output code to cast from the primary interface to the secondary interface
5858 // this is 2 basic steps:
5859 // 1) cast from primary interface object -> class object
5860 // ...gets it from interfaceObject.delegateArray[0].Target
5861 // 2) cast from class object -> secondary interface object
5862 // ...gets it from classObject.sdtcITable[interfaceIndex]
5863 baseRVal.PushVal (this, name, subIntfType);
5864
5865 // save result of casting in temp
5866 castBase.Pop (this, name);
5867
5868 // return temp reference
5869 baseRVal = castBase;
5870 }
5871
5872 return declVar;
5873 }
5874
5875 /**
5876 * @brief Find a member of a script-defined type class.
5877 * @param sdtType = reference to class declaration
5878 * @param name = name of member to find
5879 * @param argsig = argument signature used to select among overloaded members
5880 * @returns null: no such member found
5881 * else: the member found
5882 */
5883 public TokenDeclVar FindThisMember (TokenTypeSDTypeClass sdtType, TokenName name, TokenType[] argsig)
5884 {
5885 return FindThisMember (sdtType.decl, name, argsig);
5886 }
5887 public TokenDeclVar FindThisMember (TokenDeclSDTypeClass sdtDecl, TokenName name, TokenType[] argsig)
5888 {
5889 for (TokenDeclSDTypeClass sdtd = sdtDecl; sdtd != null; sdtd = sdtd.extends) {
5890 TokenDeclVar declVar = FindSingleMember (sdtd.members, name, argsig);
5891 if (declVar != null) return declVar;
5892 }
5893 return null;
5894 }
5895
5896 /**
5897 * @brief Look for a single member that matches the given name and argument signature
5898 * @param where = which dictionary to look in
5899 * @param name = basic name of the field or method, eg, "Printable"
5900 * @param argsig = argument types the method is being called with, eg, "(string)"
5901 * or null to find a field
5902 * @returns null: no member found
5903 * else: the member found
5904 */
5905 public TokenDeclVar FindSingleMember (VarDict where, TokenName name, TokenType[] argsig)
5906 {
5907 TokenDeclVar[] members = where.FindCallables (name.val, argsig);
5908 if (members == null) return null;
5909 if (members.Length > 1) {
5910 ErrorMsg (name, "more than one matching member");
5911 for (int i = 0; i < members.Length; i ++) {
5912 ErrorMsg (members[i], " " + members[i].argDecl.GetArgSig ());
5913 }
5914 }
5915 return members[0];
5916 }
5917
5918 /**
5919 * @brief Find an exact function name and argument signature match.
5920 * Also verify that the return value type is an exact match.
5921 * @param where = which method dictionary to look in
5922 * @param name = basic name of the method, eg, "Printable"
5923 * @param ret = expected return value type
5924 * @param argsig = argument types the method is being called with, eg, "(string)"
5925 * @returns null: no exact match found
5926 * else: the matching function
5927 */
5928 private TokenDeclVar FindExactWithRet (VarDict where, TokenName name, TokenType ret, TokenType[] argsig)
5929 {
5930 TokenDeclVar func = where.FindExact (name.val, argsig);
5931 if ((func != null) && (func.retType.ToString () != ret.ToString ())) {
5932 ErrorMsg (name, "return type mismatch, have " + func.retType.ToString () + ", expect " + ret.ToString ());
5933 }
5934 if (func != null) CheckAccess (func, name);
5935 return func;
5936 }
5937
5938 /**
5939 * @brief Check the private/protected/public access flags of a member.
5940 */
5941 private void CheckAccess (TokenDeclVar var, Token errorAt)
5942 {
5943 TokenDeclSDType nested;
5944 TokenDeclSDType definedBy = var.sdtClass;
5945 TokenDeclSDType accessedBy = curDeclFunc.sdtClass;
5946
5947 /*******************************\
5948 * Check member-level access *
5949 \*******************************/
5950
5951 /*
5952 * Note that if accessedBy is null, ie, accessing from global function (or event handlers),
5953 * anything tagged as SDT_PRIVATE or SDT_PROTECTED will fail.
5954 */
5955
5956 /*
5957 * Private means accessed by the class that defined the member or accessed by a nested class
5958 * of the class that defined the member.
5959 */
5960 if ((var.sdtFlags & ScriptReduce.SDT_PRIVATE) != 0) {
5961 for (nested = accessedBy; nested != null; nested = nested.outerSDType) {
5962 if (nested == definedBy) goto acc1ok;
5963 }
5964 ErrorMsg (errorAt, "private member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName);
5965 return;
5966 }
5967
5968 /*
5969 * Protected means:
5970 * If being accessed by an inner class, the inner class has access to it if the inner class derives
5971 * from the declaring class. It also has access to it if an outer class derives from the declaring
5972 * class.
5973 */
5974 if ((var.sdtFlags & ScriptReduce.SDT_PROTECTED) != 0) {
5975 for (nested = accessedBy; nested != null; nested = nested.outerSDType) {
5976 for (TokenDeclSDType rootward = nested; rootward != null; rootward = rootward.extends) {
5977 if (rootward == definedBy) goto acc1ok;
5978 }
5979 }
5980 ErrorMsg (errorAt, "protected member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName);
5981 return;
5982 }
5983 acc1ok:
5984
5985 /******************************\
5986 * Check class-level access *
5987 \******************************/
5988
5989 /*
5990 * If being accessed by same or inner class than where defined, it is ok.
5991 *
5992 * class DefiningClass {
5993 * varBeingAccessed;
5994 * .
5995 * .
5996 * .
5997 * class AccessingClass {
5998 * functionDoingAccess() { }
5999 * }
6000 * .
6001 * .
6002 * .
6003 * }
6004 */
6005 nested = accessedBy;
6006 while (true) {
6007 if (nested == definedBy) return;
6008 if (nested == null) break;
6009 nested = (TokenDeclSDTypeClass)nested.outerSDType;
6010 }
6011
6012 /*
6013 * It is being accessed by an outer class than where defined,
6014 * check for a 'private' or 'protected' class tag that blocks.
6015 */
6016 do {
6017
6018 /*
6019 * If the field's class is defined directly inside the accessing class,
6020 * access is allowed regardless of class-level private or protected tags.
6021 *
6022 * class AccessingClass {
6023 * functionDoingAccess() { }
6024 * class DefiningClass {
6025 * varBeingAccessed;
6026 * }
6027 * }
6028 */
6029 if (definedBy.outerSDType == accessedBy) return;
6030
6031 /*
6032 * If the field's class is defined two or more levels inside the accessing class,
6033 * access is denied if the defining class is tagged private.
6034 *
6035 * class AccessingClass {
6036 * functionDoingAccess() { }
6037 * .
6038 * .
6039 * .
6040 * class IntermediateClass {
6041 * private class DefiningClass {
6042 * varBeingAccessed;
6043 * }
6044 * }
6045 * .
6046 * .
6047 * .
6048 * }
6049 */
6050 if ((definedBy.accessLevel & ScriptReduce.SDT_PRIVATE) != 0) {
6051 ErrorMsg (errorAt, "member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName +
6052 " because of private class " + definedBy.longName.val);
6053 return;
6054 }
6055
6056 /*
6057 * Likewise, if DefiningClass is tagged protected, the AccessingClass must derive from the
6058 * IntermediateClass or access is denied.
6059 */
6060 if ((definedBy.accessLevel & ScriptReduce.SDT_PROTECTED) != 0) {
6061 for (TokenDeclSDType extends = accessedBy; extends != definedBy.outerSDType; extends = extends.extends) {
6062 if (extends == null) {
6063 ErrorMsg (errorAt, "member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName +
6064 " because of protected class " + definedBy.longName.val);
6065 return;
6066 }
6067 }
6068 }
6069
6070 /*
6071 * Check next outer level.
6072 */
6073 definedBy = definedBy.outerSDType;
6074 } while (definedBy != null);
6075 }
6076
6077 /**
6078 * @brief Convert a list of argument types to printable string, eg, "(list,string,float,integer)"
6079 * If given a null, return "" indicating it is a field not a method
6080 */
6081 public static string ArgSigString (TokenType[] argsig)
6082 {
6083 if (argsig == null) return "";
6084 StringBuilder sb = new StringBuilder ("(");
6085 for (int i = 0; i < argsig.Length; i ++) {
6086 if (i > 0) sb.Append (",");
6087 sb.Append (argsig[i].ToString ());
6088 }
6089 sb.Append (")");
6090 return sb.ToString ();
6091 }
6092
6093 /**
6094 * @brief output error message and remember that we did
6095 */
6096 public void ErrorMsg (Token token, string message)
6097 {
6098 if ((token == null) || (token.emsg == null)) token = errorMessageToken;
6099 if (!youveAnError || (token.file != lastErrorFile) || (token.line > lastErrorLine)) {
6100 token.ErrorMsg (message);
6101 youveAnError = true;
6102 lastErrorFile = token.file;
6103 lastErrorLine = token.line;
6104 }
6105 }
6106
6107 /**
6108 * @brief Find a private static method.
6109 * @param owner = class the method is part of
6110 * @param name = name of method to find
6111 * @param args = array of argument types
6112 * @returns pointer to method
6113 */
6114 public static MethodInfo GetStaticMethod (Type owner, string name, Type[] args)
6115 {
6116 MethodInfo mi = owner.GetMethod (name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, args, null);
6117 if (mi == null) {
6118 throw new Exception ("undefined method " + owner.ToString () + "." + name);
6119 }
6120 return mi;
6121 }
6122
6123 // http://wiki.secondlife.com/wiki/Rotation 'negate a rotation' says just negate .s component
6124 // but http://wiki.secondlife.com/wiki/LSL_Language_Test (lslangtest1.lsl) says negate all 4 values
6125 public static LSL_Rotation LSLRotationNegate (LSL_Rotation r) { return new LSL_Rotation (-r.x,-r.y,-r.z,-r.s); }
6126 public static LSL_Vector LSLVectorNegate (LSL_Vector v) { return -v; }
6127 public static string CatchExcToStr (Exception exc) { return exc.ToString(); }
6128 //public static void ConsoleWrite (string str) { Console.Write(str); }
6129
6130 /**
6131 * @brief Defines an internal label that is used as a target for 'break' and 'continue' statements.
6132 */
6133 private class BreakContTarg {
6134 public bool used;
6135 public ScriptMyLabel label;
6136 public TokenStmtBlock block;
6137
6138 public BreakContTarg (ScriptCodeGen scg, string name) {
6139 used = false; // assume it isn't referenced at all
6140 label = scg.ilGen.DefineLabel (name); // label that the break/continue jumps to
6141 block = scg.curStmtBlock; // { ... } that the break/continue label is in
6142 }
6143 }
6144 }
6145
6146 /**
6147 * @brief Marker interface indicates an exception that can't be caught by a script-level try/catch.
6148 */
6149 public interface IXMRUncatchable { }
6150
6151 /**
6152 * @brief Thrown by a script when it attempts to change to an undefined state.
6153 * These can be detected at compile time but the moron XEngine compiles
6154 * such things, so we compile them as runtime errors.
6155 */
6156 [SerializableAttribute]
6157 public class ScriptUndefinedStateException : Exception, ISerializable {
6158 public string stateName;
6159 public ScriptUndefinedStateException (string stateName) : base ("undefined state " + stateName) {
6160 this.stateName = stateName;
6161 }
6162 protected ScriptUndefinedStateException (SerializationInfo info, StreamingContext context) : base (info, context)
6163 { }
6164 }
6165
6166 /**
6167 * @brief Created by a throw statement.
6168 */
6169 [SerializableAttribute]
6170 public class ScriptThrownException : Exception, ISerializable {
6171 public object thrown;
6172
6173 /**
6174 * @brief Called by a throw statement to wrap the object in a unique
6175 * tag that capable of capturing a stack trace. Script can
6176 * unwrap it by calling xmrExceptionThrownValue().
6177 */
6178 public static Exception Wrap (object thrown)
6179 {
6180 return new ScriptThrownException (thrown);
6181 }
6182 private ScriptThrownException (object thrown) : base (thrown.ToString ())
6183 {
6184 this.thrown = thrown;
6185 }
6186
6187 /**
6188 * @brief Used by serialization/deserialization.
6189 */
6190 protected ScriptThrownException (SerializationInfo info, StreamingContext context) : base (info, context)
6191 { }
6192 }
6193
6194 /**
6195 * @brief Thrown by a script when it attempts to change to a defined state.
6196 */
6197 [SerializableAttribute]
6198 public class ScriptChangeStateException : Exception, ISerializable, IXMRUncatchable {
6199 public int newState;
6200 public ScriptChangeStateException (int newState) {
6201 this.newState = newState;
6202 }
6203 protected ScriptChangeStateException (SerializationInfo info, StreamingContext context) : base (info, context)
6204 { }
6205 }
6206
6207 /**
6208 * @brief We are restoring to the body of a catch { } so we need to
6209 * wrap the original exception in an outer exception, so the
6210 * system won't try to refill the stack trace.
6211 *
6212 * We don't mark this one serializable as it should never get
6213 * serialized out. It only lives from the throw to the very
6214 * beginning of the catch handler where it is promptly unwrapped.
6215 * No CheckRun() call can possibly intervene.
6216 */
6217 public class ScriptRestoreCatchException : Exception {
6218
6219 // old code uses these
6220 private object e;
6221 public ScriptRestoreCatchException (object e) {
6222 this.e = e;
6223 }
6224 public static object Unwrap (object o)
6225 {
6226 if (o is IXMRUncatchable) return null;
6227 if (o is ScriptRestoreCatchException) return ((ScriptRestoreCatchException)o).e;
6228 return o;
6229 }
6230
6231 // new code uses these
6232 private Exception ee;
6233 public ScriptRestoreCatchException (Exception ee) {
6234 this.ee = ee;
6235 }
6236 public static Exception Unwrap (Exception oo)
6237 {
6238 if (oo is IXMRUncatchable) return null;
6239 if (oo is ScriptRestoreCatchException) return ((ScriptRestoreCatchException)oo).ee;
6240 return oo;
6241 }
6242 }
6243
6244 [SerializableAttribute]
6245 public class ScriptBadCallNoException : Exception {
6246 public ScriptBadCallNoException (int callNo) : base ("bad callNo " + callNo) { }
6247 protected ScriptBadCallNoException (SerializationInfo info, StreamingContext context) : base (info, context)
6248 { }
6249 }
6250
6251 public class CVVMismatchException : Exception {
6252 public int oldcvv;
6253 public int newcvv;
6254
6255 public CVVMismatchException (int oldcvv, int newcvv) : base ("object version is " + oldcvv.ToString () +
6256 " but accept only " + newcvv.ToString ())
6257 {
6258 this.oldcvv = oldcvv;
6259 this.newcvv = newcvv;
6260 }
6261 }
6262}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs
new file mode 100644
index 0000000..9c0d621
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs
@@ -0,0 +1,2637 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using System;
30using System.Collections.Generic;
31using System.IO;
32using System.Reflection;
33using System.Reflection.Emit;
34using System.Text;
35
36
37/**
38 * @brief Wrapper class for ScriptMyILGen to do simple optimizations.
39 * The main one is to figure out which locals are active at the labels
40 * so the stack capture/restore code doesn't have to do everything.
41 * Second is it removes unnecessary back-to-back stloc/ldloc's.
42 */
43
44namespace OpenSim.Region.ScriptEngine.XMREngine
45{
46 /**
47 * @brief This is a list that keeps track of types pushed on the evaluation stack.
48 */
49 public class StackDepth : List<Type> {
50 public List<bool> isBoxeds = new List<bool> ();
51
52 /**
53 * @brief Clear both stacks.
54 */
55 public new void Clear ()
56 {
57 base.Clear ();
58 isBoxeds.Clear ();
59 }
60
61 /**
62 * @brief Pop call parameters and validate the types.
63 */
64 public void Pop (ParameterInfo[] pis)
65 {
66 int n = pis.Length;
67 int c = this.Count;
68 if (n > c) throw new Exception ("stack going negative");
69 for (int i = n; -- i >= 0;) {
70 -- c;
71 ExpectedVsOnStack (pis[i].ParameterType, this[c], isBoxeds[c]);
72 }
73 Pop (n);
74 }
75
76 /**
77 * @brief Pop values and validate the types.
78 */
79 public void Pop (Type[] ts)
80 {
81 int n = ts.Length;
82 int c = this.Count;
83 if (n > c) throw new Exception ("stack going negative");
84 for (int i = ts.Length; -- i >= 0;) {
85 -- c;
86 ExpectedVsOnStack (ts[i], this[c], isBoxeds[c]);
87 }
88 Pop (n);
89 }
90
91 /**
92 * @brief Pop a single value and validate the type.
93 */
94 public void Pop (Type t)
95 {
96 int c = this.Count;
97 if (c < 1) throw new Exception ("stack going negative");
98 ExpectedVsOnStack (t, this[c-1], isBoxeds[c-1]);
99 Pop (1);
100 }
101
102 /**
103 * @brief Pop a single value and validate that it is a numeric type.
104 */
105 public Type PopNumVal ()
106 {
107 int c = this.Count;
108 if (c < 1) throw new Exception ("stack going negative");
109 Type st = this[--c];
110 if (st == null) {
111 throw new Exception ("stack has null, expecting a numeric");
112 }
113 if (isBoxeds[c]) {
114 throw new Exception ("stack is boxed " + st.Name + ", expecting a numeric");
115 }
116 if ((st != typeof (bool)) && (st != typeof (char)) && (st != typeof (int)) &&
117 (st != typeof (long)) && (st != typeof (float)) && (st != typeof (double))) {
118 throw new Exception ("stack has " + st.Name + ", expecting a numeric");
119 }
120 return Pop (1);
121 }
122
123 /**
124 * @brief Pop a single value and validate that it is a reference type
125 */
126 public Type PopRef ()
127 {
128 int c = this.Count;
129 if (c < 1) throw new Exception ("stack going negative");
130 Type st = this[--c];
131 if ((st != null) && !isBoxeds[c] && st.IsValueType) {
132 throw new Exception ("stack has " + st.Name + ", expecting a ref type");
133 }
134 return Pop (1);
135 }
136
137 /**
138 * @brief Pop a single value and validate that it is a value type
139 */
140 public Type PopValue ()
141 {
142 int c = this.Count;
143 if (c < 1) throw new Exception ("stack going negative");
144 Type st = this[--c];
145 if (st == null) {
146 throw new Exception ("stack has null, expecting a value type");
147 }
148 if (!st.IsValueType) {
149 throw new Exception ("stack has " + st.Name + ", expecting a value type");
150 }
151 if (isBoxeds[c]) {
152 throw new Exception ("stack has boxed " + st.Name + ", expecting an unboxed value type");
153 }
154 return Pop (1);
155 }
156
157 // ex = what is expected to be on stack
158 // st = what is actually on stack (null for ldnull)
159 // stBoxed = stack value is boxed
160 public static void ExpectedVsOnStack (Type ex, Type st, bool stBoxed)
161 {
162 // ldnull pushed on stack can go into any pointer type
163 if (st == null) {
164 if (ex.IsByRef || ex.IsPointer || ex.IsClass || ex.IsInterface) return;
165 throw new Exception ("stack has null, expect " + ex.Name);
166 }
167
168 // simple case of expecting an object
169 // ...so the stack can have object,string, etc
170 // but we cant allow int = boxed int here
171 if (ex.IsAssignableFrom (st) && !stBoxed) return;
172
173 // case of expecting an enum on the stack
174 // but all the CIL code knows about are ints etc
175 // so convert the Enum type to integer or whatever
176 // and that should be assignable from what's on stack
177 if (ex.IsEnum && typeof (int).IsAssignableFrom (st)) return;
178
179 // bool, char, int are interchangeable on the stack
180 if ((ex == typeof (bool) || ex == typeof (char) || ex == typeof (int)) &&
181 (st == typeof (bool) || st == typeof (char) || st == typeof (int))) return;
182
183 // float and double are interchangeable on the stack
184 if ((ex == typeof (float) || ex == typeof (double)) &&
185 (st == typeof (float) || st == typeof (double))) return;
186
187 // object can accept any boxed type
188 if ((ex == typeof (object)) && stBoxed) return;
189
190 // otherwise, it is disallowed
191 throw new Exception ("stack has " + StackTypeString (st, stBoxed) + ", expect " + ex.Name);
192 }
193
194 /**
195 * @brief Pop values without any validation.
196 */
197 public Type Pop (int n)
198 {
199 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
200 Type lastPopped = null;
201 int c = this.Count;
202 if (n > c) throw new Exception ("stack going negative");
203 if (n > 0) {
204 lastPopped = this[c-n];
205 this.RemoveRange (c - n, n);
206 isBoxeds.RemoveRange (c - n, n);
207 }
208 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
209 return lastPopped;
210 }
211
212 /**
213 * @brief Peek at the n'th stack value.
214 * n = 0 : top of stack
215 * 1 : next to top
216 * ...
217 */
218 public Type Peek (int n)
219 {
220 int c = this.Count;
221 if (n > c - 1) throw new Exception ("stack going negative");
222 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
223 return this[c-n-1];
224 }
225 public bool PeekBoxed (int n)
226 {
227 int c = isBoxeds.Count;
228 if (n > c - 1) throw new Exception ("stack going negative");
229 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
230 return isBoxeds[c-n-1];
231 }
232
233 /**
234 * @brief Push a single value of the given type.
235 */
236 public void Push (Type t)
237 {
238 Push (t, false);
239 }
240 public void Push (Type t, bool isBoxed)
241 {
242 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
243 this.Add (t);
244 isBoxeds.Add (isBoxed);
245 }
246
247 /**
248 * @brief See if the types at a given label exactly match those on the stack.
249 * We should have the stack types be the same no matter how we branched
250 * or fell through to a particular label.
251 */
252 public void Matches (ScriptMyLabel label)
253 {
254 Type[] ts = label.stackDepth;
255 bool[] tsBoxeds = label.stackBoxeds;
256 int i;
257
258 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
259
260 if (ts == null) {
261 label.stackDepth = this.ToArray ();
262 label.stackBoxeds = isBoxeds.ToArray ();
263 } else if (ts.Length != this.Count) {
264 throw new Exception ("stack depth mismatch");
265 } else {
266 for (i = this.Count; -- i >= 0;) {
267 if (tsBoxeds[i] != this.isBoxeds[i]) goto mismatch;
268 if (ts[i] == this[i]) continue;
269 if ((ts[i] == typeof (bool) || ts[i] == typeof (char) || ts[i] == typeof (int)) &&
270 (this[i] == typeof (bool) || this[i] == typeof (char) || this[i] == typeof (int))) continue;
271 if ((ts[i] == typeof (double) || ts[i] == typeof (float)) &&
272 (this[i] == typeof (double) || this[i] == typeof (float))) continue;
273 goto mismatch;
274 }
275 }
276 return;
277 mismatch:
278 throw new Exception ("stack type mismatch: " + StackTypeString (ts[i], tsBoxeds[i]) + " vs " + StackTypeString (this[i], this.isBoxeds[i]));
279 }
280
281 private static string StackTypeString (Type ts, bool isBoxed)
282 {
283 if (!isBoxed) return ts.Name;
284 return "[" + ts.Name + "]";
285 }
286 }
287
288 /**
289 * @brief One of these per opcode and label in the function plus other misc markers.
290 * They form the CIL instruction stream of the function.
291 */
292 public abstract class GraphNode {
293 private static readonly bool DEBUG = false;
294
295 public const int OPINDENT = 4;
296 public const int OPDEBLEN = 12;
297
298 public ScriptCollector coll;
299 public GraphNodeBeginExceptionBlock tryBlock; // start of enclosing try block
300 // valid in the try section
301 // null in the catch/finally sections
302 // null outside of try block
303 // for the try node itself, links to outer try block
304 public GraphNodeBeginExceptionBlock excBlock; // start of enclosing try block
305 // valid in the try/catch/finally sections
306 // null outside of try/catch/finally block
307 // for the try node itself, links to outer try block
308
309 /*
310 * List of nodes in order as originally given.
311 */
312 public GraphNode nextLin, prevLin;
313 public int linSeqNo;
314
315 /**
316 * @brief Save pointer to collector.
317 */
318 public GraphNode (ScriptCollector coll)
319 {
320 this.coll = coll;
321 }
322
323 /**
324 * @brief Chain graph node to end of linear list.
325 */
326 public virtual void ChainLin ()
327 {
328 coll.lastLin.nextLin = this;
329 this.prevLin = coll.lastLin;
330 coll.lastLin = this;
331 this.tryBlock = coll.curTryBlock;
332 this.excBlock = coll.curExcBlock;
333
334 if (DEBUG) {
335 StringBuilder sb = new StringBuilder ("ChainLin*:");
336 sb.Append (coll.stackDepth.Count.ToString("D2"));
337 sb.Append (' ');
338 this.DebString (sb);
339 Console.WriteLine (sb.ToString ());
340 }
341 }
342
343 /**
344 * @brief Append full info to debugging string for printing out the instruction.
345 */
346 public void DebStringExt (StringBuilder sb)
347 {
348 int x = sb.Length;
349 sb.Append (this.linSeqNo.ToString ().PadLeft (5));
350 sb.Append (": ");
351 this.DebString (sb);
352
353 if (this.ReadsLocal () != null) ScriptCollector.PadToLength (sb, x + 60, " [read]");
354 if (this.WritesLocal () != null) ScriptCollector.PadToLength (sb, x + 68, " [write]");
355 ScriptCollector.PadToLength (sb, x + 72, " ->");
356 bool first = true;
357 foreach (GraphNode nn in this.NextNodes) {
358 if (first) {
359 sb.Append (nn.linSeqNo.ToString ().PadLeft (5));
360 first = false;
361 } else {
362 sb.Append (',');
363 sb.Append (nn.linSeqNo);
364 }
365 }
366 }
367
368 /**
369 * @brief See if it's possible for it to fall through to the next inline (nextLin) instruction.
370 */
371 public virtual bool CanFallThrough ()
372 {
373 return true;
374 }
375
376 /**
377 * @brief Append to debugging string for printing out the instruction.
378 */
379 public abstract void DebString (StringBuilder sb);
380 public override string ToString ()
381 {
382 StringBuilder sb = new StringBuilder ();
383 this.DebString (sb);
384 return sb.ToString ();
385 }
386
387 /**
388 * @brief See if this instruction reads a local variable.
389 */
390 public virtual ScriptMyLocal ReadsLocal () { return null; }
391
392 /**
393 * @brief See if this instruction writes a local variable.
394 */
395 public virtual ScriptMyLocal WritesLocal () { return null; }
396
397 /**
398 * @brief Write this instruction out to the wrapped object file.
399 */
400 public abstract void WriteOutOne (ScriptMyILGen ilGen);
401
402 /**
403 * @brief Iterate through all the possible next nodes, including the next inline node, if any.
404 * The next inline code is excluded if the instruction never falls through, eg, return, unconditional branch.
405 * It includes a possible conditional branch to the beginning of the corresponding catch/finally of every
406 * instruction in a try section.
407 */
408 private System.Collections.Generic.IEnumerable<GraphNode> nextNodes, nextNodesCatchFinally;
409 public System.Collections.Generic.IEnumerable<GraphNode> NextNodes
410 { get {
411 if (nextNodes == null) {
412 nextNodes = GetNNEnumerable ();
413 nextNodesCatchFinally = new NNEnumerableCatchFinally (this);
414 }
415 return nextNodesCatchFinally;
416 } }
417
418 /**
419 * @brief This acts as a wrapper around all the other NNEnumerable's below.
420 * It assumes every instruction in a try { } can throw an exception so it
421 * says that every instruction in a try { } can conditionally branch to
422 * the beginning of the corresponding catch { } or finally { }.
423 */
424 private class NNEnumerableCatchFinally : System.Collections.Generic.IEnumerable<GraphNode> {
425 private GraphNode gn;
426 public NNEnumerableCatchFinally (GraphNode gn)
427 {
428 this.gn = gn;
429 }
430 System.Collections.Generic.IEnumerator<GraphNode> System.Collections.Generic.IEnumerable<GraphNode>.GetEnumerator ()
431 {
432 return new NNEnumeratorCatchFinally (gn);
433 }
434 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
435 {
436 return new NNEnumeratorCatchFinally (gn);
437 }
438 }
439 private class NNEnumeratorCatchFinally : NNEnumeratorBase {
440 private GraphNode gn;
441 private int index = 0;
442 private System.Collections.Generic.IEnumerator<GraphNode> realEnumerator;
443 public NNEnumeratorCatchFinally (GraphNode gn)
444 {
445 this.gn = gn;
446 this.realEnumerator = gn.nextNodes.GetEnumerator ();
447 }
448 public override bool MoveNext ()
449 {
450 /*
451 * First off, return any targets the instruction can come up with.
452 */
453 if (realEnumerator.MoveNext ()) {
454 nn = realEnumerator.Current;
455 return true;
456 }
457
458 /*
459 * Then if this instruction is in a try section, say this instruction
460 * can potentially branch to the beginning of the corresponding
461 * catch/finally.
462 */
463 if ((index == 0) && (gn.tryBlock != null)) {
464 index ++;
465 nn = gn.tryBlock.catchFinallyBlock;
466 return true;
467 }
468
469 /*
470 * That's all we can do.
471 */
472 nn = null;
473 return false;
474 }
475 public override void Reset ()
476 {
477 realEnumerator.Reset ();
478 index = 0;
479 nn = null;
480 }
481 }
482
483 /**
484 * @brief This default iterator always returns the next inline node as the one-and-only next node.
485 * Other instructions need to override it if they can possibly do other than that.
486 */
487
488 /**
489 * @brief GetNNEnumerable() gets the nextnode enumerable part of a GraphNode,
490 * which in turn gives the list of nodes that can possibly be next in
491 * a flow-control sense. It simply instantiates the NNEnumerator sub-
492 * class which does the actual enumeration.
493 */
494 protected virtual System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
495 {
496 return new NNEnumerable (this, typeof (NNEnumerator));
497 }
498
499 private class NNEnumerator : NNEnumeratorBase {
500 private GraphNode gn;
501 private int index;
502 public NNEnumerator (GraphNode gn)
503 {
504 this.gn = gn;
505 }
506 public override bool MoveNext ()
507 {
508 switch (index) {
509 case 0: {
510 index ++;
511 nn = gn.nextLin;
512 return nn != null;
513 }
514 case 1: {
515 nn = null;
516 return false;
517 }
518 }
519 throw new Exception ();
520 }
521 public override void Reset ()
522 {
523 index = 0;
524 nn = null;
525 }
526 }
527 }
528
529 /**
530 * @brief Things that derive from this are the beginning of a block.
531 * A block of code is that which begins with a label or is the beginning of all code
532 * and it contains no labels, ie, it can't be jumped into other than at its beginning.
533 */
534 public abstract class GraphNodeBlock : GraphNode {
535 public List<ScriptMyLocal> localsWrittenBeforeRead = new List<ScriptMyLocal> ();
536 public List<ScriptMyLocal> localsReadBeforeWritten = new List<ScriptMyLocal> ();
537 public int hasBeenResolved;
538 public GraphNodeBlock (ScriptCollector coll) : base (coll) { }
539 }
540
541 /**
542 * @brief This placeholder is at the beginning of the code so the first few instructions
543 * belong to some block.
544 */
545 public class GraphNodeBegin : GraphNodeBlock {
546 public GraphNodeBegin (ScriptCollector coll) : base (coll) { }
547 public override void DebString (StringBuilder sb) { sb.Append ("begin"); }
548 public override void WriteOutOne (ScriptMyILGen ilGen) { }
549 }
550
551 /**
552 * @brief Beginning of try block.
553 */
554 public class GraphNodeBeginExceptionBlock : GraphNodeBlock {
555 public GraphNodeBeginExceptionBlock outerTryBlock; // next outer try opcode or null
556 public GraphNodeCatchFinallyBlock catchFinallyBlock; // start of associated catch or finally
557 public GraphNodeEndExceptionBlock endExcBlock; // end of associated catch or finally
558 public int excBlkSeqNo; // debugging
559
560 public GraphNodeBeginExceptionBlock (ScriptCollector coll) : base (coll)
561 { }
562
563 public override void ChainLin ()
564 {
565 base.ChainLin ();
566
567 // we should always start try blocks with nothing on stack
568 // ...as CLI wipes stack for various conditions
569 if (coll.stackDepth.Count != 0) {
570 throw new Exception ("stack depth " + coll.stackDepth.Count);
571 }
572 }
573
574 public override void DebString (StringBuilder sb)
575 {
576 sb.Append (" beginexceptionblock_");
577 sb.Append (excBlkSeqNo);
578 }
579
580 public override void WriteOutOne (ScriptMyILGen ilGen)
581 {
582 ilGen.BeginExceptionBlock ();
583 }
584 }
585
586 /**
587 * @brief Beginning of catch or finally block.
588 */
589 public abstract class GraphNodeCatchFinallyBlock : GraphNodeBlock {
590 public GraphNodeCatchFinallyBlock (ScriptCollector coll) : base (coll)
591 { }
592
593 public override void ChainLin ()
594 {
595 base.ChainLin ();
596
597 // we should always start catch/finally blocks with nothing on stack
598 // ...as CLI wipes stack for various conditions
599 if (coll.stackDepth.Count != 0) {
600 throw new Exception ("stack depth " + coll.stackDepth.Count);
601 }
602 }
603 }
604
605 /**
606 * @brief Beginning of catch block.
607 */
608 public class GraphNodeBeginCatchBlock : GraphNodeCatchFinallyBlock {
609 public Type excType;
610
611 public GraphNodeBeginCatchBlock (ScriptCollector coll, Type excType) : base (coll)
612 {
613 this.excType = excType;
614 }
615
616 public override void ChainLin ()
617 {
618 base.ChainLin ();
619
620 // catch block always enters with one value on stack
621 if (coll.stackDepth.Count != 0) {
622 throw new Exception ("stack depth " + coll.stackDepth.Count);
623 }
624 coll.stackDepth.Push (excType);
625 }
626
627 public override void DebString (StringBuilder sb)
628 {
629 sb.Append (" begincatchblock_");
630 sb.Append (excBlock.excBlkSeqNo);
631 }
632
633 public override void WriteOutOne (ScriptMyILGen ilGen)
634 {
635 ilGen.BeginCatchBlock (excType);
636 }
637
638 /**
639 * @brief The beginning of every catch { } conditinally branches to the beginning
640 * of all outer catch { }s up to and including the next outer finally { }.
641 */
642 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
643 {
644 return new NNEnumerable (this, typeof (NNEnumerator));
645 }
646
647 private class NNEnumerator : NNEnumeratorBase {
648 private GraphNodeBeginCatchBlock gn;
649 private int index;
650 public NNEnumerator (GraphNodeBeginCatchBlock gn)
651 {
652 this.gn = gn;
653 }
654 public override bool MoveNext ()
655 {
656 while (true) {
657 switch (index) {
658 case 0: {
659 // start with the fallthru
660 nn = gn.nextLin;
661 index ++;
662 return true;
663 }
664
665 case 1: {
666 // get the first outer catch { } or finally { }
667 // pretend we last returned beginning of this catch { }
668 // then loop back to get next outer catch { } or finally { }
669 nn = gn;
670 break;
671 }
672
673 case 2: {
674 // nn points to a catch { } previously returned
675 // get the corresponding try { }
676 GraphNodeBeginExceptionBlock nntry = nn.excBlock;
677
678 // step out to next outer try { }
679 nntry = nntry.excBlock;
680 if (nntry == null) break;
681
682 // return corresponding catch { } or finally { }
683 nn = nntry.catchFinallyBlock;
684
685 // if it's a finally { } we don't do anything after that
686 if (nn is GraphNodeBeginFinallyBlock) index ++;
687 return true;
688 }
689
690 case 3: {
691 // we've returned the fallthru, catches and one finally
692 // so there's nothing more to say
693 nn = null;
694 return false;
695 }
696
697 default: throw new Exception ();
698 }
699 index ++;
700 }
701 }
702 public override void Reset ()
703 {
704 index = 0;
705 nn = null;
706 }
707 }
708 }
709
710 /**
711 * @brief Beginning of finally block.
712 */
713 public class GraphNodeBeginFinallyBlock : GraphNodeCatchFinallyBlock {
714
715 // leaveTargets has a list of all the targets of any contained
716 // leave instructions, ie, where an endfinally can possibly jump.
717 // But only those targets within the next outer finally { }, we
718 // don't contain any targets outside of that, those targets are
719 // stored in the actual finally that will jump to the target.
720 // The endfinally enumerator assumes that it is always possible
721 // for it to jump to the next outer finally (as would happen for
722 // an uncaught exception), so no need to do anything special.
723 public List<GraphNodeBlock> leaveTargets = new List<GraphNodeBlock> ();
724
725 public GraphNodeBeginFinallyBlock (ScriptCollector coll) : base (coll)
726 { }
727
728 public override void DebString (StringBuilder sb)
729 {
730 sb.Append (" beginfinallyblock_");
731 sb.Append (excBlock.excBlkSeqNo);
732 }
733
734 public override void WriteOutOne (ScriptMyILGen ilGen)
735 {
736 ilGen.BeginFinallyBlock ();
737 }
738 }
739
740 /**
741 * @brief End of try/catch/finally block.
742 */
743 public class GraphNodeEndExceptionBlock : GraphNode {
744 public GraphNodeEndExceptionBlock (ScriptCollector coll) : base (coll)
745 { }
746
747 public override void ChainLin ()
748 {
749 base.ChainLin ();
750
751 // we should always end exception blocks with nothing on stack
752 // ...as CLI wipes stack for various conditions
753 if (coll.stackDepth.Count != 0) {
754 throw new Exception ("stack depth " + coll.stackDepth.Count);
755 }
756 }
757
758 public override void DebString (StringBuilder sb)
759 {
760 sb.Append (" endexceptionblock_");
761 sb.Append (excBlock.excBlkSeqNo);
762 }
763
764 public override void WriteOutOne (ScriptMyILGen ilGen)
765 {
766 ilGen.EndExceptionBlock ();
767 }
768 }
769
770 /**
771 * @brief Actual instruction emits...
772 */
773 public abstract class GraphNodeEmit : GraphNode {
774 public OpCode opcode;
775 public Token errorAt;
776
777 public GraphNodeEmit (ScriptCollector coll, Token errorAt, OpCode opcode) : base (coll)
778 {
779 this.opcode = opcode;
780 this.errorAt = errorAt;
781 }
782
783 public override void ChainLin ()
784 {
785 base.ChainLin ();
786
787 // compute resultant stack depth
788 int stack = coll.stackDepth.Count;
789
790 if ((stack != 0) && ((opcode == OpCodes.Endfinally) || (opcode == OpCodes.Leave) || (opcode == OpCodes.Rethrow))) {
791 throw new Exception (opcode + " stack depth " + stack);
792 }
793 if ((stack != 1) && (opcode == OpCodes.Throw)) {
794 throw new Exception (opcode + " stack depth " + stack);
795 }
796 }
797
798 /**
799 * @brief See if it's possible for it to fall through to the next inline (nextLin) instruction.
800 */
801 public override bool CanFallThrough ()
802 {
803 switch (opcode.FlowControl) {
804 case FlowControl.Branch: return false; // unconditional branch
805 case FlowControl.Break: return true; // break
806 case FlowControl.Call: return true; // call
807 case FlowControl.Cond_Branch: return true; // conditional branch
808 case FlowControl.Next: return true; // falls through to next instruction
809 case FlowControl.Return: return false; // return
810 case FlowControl.Throw: return false; // throw
811 default: {
812 string op = opcode.ToString ();
813 if (op == "volatile.") return true;
814 throw new Exception ("unknown flow control " + opcode.FlowControl + " for " + op);
815 }
816 }
817 }
818
819 // if followed by OpCodes.Pop, it can be discarded
820 public bool isPoppable
821 { get {
822 return
823 ((opcode.StackBehaviourPop == StackBehaviour.Pop0) && // ldarg,ldloc,ldsfld
824 (opcode.StackBehaviourPush == StackBehaviour.Push1)) ||
825 ((opcode.StackBehaviourPop == StackBehaviour.Pop0) && // ldarga,ldloca,ldc,ldsflda,...
826 (opcode.StackBehaviourPush == StackBehaviour.Pushi)) ||
827 (opcode == OpCodes.Ldnull) ||
828 (opcode == OpCodes.Ldc_R4) ||
829 (opcode == OpCodes.Ldc_R8) ||
830 (opcode == OpCodes.Ldstr) ||
831 (opcode == OpCodes.Ldc_I8) ||
832 (opcode == OpCodes.Dup);
833 } }
834
835 public override void DebString (StringBuilder sb)
836 {
837 sb.Append ("".PadRight (OPINDENT));
838 sb.Append (opcode.ToString ().PadRight (OPDEBLEN));
839 }
840
841 /**
842 * @brief If instruction is terminating, we say there is nothing following (eg, return).
843 * Otherwise, say the one-and-only next instruction is the next instruction inline.
844 */
845 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
846 {
847 return new NNEnumerable (this, typeof (NNEnumerator));
848 }
849
850 private class NNEnumerator : NNEnumeratorBase {
851 private GraphNodeEmit gn;
852 private int index;
853 public NNEnumerator (GraphNodeEmit gn)
854 {
855 this.gn = gn;
856 }
857 public override bool MoveNext ()
858 {
859 switch (index) {
860 case 0: {
861 if (gn.CanFallThrough ()) {
862 index ++;
863 nn = gn.nextLin;
864 return nn != null;
865 }
866 return false;
867 }
868 case 1: {
869 nn = null;
870 return false;
871 }
872 }
873 throw new Exception ();
874 }
875 public override void Reset ()
876 {
877 index = 0;
878 nn = null;
879 }
880 }
881 }
882
883 public class GraphNodeEmitNull : GraphNodeEmit {
884 public GraphNodeEmitNull (ScriptCollector coll, Token errorAt, OpCode opcode) : base (coll, errorAt, opcode)
885 { }
886
887 public override void ChainLin ()
888 {
889 base.ChainLin ();
890
891 switch (opcode.ToString ()) {
892 case "nop": break;
893 case "break": break;
894 case "volatile.": break;
895 case "ldarg.0": coll.stackDepth.Push (coll.wrapped.argTypes[0]); break;
896 case "ldarg.1": coll.stackDepth.Push (coll.wrapped.argTypes[1]); break;
897 case "ldarg.2": coll.stackDepth.Push (coll.wrapped.argTypes[2]); break;
898 case "ldarg.3": coll.stackDepth.Push (coll.wrapped.argTypes[3]); break;
899 case "ldnull": coll.stackDepth.Push (null); break;
900 case "ldc.i4.m1":
901 case "ldc.i4.0":
902 case "ldc.i4.1":
903 case "ldc.i4.2":
904 case "ldc.i4.3":
905 case "ldc.i4.4":
906 case "ldc.i4.5":
907 case "ldc.i4.6":
908 case "ldc.i4.7":
909 case "ldc.i4.8": {
910 coll.stackDepth.Push (typeof (int));
911 break;
912 }
913 case "dup": {
914 Type t = coll.stackDepth.Peek (0);
915 bool b = coll.stackDepth.PeekBoxed (0);
916 coll.stackDepth.Push (t, b);
917 break;
918 }
919 case "pop": {
920 coll.stackDepth.Pop (1);
921 break;
922 }
923 case "ret": {
924 int sd = (coll.wrapped.retType != typeof (void)) ? 1 : 0;
925 if (coll.stackDepth.Count != sd) throw new Exception ("bad stack depth");
926 if (sd > 0) {
927 coll.stackDepth.Pop (coll.wrapped.retType);
928 }
929 break;
930 }
931 case "add":
932 case "sub":
933 case "mul":
934 case "div":
935 case "div.un":
936 case "rem":
937 case "rem.un":
938 case "and":
939 case "or":
940 case "xor":
941 case "shl":
942 case "shr":
943 case "shr.un":
944 case "add.ovf":
945 case "add.ovf.un":
946 case "mul.ovf":
947 case "mul.ovf.un":
948 case "sub.ovf":
949 case "sub.ovf.un": {
950 coll.stackDepth.PopNumVal ();
951 Type t = coll.stackDepth.PopNumVal ();
952 coll.stackDepth.Push (t);
953 break;
954 }
955 case "neg":
956 case "not": {
957 Type t = coll.stackDepth.PopNumVal ();
958 coll.stackDepth.Push (t);
959 break;
960 }
961 case "conv.i1":
962 case "conv.i2":
963 case "conv.i4":
964 case "conv.i8":
965 case "conv.r4":
966 case "conv.r8":
967 case "conv.u4":
968 case "conv.u8":
969 case "conv.r.un":
970 case "conv.ovf.i1.un":
971 case "conv.ovf.i2.un":
972 case "conv.ovf.i4.un":
973 case "conv.ovf.i8.un":
974 case "conv.ovf.u1.un":
975 case "conv.ovf.u2.un":
976 case "conv.ovf.u4.un":
977 case "conv.ovf.u8.un":
978 case "conv.ovf.i.un":
979 case "conv.ovf.u.un":
980 case "conv.ovf.i1":
981 case "conv.ovf.u1":
982 case "conv.ovf.i2":
983 case "conv.ovf.u2":
984 case "conv.ovf.i4":
985 case "conv.ovf.u4":
986 case "conv.ovf.i8":
987 case "conv.ovf.u8":
988 case "conv.u2":
989 case "conv.u1":
990 case "conv.i":
991 case "conv.ovf.i":
992 case "conv.ovf.u":
993 case "conv.u": {
994 coll.stackDepth.PopNumVal ();
995 coll.stackDepth.Push (ConvToType (opcode));
996 break;
997 }
998 case "throw": {
999 if (coll.stackDepth.Count != 1) throw new Exception ("bad stack depth " + coll.stackDepth.Count);
1000 coll.stackDepth.PopRef ();
1001 break;
1002 }
1003 case "ldlen": {
1004 coll.stackDepth.Pop (typeof (string));
1005 coll.stackDepth.Push (typeof (int));
1006 break;
1007 }
1008 case "ldelem.i1":
1009 case "ldelem.u1":
1010 case "ldelem.i2":
1011 case "ldelem.u2":
1012 case "ldelem.i4":
1013 case "ldelem.u4":
1014 case "ldelem.i8":
1015 case "ldelem.i":
1016 case "ldelem.r4":
1017 case "ldelem.r8":
1018 case "ldelem.ref": {
1019 Type t = coll.stackDepth.Peek (1).GetElementType ();
1020 coll.stackDepth.Pop (typeof (int));
1021 coll.stackDepth.Pop (t.MakeArrayType ());
1022 coll.stackDepth.Push (t);
1023 break;
1024 }
1025 case "stelem.i":
1026 case "stelem.i1":
1027 case "stelem.i2":
1028 case "stelem.i4":
1029 case "stelem.i8":
1030 case "stelem.r4":
1031 case "stelem.r8":
1032 case "stelem.ref": {
1033 Type t = coll.stackDepth.Peek (2).GetElementType ();
1034 coll.stackDepth.Pop (t);
1035 coll.stackDepth.Pop (typeof (int));
1036 coll.stackDepth.Pop (t.MakeArrayType ());
1037 break;
1038 }
1039 case "endfinally":
1040 case "rethrow": {
1041 if (coll.stackDepth.Count != 0) throw new Exception ("bad stack depth " + coll.stackDepth.Count);
1042 break;
1043 }
1044 case "ceq": {
1045 Type t = coll.stackDepth.Pop (1);
1046 if (t == null) {
1047 coll.stackDepth.PopRef ();
1048 } else {
1049 coll.stackDepth.Pop (t);
1050 }
1051 coll.stackDepth.Push (typeof (int));
1052 break;
1053 }
1054 case "cgt":
1055 case "cgt.un":
1056 case "clt":
1057 case "clt.un": {
1058 coll.stackDepth.PopNumVal ();
1059 coll.stackDepth.PopNumVal ();
1060 coll.stackDepth.Push (typeof (int));
1061 break;
1062 }
1063 case "ldind.i4": {
1064 coll.stackDepth.Pop (typeof (int).MakeByRefType ());
1065 coll.stackDepth.Push (typeof (int));
1066 break;
1067 }
1068 case "stind.i4": {
1069 coll.stackDepth.Pop (typeof (int));
1070 coll.stackDepth.Pop (typeof (int).MakeByRefType ());
1071 break;
1072 }
1073 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1074 }
1075 }
1076
1077 private static Type ConvToType (OpCode opcode)
1078 {
1079 string s = opcode.ToString ();
1080 s = s.Substring (5); // strip off "conv."
1081 if (s.StartsWith ("ovf.")) s = s.Substring (4);
1082 if (s.EndsWith (".un")) s = s.Substring (0, s.Length - 3);
1083
1084 switch (s) {
1085 case "i": return typeof (IntPtr);
1086 case "i1": return typeof (sbyte);
1087 case "i2": return typeof (short);
1088 case "i4": return typeof (int);
1089 case "i8": return typeof (long);
1090 case "r":
1091 case "r4": return typeof (float);
1092 case "r8": return typeof (double);
1093 case "u1": return typeof (byte);
1094 case "u2": return typeof (ushort);
1095 case "u4": return typeof (uint);
1096 case "u8": return typeof (ulong);
1097 case "u": return typeof (UIntPtr);
1098 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1099 }
1100 }
1101
1102 public override void WriteOutOne (ScriptMyILGen ilGen)
1103 {
1104 ilGen.Emit (errorAt, opcode);
1105 }
1106 }
1107
1108 public class GraphNodeEmitNullEndfinally : GraphNodeEmitNull {
1109 public GraphNodeEmitNullEndfinally (ScriptCollector coll, Token errorAt) : base (coll, errorAt, OpCodes.Endfinally)
1110 { }
1111
1112 /**
1113 * @brief Endfinally can branch to:
1114 * 1) the corresponding EndExceptionBlock
1115 * 2) any of the corresponding BeginFinallyBlock's leaveTargets
1116 * 3) the next outer BeginFinallyBlock
1117 */
1118 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
1119 {
1120 return new NNEnumerable (this, typeof (NNEnumerator));
1121 }
1122
1123 private class NNEnumerator : NNEnumeratorBase {
1124 private GraphNodeEmitNullEndfinally gn;
1125 private IEnumerator<GraphNodeBlock> leaveTargetEnumerator;
1126 private int index;
1127 public NNEnumerator (GraphNodeEmitNullEndfinally gn)
1128 {
1129 this.gn = gn;
1130
1131 // endfinally instruction must be within some try/catch/finally mess
1132 GraphNodeBeginExceptionBlock thistry = gn.excBlock;
1133
1134 // endfinally instruction must be within some finally { } mess
1135 GraphNodeBeginFinallyBlock thisfin = (GraphNodeBeginFinallyBlock)thistry.catchFinallyBlock;
1136
1137 // get the list of the finally { } leave instruction targets
1138 this.leaveTargetEnumerator = thisfin.leaveTargets.GetEnumerator ();
1139 }
1140 public override bool MoveNext ()
1141 {
1142 while (true) {
1143 switch (index) {
1144
1145 // to start, return end of our finally { }
1146 case 0: {
1147 GraphNodeBeginExceptionBlock thistry = gn.excBlock;
1148 nn = thistry.endExcBlock;
1149 if (nn == null) throw new NullReferenceException ("thistry.endExcBlock");
1150 index ++;
1151 return true;
1152 }
1153
1154 // return next one of our finally { }'s leave targets
1155 // ie, where any leave instructions in the try { } want
1156 // the finally { } to go to when it finishes
1157 case 1: {
1158 if (this.leaveTargetEnumerator.MoveNext ()) {
1159 nn = this.leaveTargetEnumerator.Current;
1160 if (nn == null) throw new NullReferenceException ("this.leaveTargetEnumerator.Current");
1161 return true;
1162 }
1163 break;
1164 }
1165
1166 // return beginning of next outer finally { }
1167 case 2: {
1168 GraphNodeBeginExceptionBlock nntry = gn.excBlock;
1169 while ((nntry = nntry.excBlock) != null) {
1170 if (nntry.catchFinallyBlock is GraphNodeBeginFinallyBlock) {
1171 nn = nntry.catchFinallyBlock;
1172 if (nn == null) throw new NullReferenceException ("nntry.catchFinallyBlock");
1173 index ++;
1174 return true;
1175 }
1176 }
1177 break;
1178 }
1179
1180 // got nothing more
1181 case 3: {
1182 return false;
1183 }
1184
1185 default: throw new Exception ();
1186 }
1187 index ++;
1188 }
1189 }
1190 public override void Reset ()
1191 {
1192 leaveTargetEnumerator.Reset ();
1193 index = 0;
1194 nn = null;
1195 }
1196 }
1197 }
1198
1199 public class GraphNodeEmitField : GraphNodeEmit {
1200 public FieldInfo field;
1201
1202 public GraphNodeEmitField (ScriptCollector coll, Token errorAt, OpCode opcode, FieldInfo field) : base (coll, errorAt, opcode)
1203 {
1204 this.field = field;
1205 }
1206
1207 public override void ChainLin ()
1208 {
1209 base.ChainLin ();
1210
1211 switch (opcode.ToString ()) {
1212 case "ldfld": PopPointer (); coll.stackDepth.Push (field.FieldType); break;
1213 case "ldflda": PopPointer (); coll.stackDepth.Push (field.FieldType.MakeByRefType ()); break;
1214 case "stfld": coll.stackDepth.Pop (field.FieldType); PopPointer (); break;
1215 case "ldsfld": coll.stackDepth.Push (field.FieldType); break;
1216 case "ldsflda": coll.stackDepth.Push (field.FieldType.MakeByRefType ()); break;
1217 case "stsfld": coll.stackDepth.Pop (field.FieldType); break;
1218 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1219 }
1220 }
1221 private void PopPointer ()
1222 {
1223 Type t = field.DeclaringType; // get class/field type
1224 if (t.IsValueType) {
1225 Type brt = t.MakeByRefType (); // if value type, eg Vector, it can be pushed by reference or by value
1226 int c = coll.stackDepth.Count;
1227 if ((c > 0) && (coll.stackDepth[c-1] == brt)) t = brt;
1228 }
1229 coll.stackDepth.Pop (t); // type of what should be on the stack pointing to object or struct
1230 }
1231
1232 public override void DebString (StringBuilder sb)
1233 {
1234 base.DebString (sb);
1235 sb.Append (field.Name);
1236 }
1237
1238 public override void WriteOutOne (ScriptMyILGen ilGen)
1239 {
1240 ilGen.Emit (errorAt, opcode, field);
1241 }
1242 }
1243
1244 public class GraphNodeEmitLocal : GraphNodeEmit {
1245 public ScriptMyLocal myLocal;
1246
1247 public GraphNodeEmitLocal (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptMyLocal myLocal) : base (coll, errorAt, opcode)
1248 {
1249 this.myLocal = myLocal;
1250 }
1251
1252 public override void ChainLin ()
1253 {
1254 base.ChainLin ();
1255
1256 switch (opcode.ToString ()) {
1257 case "ldloc": coll.stackDepth.Push (myLocal.type); break;
1258 case "ldloca": coll.stackDepth.Push (myLocal.type.MakeByRefType ()); break;
1259 case "stloc": coll.stackDepth.Pop (myLocal.type); break;
1260 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1261 }
1262 }
1263
1264 public override void DebString (StringBuilder sb)
1265 {
1266 base.DebString (sb);
1267 sb.Append (myLocal.name);
1268 }
1269
1270 public override ScriptMyLocal ReadsLocal ()
1271 {
1272 if (opcode == OpCodes.Ldloc) return myLocal;
1273 if (opcode == OpCodes.Ldloca) return myLocal;
1274 if (opcode == OpCodes.Stloc) return null;
1275 throw new Exception ("unknown opcode " + opcode);
1276 }
1277 public override ScriptMyLocal WritesLocal ()
1278 {
1279 if (opcode == OpCodes.Ldloc) return null;
1280 if (opcode == OpCodes.Ldloca) return myLocal;
1281 if (opcode == OpCodes.Stloc) return myLocal;
1282 throw new Exception ("unknown opcode " + opcode);
1283 }
1284
1285 public override void WriteOutOne (ScriptMyILGen ilGen)
1286 {
1287 ilGen.Emit (errorAt, opcode, myLocal);
1288 }
1289 }
1290
1291 public class GraphNodeEmitType : GraphNodeEmit {
1292 public Type type;
1293
1294 public GraphNodeEmitType (ScriptCollector coll, Token errorAt, OpCode opcode, Type type) : base (coll, errorAt, opcode)
1295 {
1296 this.type = type;
1297 }
1298
1299 public override void ChainLin ()
1300 {
1301 base.ChainLin ();
1302
1303 switch (opcode.ToString ()) {
1304 case "castclass":
1305 case "isinst": {
1306 coll.stackDepth.PopRef ();
1307 coll.stackDepth.Push (type, type.IsValueType);
1308 break;
1309 }
1310 case "box": {
1311 if (!type.IsValueType) throw new Exception ("can't box a non-value type");
1312 coll.stackDepth.Pop (type);
1313 coll.stackDepth.Push (type, true);
1314 break;
1315 }
1316 case "unbox":
1317 case "unbox.any": {
1318 if (!type.IsValueType) throw new Exception ("can't unbox to a non-value type");
1319 coll.stackDepth.PopRef ();
1320 coll.stackDepth.Push (type);
1321 break;
1322 }
1323 case "newarr": {
1324 coll.stackDepth.Pop (typeof (int));
1325 coll.stackDepth.Push (type.MakeArrayType ());
1326 break;
1327 }
1328 case "sizeof": {
1329 coll.stackDepth.Pop (1);
1330 coll.stackDepth.Push (typeof (int));
1331 break;
1332 }
1333 case "ldelem": {
1334 coll.stackDepth.Pop (typeof (int));
1335 coll.stackDepth.Pop (type.MakeArrayType ());
1336 coll.stackDepth.Push (type);
1337 break;
1338 }
1339 case "ldelema": {
1340 coll.stackDepth.Pop (typeof (int));
1341 coll.stackDepth.Pop (type.MakeArrayType ());
1342 coll.stackDepth.Push (type.MakeByRefType ());
1343 break;
1344 }
1345 case "stelem": {
1346 coll.stackDepth.Pop (type);
1347 coll.stackDepth.Pop (typeof (int));
1348 coll.stackDepth.Pop (type.MakeArrayType ());
1349 break;
1350 }
1351 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1352 }
1353 }
1354
1355 public override void DebString (StringBuilder sb)
1356 {
1357 base.DebString (sb);
1358 sb.Append (type.Name);
1359 }
1360
1361 public override void WriteOutOne (ScriptMyILGen ilGen)
1362 {
1363 ilGen.Emit (errorAt, opcode, type);
1364 }
1365 }
1366
1367 public class GraphNodeEmitLabel : GraphNodeEmit {
1368 public ScriptMyLabel myLabel;
1369
1370 public GraphNodeEmitLabel (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptMyLabel myLabel) : base (coll, errorAt, opcode)
1371 {
1372 this.myLabel = myLabel;
1373 }
1374
1375 public override void ChainLin ()
1376 {
1377 base.ChainLin ();
1378
1379 switch (opcode.ToString ()) {
1380 case "brfalse.s":
1381 case "brtrue.s":
1382 case "brfalse":
1383 case "brtrue": {
1384 coll.stackDepth.Pop (1);
1385 break;
1386 }
1387 case "beq.s":
1388 case "bge.s":
1389 case "bgt.s":
1390 case "ble.s":
1391 case "blt.s":
1392 case "bne.un.s":
1393 case "bge.un.s":
1394 case "bgt.un.s":
1395 case "ble.un.s":
1396 case "blt.un.s":
1397 case "beq":
1398 case "bge":
1399 case "bgt":
1400 case "ble":
1401 case "blt":
1402 case "bne.un":
1403 case "bge.un":
1404 case "bgt.un":
1405 case "ble.un":
1406 case "blt.un": {
1407 coll.stackDepth.PopNumVal ();
1408 coll.stackDepth.PopNumVal ();
1409 break;
1410 }
1411 case "br":
1412 case "br.s": break;
1413 case "leave": {
1414 if (coll.stackDepth.Count != 0) throw new Exception ("bad stack depth " + coll.stackDepth.Count);
1415 break;
1416 }
1417 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1418 }
1419
1420 // if a target doesn't have a depth yet, set its depth to the depth after instruction executes
1421 // otherwise, make sure it matches all other branches to that target and what fell through to it
1422 coll.stackDepth.Matches (myLabel);
1423 }
1424
1425 public override void DebString (StringBuilder sb)
1426 {
1427 base.DebString (sb);
1428 sb.Append (myLabel.name);
1429 }
1430
1431 public override void WriteOutOne (ScriptMyILGen ilGen)
1432 {
1433 ilGen.Emit (errorAt, opcode, myLabel);
1434 }
1435
1436 /**
1437 * @brief Conditional branches return the next inline followed by the branch target
1438 * Unconditional branches return only the branch target
1439 * But if the target is outside our scope (eg __retlbl), omit it from the list
1440 */
1441 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
1442 {
1443 return new NNEnumerable (this, typeof (NNEnumerator));
1444 }
1445
1446 private class NNEnumerator : NNEnumeratorBase {
1447 private GraphNodeEmitLabel gn;
1448 private int index;
1449 public NNEnumerator (GraphNodeEmitLabel gn)
1450 {
1451 this.gn = gn;
1452 }
1453 public override bool MoveNext ()
1454 {
1455 switch (gn.opcode.FlowControl) {
1456 case FlowControl.Branch: {
1457 // unconditional branch just goes to target and nothing else
1458 switch (index) {
1459 case 0: {
1460 nn = gn.myLabel.whereAmI;
1461 index ++;
1462 return nn != null;
1463 }
1464 case 1: {
1465 return false;
1466 }
1467 }
1468 throw new Exception ();
1469 }
1470 case FlowControl.Cond_Branch: {
1471 // conditional branch goes inline and to target
1472 switch (index) {
1473 case 0: {
1474 nn = gn.nextLin;
1475 index ++;
1476 return true;
1477 }
1478 case 1: {
1479 nn = gn.myLabel.whereAmI;
1480 index ++;
1481 return nn != null;
1482 }
1483 case 2: {
1484 return false;
1485 }
1486 }
1487 throw new Exception ();
1488 }
1489 default: throw new Exception ("unknown flow control " + gn.opcode.FlowControl.ToString () +
1490 " of " + gn.opcode.ToString ());
1491 }
1492 }
1493 public override void Reset ()
1494 {
1495 index = 0;
1496 nn = null;
1497 }
1498 }
1499 }
1500
1501 public class GraphNodeEmitLabelLeave : GraphNodeEmitLabel {
1502 public GraphNodeBlock unwindTo; // if unwinding, innermost finally block being unwound
1503 // else, same as myTarget.whereAmI
1504 // null if unwinding completely out of scope, eg, __retlbl
1505
1506 public GraphNodeEmitLabelLeave (ScriptCollector coll, Token errorAt, ScriptMyLabel myLabel) : base (coll, errorAt, OpCodes.Leave, myLabel)
1507 { }
1508
1509 /**
1510 * @brief Leave instructions have exactly one unconditional next node.
1511 * Either the given target if within the same try block
1512 * or the beginning of the intervening finally block.
1513 */
1514 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
1515 {
1516 return new NNEnumerable (this, typeof (NNEnumerator));
1517 }
1518
1519 private class NNEnumerator : NNEnumeratorBase {
1520 private GraphNodeEmitLabelLeave gn;
1521 private int index;
1522 public NNEnumerator (GraphNodeEmitLabelLeave gn)
1523 {
1524 this.gn = gn;
1525 }
1526 public override bool MoveNext ()
1527 {
1528 if (index == 0) {
1529 nn = gn.unwindTo;
1530 index ++;
1531 return nn != null;
1532 }
1533 nn = null;
1534 return false;
1535 }
1536 public override void Reset ()
1537 {
1538 index = 0;
1539 nn = null;
1540 }
1541 }
1542 }
1543
1544 public class GraphNodeEmitLabels : GraphNodeEmit {
1545 public ScriptMyLabel[] myLabels;
1546
1547 public GraphNodeEmitLabels (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels) : base (coll, errorAt, opcode)
1548 {
1549 this.myLabels = myLabels;
1550 }
1551
1552 public override void ChainLin ()
1553 {
1554 base.ChainLin ();
1555
1556 switch (opcode.ToString ()) {
1557 case "switch": {
1558 coll.stackDepth.Pop (typeof (int));
1559 break;
1560 }
1561 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1562 }
1563
1564 // if a target doesn't have a depth yet, set its depth to the depth after instruction executes
1565 // otherwise, make sure it matches all other branches to that target and what fell through to it
1566 foreach (ScriptMyLabel myLabel in myLabels) {
1567 coll.stackDepth.Matches (myLabel);
1568 }
1569 }
1570
1571 public override void DebString (StringBuilder sb)
1572 {
1573 base.DebString (sb);
1574 bool first = true;
1575 foreach (ScriptMyLabel lbl in myLabels) {
1576 if (!first) sb.Append (',');
1577 sb.Append (lbl.name);
1578 first = false;
1579 }
1580 }
1581
1582 public override void WriteOutOne (ScriptMyILGen ilGen)
1583 {
1584 ilGen.Emit (errorAt, opcode, myLabels);
1585 }
1586
1587 /**
1588 * @brief Return list of all labels followed by the next linear instruction
1589 * But if the target is outside our scope (eg __retlbl), omit it from the list
1590 */
1591 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
1592 {
1593 return new NNEnumerable (this, typeof (NNEnumerator));
1594 }
1595
1596 private class NNEnumerator : NNEnumeratorBase {
1597 private GraphNodeEmitLabels gn;
1598 private int index;
1599 public NNEnumerator (GraphNodeEmitLabels gn)
1600 {
1601 this.gn = gn;
1602 }
1603 public override bool MoveNext ()
1604 {
1605 /*
1606 * Return next from list of switch case labels.
1607 */
1608 while (index < gn.myLabels.Length) {
1609 nn = gn.myLabels[index++].whereAmI;
1610 if (nn != null) return true;
1611 }
1612
1613 /*
1614 * If all ran out, the switch instruction falls through.
1615 */
1616 if (index == gn.myLabels.Length) {
1617 index ++;
1618 nn = gn.nextLin;
1619 return true;
1620 }
1621
1622 /*
1623 * Even ran out of that, say there's nothing more.
1624 */
1625 nn = null;
1626 return false;
1627 }
1628 public override void Reset ()
1629 {
1630 index = 0;
1631 nn = null;
1632 }
1633 }
1634 }
1635
1636 public class GraphNodeEmitIntMeth : GraphNodeEmit {
1637 public ScriptObjWriter method;
1638
1639 public GraphNodeEmitIntMeth (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptObjWriter method) : base (coll, errorAt, opcode)
1640 {
1641 this.method = method;
1642 }
1643
1644 public override void ChainLin ()
1645 {
1646 base.ChainLin ();
1647
1648 switch (opcode.ToString ()) {
1649 case "call": {
1650
1651 // calls have Varpop so pop the number of arguments
1652 // they are all static so there is no separate 'this' parameter
1653 coll.stackDepth.Pop (this.method.argTypes);
1654
1655 // calls are also Varpush so they push a return value iff non-void
1656 if (this.method.retType != typeof (void)) coll.stackDepth.Push (this.method.retType);
1657 break;
1658 }
1659
1660 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1661 }
1662 }
1663
1664 public override void DebString (StringBuilder sb)
1665 {
1666 base.DebString (sb);
1667 sb.Append (method.methName);
1668 }
1669
1670 public override void WriteOutOne (ScriptMyILGen ilGen)
1671 {
1672 ilGen.Emit (errorAt, opcode, method);
1673 }
1674 }
1675
1676 public class GraphNodeEmitExtMeth : GraphNodeEmit {
1677 public MethodInfo method;
1678
1679 public GraphNodeEmitExtMeth (ScriptCollector coll, Token errorAt, OpCode opcode, MethodInfo method) : base (coll, errorAt, opcode)
1680 {
1681 this.method = method;
1682 }
1683
1684 public override void ChainLin ()
1685 {
1686 base.ChainLin ();
1687
1688 switch (opcode.ToString ()) {
1689 case "call":
1690 case "callvirt": {
1691
1692 // calls have Varpop so pop the number of arguments
1693 coll.stackDepth.Pop (this.method.GetParameters ());
1694 if ((this.method.CallingConvention & CallingConventions.HasThis) != 0) {
1695 coll.stackDepth.Pop (method.DeclaringType);
1696 }
1697
1698 // calls are also Varpush so they push a return value iff non-void
1699 if (this.method.ReturnType != typeof (void)) coll.stackDepth.Push (this.method.ReturnType);
1700 break;
1701 }
1702
1703 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1704 }
1705 }
1706
1707 public override void DebString (StringBuilder sb)
1708 {
1709 base.DebString (sb);
1710 sb.Append (method.Name);
1711 }
1712
1713 public override void WriteOutOne (ScriptMyILGen ilGen)
1714 {
1715 ilGen.Emit (errorAt, opcode, method);
1716 }
1717 }
1718
1719 public class GraphNodeEmitCtor : GraphNodeEmit {
1720 public ConstructorInfo ctor;
1721
1722 public GraphNodeEmitCtor (ScriptCollector coll, Token errorAt, OpCode opcode, ConstructorInfo ctor) : base (coll, errorAt, opcode)
1723 {
1724 this.ctor = ctor;
1725 }
1726
1727 public override void ChainLin ()
1728 {
1729 base.ChainLin ();
1730
1731 switch (opcode.ToString ()) {
1732 case "newobj": {
1733 coll.stackDepth.Pop (ctor.GetParameters ());
1734 coll.stackDepth.Push (ctor.DeclaringType);
1735 break;
1736 }
1737
1738 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1739 }
1740 }
1741
1742 public override void DebString (StringBuilder sb)
1743 {
1744 base.DebString (sb);
1745 sb.Append (ctor.ReflectedType.Name);
1746 }
1747
1748 public override void WriteOutOne (ScriptMyILGen ilGen)
1749 {
1750 ilGen.Emit (errorAt, opcode, ctor);
1751 }
1752 }
1753
1754 public class GraphNodeEmitDouble : GraphNodeEmit {
1755 public double value;
1756
1757 public GraphNodeEmitDouble (ScriptCollector coll, Token errorAt, OpCode opcode, double value) : base (coll, errorAt, opcode)
1758 {
1759 this.value = value;
1760 }
1761
1762 public override void ChainLin ()
1763 {
1764 base.ChainLin ();
1765
1766 switch (opcode.ToString ()) {
1767 case "ldc.r8": coll.stackDepth.Push (typeof (double)); break;
1768 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1769 }
1770 }
1771
1772 public override void DebString (StringBuilder sb)
1773 {
1774 base.DebString (sb);
1775 sb.Append (value);
1776 }
1777
1778 public override void WriteOutOne (ScriptMyILGen ilGen)
1779 {
1780 ilGen.Emit (errorAt, opcode, value);
1781 }
1782 }
1783
1784 public class GraphNodeEmitFloat : GraphNodeEmit {
1785 public float value;
1786
1787 public GraphNodeEmitFloat (ScriptCollector coll, Token errorAt, OpCode opcode, float value) : base (coll, errorAt, opcode)
1788 {
1789 this.value = value;
1790 }
1791
1792 public override void ChainLin ()
1793 {
1794 base.ChainLin ();
1795
1796 switch (opcode.ToString ()) {
1797 case "ldc.r4": coll.stackDepth.Push (typeof (float)); break;
1798 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1799 }
1800 }
1801
1802 public override void DebString (StringBuilder sb)
1803 {
1804 base.DebString (sb);
1805 sb.Append (value);
1806 }
1807
1808 public override void WriteOutOne (ScriptMyILGen ilGen)
1809 {
1810 ilGen.Emit (errorAt, opcode, value);
1811 }
1812 }
1813
1814 public class GraphNodeEmitInt : GraphNodeEmit {
1815 public int value;
1816
1817 public GraphNodeEmitInt (ScriptCollector coll, Token errorAt, OpCode opcode, int value) : base (coll, errorAt, opcode)
1818 {
1819 this.value = value;
1820 }
1821
1822 public override void ChainLin ()
1823 {
1824 base.ChainLin ();
1825
1826 switch (opcode.ToString ()) {
1827 case "ldarg":
1828 case "ldarg.s": coll.stackDepth.Push (coll.wrapped.argTypes[value]); break;
1829 case "ldarga":
1830 case "ldarga.s": coll.stackDepth.Push (coll.wrapped.argTypes[value].MakeByRefType ()); break;
1831 case "starg":
1832 case "starg.s": coll.stackDepth.Pop (coll.wrapped.argTypes[value]); break;
1833 case "ldc.i4":
1834 case "ldc.i4.s": coll.stackDepth.Push (typeof (int)); break;
1835 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1836 }
1837 }
1838
1839 public override void DebString (StringBuilder sb)
1840 {
1841 base.DebString (sb);
1842 sb.Append (value);
1843 }
1844
1845 public override void WriteOutOne (ScriptMyILGen ilGen)
1846 {
1847 ilGen.Emit (errorAt, opcode, value);
1848 }
1849 }
1850
1851 public class GraphNodeEmitString : GraphNodeEmit {
1852 public string value;
1853
1854 public GraphNodeEmitString (ScriptCollector coll, Token errorAt, OpCode opcode, string value) : base (coll, errorAt, opcode)
1855 {
1856 this.value = value;
1857 }
1858
1859 public override void ChainLin ()
1860 {
1861 base.ChainLin ();
1862
1863 switch (opcode.ToString ()) {
1864 case "ldstr": coll.stackDepth.Push (typeof (string)); break;
1865 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1866 }
1867 }
1868
1869 public override void DebString (StringBuilder sb)
1870 {
1871 base.DebString (sb);
1872 sb.Append ("\"");
1873 sb.Append (value);
1874 sb.Append ("\"");
1875 }
1876
1877 public override void WriteOutOne (ScriptMyILGen ilGen)
1878 {
1879 ilGen.Emit (errorAt, opcode, value);
1880 }
1881 }
1882
1883 public class GraphNodeMarkLabel : GraphNodeBlock {
1884 public ScriptMyLabel myLabel;
1885
1886 public GraphNodeMarkLabel (ScriptCollector coll, ScriptMyLabel myLabel) : base (coll)
1887 {
1888 this.myLabel = myLabel;
1889 }
1890
1891 public override void ChainLin ()
1892 {
1893 base.ChainLin ();
1894
1895 // if previous instruction can fall through to this label,
1896 // if the label doesn't yet have a stack depth, mark it with current stack depth
1897 // else, the label's stack depth from forward branches and current stack depth must match
1898 // else,
1899 // label must have had a forward branch to it so we can know stack depth
1900 // set the current stack depth to the label's stack depth as of that forward branch
1901 if (myLabel.whereAmI.prevLin.CanFallThrough ()) {
1902 coll.stackDepth.Matches (myLabel);
1903 } else {
1904 if (myLabel.stackDepth == null) {
1905 throw new Exception ("stack depth unknown at " + myLabel.name);
1906 }
1907 coll.stackDepth.Clear ();
1908 int n = myLabel.stackDepth.Length;
1909 for (int i = 0; i < n; i ++) {
1910 coll.stackDepth.Push (myLabel.stackDepth[i], myLabel.stackBoxeds[i]);
1911 }
1912 }
1913 }
1914
1915 public override void DebString (StringBuilder sb)
1916 {
1917 sb.Append (myLabel.name);
1918 sb.Append (':');
1919 if (myLabel.stackDepth != null) {
1920 sb.Append (" [");
1921 sb.Append (myLabel.stackDepth.Length);
1922 sb.Append (']');
1923 }
1924 }
1925
1926 public override void WriteOutOne (ScriptMyILGen ilGen)
1927 {
1928 ilGen.MarkLabel (myLabel);
1929 }
1930 }
1931
1932
1933 /**
1934 * @brief Generates enumerator that steps through list of nodes that can
1935 * possibly be next in a flow-control sense.
1936 */
1937 public class NNEnumerable : System.Collections.Generic.IEnumerable<GraphNode> {
1938 private object[] cps;
1939 private ConstructorInfo ci;
1940
1941 public NNEnumerable (GraphNode gn, Type nnEnumeratorType)
1942 {
1943 this.cps = new object[] { gn };
1944 this.ci = nnEnumeratorType.GetConstructor (new Type[] { gn.GetType () });
1945 }
1946 System.Collections.Generic.IEnumerator<GraphNode> System.Collections.Generic.IEnumerable<GraphNode>.GetEnumerator ()
1947 {
1948 return (System.Collections.Generic.IEnumerator<GraphNode>) ci.Invoke (cps);
1949 }
1950 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
1951 {
1952 return (System.Collections.IEnumerator) ci.Invoke (cps);
1953 }
1954 }
1955
1956
1957 /**
1958 * @brief Steps through list of nodes that can possible be next in a flow-control sense.
1959 */
1960 public abstract class NNEnumeratorBase : System.Collections.Generic.IEnumerator<GraphNode> {
1961 protected GraphNode nn;
1962
1963 public abstract bool MoveNext ();
1964 public abstract void Reset ();
1965
1966 GraphNode System.Collections.Generic.IEnumerator<GraphNode>.Current {
1967 get { return this.nn; }
1968 }
1969 object System.Collections.IEnumerator.Current {
1970 get { return this.nn; }
1971 }
1972 void System.IDisposable.Dispose() { }
1973 }
1974
1975
1976 public class ScriptCollector : ScriptMyILGen {
1977 public static readonly bool DEBUG = false;
1978
1979 public ScriptObjWriter wrapped;
1980 public GraphNode firstLin, lastLin;
1981 private bool resolvedSomething;
1982 private int resolveSequence;
1983 private int excBlkSeqNos;
1984 public StackDepth stackDepth = new StackDepth ();
1985
1986 public GraphNodeBeginExceptionBlock curTryBlock = null; // pushed at beginning of try
1987 // popped at BEGINNING of catch/finally
1988 public GraphNodeBeginExceptionBlock curExcBlock = null; // pushed at beginning of try
1989 // popped at END of catch/finally
1990
1991 private List<ScriptMyLocal> declaredLocals = new List<ScriptMyLocal> ();
1992 private List<ScriptMyLabel> definedLabels = new List<ScriptMyLabel> ();
1993
1994 public string methName { get { return wrapped.methName; } }
1995
1996 /**
1997 * @brief Wrap the optimizer around the ScriptObjWriter to collect the instruction stream.
1998 * All stream-writing calls get saved to our graph nodes instead of being written to object file.
1999 */
2000 public ScriptCollector (ScriptObjWriter wrapped)
2001 {
2002 this.wrapped = wrapped;
2003 GraphNodeBegin gnb = new GraphNodeBegin (this);
2004 this.firstLin = gnb;
2005 this.lastLin = gnb;
2006 }
2007
2008 public ScriptMyLocal DeclareLocal (Type type, string name)
2009 {
2010 ScriptMyLocal loc = new ScriptMyLocal ();
2011 loc.name = name;
2012 loc.type = type;
2013 loc.number = wrapped.localNumber ++;
2014 declaredLocals.Add (loc);
2015 return loc;
2016 }
2017
2018 public ScriptMyLabel DefineLabel (string name)
2019 {
2020 ScriptMyLabel lbl = new ScriptMyLabel ();
2021 lbl.name = name;
2022 lbl.number = wrapped.labelNumber ++;
2023 definedLabels.Add (lbl);
2024 return lbl;
2025 }
2026
2027 public void BeginExceptionBlock ()
2028 {
2029 GraphNodeBeginExceptionBlock tryBlock = new GraphNodeBeginExceptionBlock (this);
2030 tryBlock.ChainLin ();
2031 tryBlock.excBlkSeqNo = ++ this.excBlkSeqNos;
2032 this.curExcBlock = tryBlock;
2033 this.curTryBlock = tryBlock;
2034 }
2035
2036 public void BeginCatchBlock (Type excType)
2037 {
2038 GraphNodeBeginCatchBlock catchBlock = new GraphNodeBeginCatchBlock (this, excType);
2039 catchBlock.ChainLin ();
2040 if (curExcBlock.catchFinallyBlock != null) throw new Exception ("only one catch/finally allowed per try");
2041 curExcBlock.catchFinallyBlock = catchBlock;
2042 curTryBlock = curExcBlock.tryBlock;
2043 }
2044
2045 public void BeginFinallyBlock ()
2046 {
2047 GraphNodeBeginFinallyBlock finallyBlock = new GraphNodeBeginFinallyBlock (this);
2048 finallyBlock.ChainLin ();
2049 if (curExcBlock.catchFinallyBlock != null) throw new Exception ("only one catch/finally allowed per try");
2050 curExcBlock.catchFinallyBlock = finallyBlock;
2051 curTryBlock = curExcBlock.tryBlock;
2052 }
2053
2054 public void EndExceptionBlock ()
2055 {
2056 GraphNodeEndExceptionBlock endExcBlock = new GraphNodeEndExceptionBlock (this);
2057 endExcBlock.ChainLin ();
2058 curExcBlock.endExcBlock = endExcBlock;
2059 curTryBlock = curExcBlock.tryBlock;
2060 curExcBlock = curExcBlock.excBlock;
2061 }
2062
2063 public void Emit (Token errorAt, OpCode opcode)
2064 {
2065 if (opcode == OpCodes.Endfinally) {
2066 new GraphNodeEmitNullEndfinally (this, errorAt).ChainLin ();
2067 } else {
2068 new GraphNodeEmitNull (this, errorAt, opcode).ChainLin ();
2069 }
2070 }
2071
2072 public void Emit (Token errorAt, OpCode opcode, FieldInfo field)
2073 {
2074 if (field == null) throw new ArgumentNullException ("field");
2075 new GraphNodeEmitField (this, errorAt, opcode, field).ChainLin ();
2076 }
2077
2078 public void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal)
2079 {
2080 new GraphNodeEmitLocal (this, errorAt, opcode, myLocal).ChainLin ();
2081 }
2082
2083 public void Emit (Token errorAt, OpCode opcode, Type type)
2084 {
2085 new GraphNodeEmitType (this, errorAt, opcode, type).ChainLin ();
2086 }
2087
2088 public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel)
2089 {
2090 if (opcode == OpCodes.Leave) {
2091 new GraphNodeEmitLabelLeave (this, errorAt, myLabel).ChainLin ();
2092 } else {
2093 new GraphNodeEmitLabel (this, errorAt, opcode, myLabel).ChainLin ();
2094 }
2095 }
2096
2097 public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels)
2098 {
2099 new GraphNodeEmitLabels (this, errorAt, opcode, myLabels).ChainLin ();
2100 }
2101
2102 public void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method)
2103 {
2104 if (method == null) throw new ArgumentNullException ("method");
2105 new GraphNodeEmitIntMeth (this, errorAt, opcode, method).ChainLin ();
2106 }
2107
2108 public void Emit (Token errorAt, OpCode opcode, MethodInfo method)
2109 {
2110 if (method == null) throw new ArgumentNullException ("method");
2111 new GraphNodeEmitExtMeth (this, errorAt, opcode, method).ChainLin ();
2112 }
2113
2114 public void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor)
2115 {
2116 if (ctor == null) throw new ArgumentNullException ("ctor");
2117 new GraphNodeEmitCtor (this, errorAt, opcode, ctor).ChainLin ();
2118 }
2119
2120 public void Emit (Token errorAt, OpCode opcode, double value)
2121 {
2122 new GraphNodeEmitDouble (this, errorAt, opcode, value).ChainLin ();
2123 }
2124
2125 public void Emit (Token errorAt, OpCode opcode, float value)
2126 {
2127 new GraphNodeEmitFloat (this, errorAt, opcode, value).ChainLin ();
2128 }
2129
2130 public void Emit (Token errorAt, OpCode opcode, int value)
2131 {
2132 new GraphNodeEmitInt (this, errorAt, opcode, value).ChainLin ();
2133 }
2134
2135 public void Emit (Token errorAt, OpCode opcode, string value)
2136 {
2137 new GraphNodeEmitString (this, errorAt, opcode, value).ChainLin ();
2138 }
2139
2140 public void MarkLabel (ScriptMyLabel myLabel)
2141 {
2142 myLabel.whereAmI = new GraphNodeMarkLabel (this, myLabel);
2143 myLabel.whereAmI.ChainLin ();
2144 }
2145
2146 /**
2147 * @brief Write the whole graph out to the object file.
2148 */
2149 public ScriptMyILGen WriteOutAll ()
2150 {
2151 foreach (ScriptMyLocal loc in declaredLocals) {
2152 if (loc.isReferenced) wrapped.DeclareLocal (loc);
2153 }
2154 foreach (ScriptMyLabel lbl in definedLabels) {
2155 wrapped.DefineLabel (lbl);
2156 }
2157 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2158 gn.WriteOutOne (wrapped);
2159 }
2160 return wrapped;
2161 }
2162
2163 /**
2164 * @brief Perform optimizations.
2165 */
2166 public void Optimize ()
2167 {
2168 if (curExcBlock != null) throw new Exception ("exception block still open");
2169
2170 /*
2171 * If an instruction says it doesn't fall through, remove all instructions to
2172 * the end of the block.
2173 */
2174 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2175 if (!gn.CanFallThrough ()) {
2176 GraphNode nn;
2177 while (((nn = gn.nextLin) != null) && !(nn is GraphNodeBlock) &&
2178 !(nn is GraphNodeEndExceptionBlock)) {
2179 if ((gn.nextLin = nn.nextLin) != null) {
2180 nn.nextLin.prevLin = gn;
2181 }
2182 }
2183 }
2184 }
2185
2186 /*
2187 * Scan for OpCodes.Leave instructions.
2188 * For each found, its target for flow analysis purposes is the beginning of the corresponding
2189 * finally block. And the end of the finally block gets a conditional branch target of the
2190 * leave instruction's target. A leave instruction can unwind zero or more finally blocks.
2191 */
2192 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2193 if (gn is GraphNodeEmitLabelLeave) {
2194 GraphNodeEmitLabelLeave leaveInstr = (GraphNodeEmitLabelLeave)gn; // the leave instruction
2195 GraphNodeMarkLabel leaveTarget = leaveInstr.myLabel.whereAmI; // label being targeted by leave
2196 GraphNodeBeginExceptionBlock leaveTargetsTryBlock = // try block directly enclosing leave target
2197 (leaveTarget == null) ? null : leaveTarget.tryBlock; // ...it must not be unwound
2198
2199 /*
2200 * Step through try { }s from the leave instruction towards its target looking for try { }s with finally { }s.
2201 * The leave instruction unconditionally branches to the beginning of the innermost one found.
2202 * The end of the last one found conditionally branches to the leave instruction's target.
2203 * If none found, the leave is a simple unconditional branch to its target.
2204 */
2205 GraphNodeBeginFinallyBlock innerFinallyBlock = null;
2206 for (GraphNodeBeginExceptionBlock tryBlock = leaveInstr.tryBlock;
2207 tryBlock != leaveTargetsTryBlock;
2208 tryBlock = tryBlock.tryBlock) {
2209 if (tryBlock == null) throw new Exception ("leave target not at or outer to leave instruction");
2210 GraphNodeCatchFinallyBlock cfb = tryBlock.catchFinallyBlock;
2211 if (cfb is GraphNodeBeginFinallyBlock) {
2212 if (innerFinallyBlock == null) {
2213 leaveInstr.unwindTo = cfb;
2214 }
2215 innerFinallyBlock = (GraphNodeBeginFinallyBlock)cfb;
2216 }
2217 }
2218
2219 /*
2220 * The end of the outermost finally being unwound can conditionally jump to the target of the leave instruction.
2221 * In the case of no finallies being unwound, the leave is just a simple unconditional branch.
2222 */
2223 if (innerFinallyBlock == null) {
2224 leaveInstr.unwindTo = leaveTarget;
2225 } else if (!innerFinallyBlock.leaveTargets.Contains (leaveTarget)) {
2226 innerFinallyBlock.leaveTargets.Add (leaveTarget);
2227 }
2228 }
2229 }
2230
2231 /*
2232 * See which variables a particular block reads before writing.
2233 * This just considers the block itself and nothing that it branches to or fallsthru to.
2234 */
2235 GraphNodeBlock currentBlock = null;
2236 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2237 if (gn is GraphNodeBlock) currentBlock = (GraphNodeBlock)gn;
2238 ScriptMyLocal rdlcl = gn.ReadsLocal ();
2239 if ((rdlcl != null) &&
2240 !currentBlock.localsWrittenBeforeRead.Contains (rdlcl) &&
2241 !currentBlock.localsReadBeforeWritten.Contains (rdlcl)) {
2242 currentBlock.localsReadBeforeWritten.Add (rdlcl);
2243 }
2244 ScriptMyLocal wrlcl = gn.WritesLocal ();
2245 if ((wrlcl != null) &&
2246 !currentBlock.localsWrittenBeforeRead.Contains (wrlcl) &&
2247 !currentBlock.localsReadBeforeWritten.Contains (wrlcl)) {
2248 currentBlock.localsWrittenBeforeRead.Add (wrlcl);
2249 }
2250 }
2251
2252 /*
2253 * For every block we branch to, add that blocks readables to our list of readables,
2254 * because we need to have those values valid on entry to our block. But if we write the
2255 * variable before we can possibly branch to that block, then we don't need to have it valid
2256 * on entry to our block. So basically it looks like the branch instruction is reading
2257 * everything required by any blocks it can branch to.
2258 */
2259 do {
2260 this.resolvedSomething = false;
2261 this.resolveSequence ++;
2262 this.ResolveBlock ((GraphNodeBlock)firstLin);
2263 } while (this.resolvedSomething);
2264
2265 /*
2266 * Repeat the cutting loops as long as we keep finding stuff.
2267 */
2268 bool didSomething;
2269 do {
2270 didSomething = false;
2271
2272 /*
2273 * Strip out ldc.i4.1/xor/ldc.i4.1/xor
2274 */
2275 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2276 if (!(gn is GraphNodeEmit)) continue;
2277 GraphNodeEmit xor2 = (GraphNodeEmit)gn;
2278 if (xor2.opcode != OpCodes.Xor) continue;
2279 if (!(xor2.prevLin is GraphNodeEmit)) continue;
2280 GraphNodeEmit ld12 = (GraphNodeEmit)xor2.prevLin;
2281 if (ld12.opcode != OpCodes.Ldc_I4_1) continue;
2282 if (!(ld12.prevLin is GraphNodeEmit)) continue;
2283 GraphNodeEmit xor1 = (GraphNodeEmit)ld12.prevLin;
2284 if (xor1.opcode != OpCodes.Xor) continue;
2285 if (!(xor2.prevLin is GraphNodeEmit)) continue;
2286 GraphNodeEmit ld11 = (GraphNodeEmit)xor1.prevLin;
2287 if (ld11.opcode != OpCodes.Ldc_I4_1) continue;
2288 ld11.prevLin.nextLin = xor2.nextLin;
2289 xor2.nextLin.prevLin = ld11.prevLin;
2290 didSomething = true;
2291 }
2292
2293 /*
2294 * Replace c{cond}/ldc.i4.1/xor/br{false,true} -> c{cond}/br{true,false}
2295 */
2296 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2297 if (!(gn is GraphNodeEmit)) continue;
2298 GraphNodeEmit brft = (GraphNodeEmit)gn;
2299 if ((brft.opcode != OpCodes.Brfalse) && (brft.opcode != OpCodes.Brtrue)) continue;
2300 if (!(brft.prevLin is GraphNodeEmit)) continue;
2301 GraphNodeEmit xor = (GraphNodeEmit)brft.prevLin;
2302 if (xor.opcode != OpCodes.Xor) continue;
2303 if (!(xor.prevLin is GraphNodeEmit)) continue;
2304 GraphNodeEmit ldc = (GraphNodeEmit)xor.prevLin;
2305 if (ldc.opcode != OpCodes.Ldc_I4_1) continue;
2306 if (!(ldc.prevLin is GraphNodeEmit)) continue;
2307 GraphNodeEmit cmp = (GraphNodeEmit)ldc.prevLin;
2308 if (cmp.opcode.StackBehaviourPop != StackBehaviour.Pop1_pop1) continue;
2309 if (cmp.opcode.StackBehaviourPush != StackBehaviour.Pushi) continue;
2310 cmp.nextLin = brft;
2311 brft.prevLin = cmp;
2312 brft.opcode = (brft.opcode == OpCodes.Brfalse) ? OpCodes.Brtrue : OpCodes.Brfalse;
2313 didSomething = true;
2314 }
2315
2316 /*
2317 * Replace c{cond}/br{false,true} -> b{!,}{cond}
2318 */
2319 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2320 if (!(gn is GraphNodeEmit)) continue;
2321 GraphNodeEmit brft = (GraphNodeEmit)gn;
2322 if ((brft.opcode != OpCodes.Brfalse) && (brft.opcode != OpCodes.Brtrue)) continue;
2323 if (!(brft.prevLin is GraphNodeEmit)) continue;
2324 GraphNodeEmit cmp = (GraphNodeEmit)brft.prevLin;
2325 if (cmp.opcode.StackBehaviourPop != StackBehaviour.Pop1_pop1) continue;
2326 if (cmp.opcode.StackBehaviourPush != StackBehaviour.Pushi) continue;
2327 cmp.prevLin.nextLin = brft;
2328 brft.prevLin = cmp.prevLin;
2329 bool brtru = (brft.opcode == OpCodes.Brtrue);
2330 if (cmp.opcode == OpCodes.Ceq) brft.opcode = brtru ? OpCodes.Beq : OpCodes.Bne_Un;
2331 else if (cmp.opcode == OpCodes.Cgt) brft.opcode = brtru ? OpCodes.Bgt : OpCodes.Ble;
2332 else if (cmp.opcode == OpCodes.Cgt_Un) brft.opcode = brtru ? OpCodes.Bgt_Un : OpCodes.Ble_Un;
2333 else if (cmp.opcode == OpCodes.Clt) brft.opcode = brtru ? OpCodes.Blt : OpCodes.Bge;
2334 else if (cmp.opcode == OpCodes.Clt_Un) brft.opcode = brtru ? OpCodes.Blt_Un : OpCodes.Bge_Un;
2335 else throw new Exception ();
2336 didSomething = true;
2337 }
2338
2339 /*
2340 * Replace ld{c.i4.0,null}/br{ne.un,eq} -> br{true,false}
2341 */
2342 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2343 if (!(gn is GraphNodeEmit)) continue;
2344 GraphNodeEmit brcc = (GraphNodeEmit)gn;
2345 if ((brcc.opcode != OpCodes.Bne_Un) && (brcc.opcode != OpCodes.Beq)) continue;
2346 if (!(brcc.prevLin is GraphNodeEmit)) continue;
2347 GraphNodeEmit ldc0 = (GraphNodeEmit)brcc.prevLin;
2348 if ((ldc0.opcode != OpCodes.Ldc_I4_0) && (ldc0.opcode != OpCodes.Ldnull)) continue;
2349 ldc0.prevLin.nextLin = brcc;
2350 brcc.prevLin = ldc0.prevLin;
2351 brcc.opcode = (brcc.opcode == OpCodes.Bne_Un) ? OpCodes.Brtrue : OpCodes.Brfalse;
2352 didSomething = true;
2353 }
2354
2355 /*
2356 * Replace:
2357 * ldloc v1
2358 * stloc v2
2359 * ld<anything> except ld<anything> v2
2360 * ldloc v2
2361 * ...v2 unreferenced hereafter
2362 * With:
2363 * ld<anything> except ld<anything> v2
2364 * ldloc v1
2365 */
2366 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2367
2368 // check for 'ldloc v1' instruction
2369 if (!(gn is GraphNodeEmitLocal)) continue;
2370 GraphNodeEmitLocal ldlv1 = (GraphNodeEmitLocal)gn;
2371 if (ldlv1.opcode != OpCodes.Ldloc) continue;
2372
2373 // check for 'stloc v2' instruction
2374 if (!(ldlv1.nextLin is GraphNodeEmitLocal)) continue;
2375 GraphNodeEmitLocal stlv2 = (GraphNodeEmitLocal)ldlv1.nextLin;
2376 if (stlv2.opcode != OpCodes.Stloc) continue;
2377
2378 // check for 'ld<anything> except ld<anything> v2' instruction
2379 if (!(stlv2.nextLin is GraphNodeEmit)) continue;
2380 GraphNodeEmit ldany = (GraphNodeEmit)stlv2.nextLin;
2381 if (!ldany.opcode.ToString ().StartsWith ("ld")) continue;
2382 if ((ldany is GraphNodeEmitLocal) &&
2383 ((GraphNodeEmitLocal)ldany).myLocal == stlv2.myLocal) continue;
2384
2385 // check for 'ldloc v2' instruction
2386 if (!(ldany.nextLin is GraphNodeEmitLocal)) continue;
2387 GraphNodeEmitLocal ldlv2 = (GraphNodeEmitLocal)ldany.nextLin;
2388 if (ldlv2.opcode != OpCodes.Ldloc) continue;
2389 if (ldlv2.myLocal != stlv2.myLocal) continue;
2390
2391 // check that v2 is not needed after this at all
2392 if (IsLocalNeededAfterThis (ldlv2, ldlv2.myLocal)) continue;
2393
2394 // make 'ld<anything>...' the first instruction
2395 ldany.prevLin = ldlv1.prevLin;
2396 ldany.prevLin.nextLin = ldany;
2397
2398 // make 'ldloc v1' the second instruction
2399 ldany.nextLin = ldlv1;
2400 ldlv1.prevLin = ldany;
2401
2402 // and make 'ldloc v1' the last instruction
2403 ldlv1.nextLin = ldlv2.nextLin;
2404 ldlv1.nextLin.prevLin = ldlv1;
2405
2406 didSomething = true;
2407 }
2408
2409 /*
2410 * Remove all the stloc/ldloc that are back-to-back without the local
2411 * being needed afterwards. If it is needed afterwards, replace the
2412 * stloc/ldloc with dup/stloc.
2413 */
2414 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2415 if ((gn is GraphNodeEmitLocal) &&
2416 (gn.prevLin is GraphNodeEmitLocal)) {
2417 GraphNodeEmitLocal stloc = (GraphNodeEmitLocal)gn.prevLin;
2418 GraphNodeEmitLocal ldloc = (GraphNodeEmitLocal)gn;
2419 if ((stloc.opcode == OpCodes.Stloc) &&
2420 (ldloc.opcode == OpCodes.Ldloc) &&
2421 (stloc.myLocal == ldloc.myLocal)) {
2422 if (IsLocalNeededAfterThis (ldloc, ldloc.myLocal)) {
2423 GraphNodeEmitNull dup = new GraphNodeEmitNull (this, stloc.errorAt, OpCodes.Dup);
2424 dup.nextLin = stloc;
2425 dup.prevLin = stloc.prevLin;
2426 stloc.nextLin = ldloc.nextLin;
2427 stloc.prevLin = dup;
2428 dup.prevLin.nextLin = dup;
2429 stloc.nextLin.prevLin = stloc;
2430 gn = stloc;
2431 } else {
2432 stloc.prevLin.nextLin = ldloc.nextLin;
2433 ldloc.nextLin.prevLin = stloc.prevLin;
2434 gn = stloc.prevLin;
2435 }
2436 didSomething = true;
2437 }
2438 }
2439 }
2440
2441 /*
2442 * Remove all write-only local variables, ie, those with no ldloc[a] references.
2443 * Replace any stloc instructions with pops.
2444 */
2445 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2446 ScriptMyLocal rdlcl = gn.ReadsLocal ();
2447 if (rdlcl != null) rdlcl.isReferenced = true;
2448 }
2449 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2450 ScriptMyLocal wrlcl = gn.WritesLocal ();
2451 if ((wrlcl != null) && !wrlcl.isReferenced) {
2452 if (!(gn is GraphNodeEmitLocal) || (((GraphNodeEmitLocal)gn).opcode != OpCodes.Stloc)) {
2453 throw new Exception ("expecting stloc");
2454 }
2455 GraphNodeEmitNull pop = new GraphNodeEmitNull (this, ((GraphNodeEmit)gn).errorAt, OpCodes.Pop);
2456 pop.nextLin = gn.nextLin;
2457 pop.prevLin = gn.prevLin;
2458 gn.nextLin.prevLin = pop;
2459 gn.prevLin.nextLin = pop;
2460 gn = pop;
2461 didSomething = true;
2462 }
2463 }
2464
2465 /*
2466 * Remove any Ld<const>/Dup,Pop.
2467 */
2468 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2469 if ((gn is GraphNodeEmit) &&
2470 (gn.nextLin is GraphNodeEmit)) {
2471 GraphNodeEmit gne = (GraphNodeEmit)gn;
2472 GraphNodeEmit nne = (GraphNodeEmit)gn.nextLin;
2473 if (gne.isPoppable && (nne.opcode == OpCodes.Pop)) {
2474 gne.prevLin.nextLin = nne.nextLin;
2475 nne.nextLin.prevLin = gne.prevLin;
2476 gn = gne.prevLin;
2477 didSomething = true;
2478 }
2479 }
2480 }
2481 } while (didSomething);
2482
2483 /*
2484 * Dump out the results.
2485 */
2486 if (DEBUG) {
2487 Console.WriteLine ("");
2488 Console.WriteLine (methName);
2489 Console.WriteLine (" resolveSequence=" + this.resolveSequence);
2490
2491 Console.WriteLine (" Locals:");
2492 foreach (ScriptMyLocal loc in declaredLocals) {
2493 Console.WriteLine (" " + loc.type.Name + " " + loc.name);
2494 }
2495
2496 Console.WriteLine (" Labels:");
2497 foreach (ScriptMyLabel lbl in definedLabels) {
2498 Console.WriteLine (" " + lbl.name);
2499 }
2500
2501 Console.WriteLine (" Code:");
2502 DumpCode ();
2503 }
2504 }
2505
2506 private void DumpCode ()
2507 {
2508 int linSeqNos = 0;
2509 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2510 gn.linSeqNo = ++ linSeqNos;
2511 }
2512 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2513 StringBuilder sb = new StringBuilder ();
2514 gn.DebStringExt (sb);
2515 Console.WriteLine (sb.ToString ());
2516 if (gn is GraphNodeBlock) {
2517 GraphNodeBlock gnb = (GraphNodeBlock)gn;
2518 foreach (ScriptMyLocal lcl in gnb.localsReadBeforeWritten) {
2519 Console.WriteLine (" reads " + lcl.name);
2520 }
2521 }
2522 }
2523 }
2524
2525 /**
2526 * @brief Scan the given block for branches to other blocks.
2527 * For any locals read by those blocks, mark them as being read by this block,
2528 * provided this block has not written them by that point. This makes it look
2529 * as though the branch instruction is reading all the locals needed by any
2530 * target blocks.
2531 */
2532 private void ResolveBlock (GraphNodeBlock currentBlock)
2533 {
2534 if (currentBlock.hasBeenResolved == this.resolveSequence) return;
2535
2536 /*
2537 * So we don't recurse forever on a backward branch.
2538 */
2539 currentBlock.hasBeenResolved = this.resolveSequence;
2540
2541 /*
2542 * Assume we haven't written any locals yet.
2543 */
2544 List<ScriptMyLocal> localsWrittenSoFar = new List<ScriptMyLocal> ();
2545
2546 /*
2547 * Scan through the instructions in this block.
2548 */
2549 for (GraphNode gn = currentBlock; gn != null;) {
2550
2551 /*
2552 * See if the instruction writes a local we don't know about yet.
2553 */
2554 ScriptMyLocal wrlcl = gn.WritesLocal ();
2555 if ((wrlcl != null) && !localsWrittenSoFar.Contains (wrlcl)) {
2556 localsWrittenSoFar.Add (wrlcl);
2557 }
2558
2559 /*
2560 * Scan through all the possible next instructions after this.
2561 * Note that if we are in the first part of a try/catch/finally block,
2562 * every instruction conditionally branches to the beginning of the
2563 * second part (the catch/finally block).
2564 */
2565 GraphNode nextFallthruNode = null;
2566 foreach (GraphNode nn in gn.NextNodes) {
2567 if (nn is GraphNodeBlock) {
2568
2569 /*
2570 * Start of a block, go through all locals needed by that block on entry.
2571 */
2572 GraphNodeBlock nextBlock = (GraphNodeBlock)nn;
2573 ResolveBlock (nextBlock);
2574 foreach (ScriptMyLocal readByNextBlock in nextBlock.localsReadBeforeWritten) {
2575
2576 /*
2577 * If this block hasn't written it by now and this block doesn't already
2578 * require it on entry, say this block requires it on entry.
2579 */
2580 if (!localsWrittenSoFar.Contains (readByNextBlock) &&
2581 !currentBlock.localsReadBeforeWritten.Contains (readByNextBlock)) {
2582 currentBlock.localsReadBeforeWritten.Add (readByNextBlock);
2583 this.resolvedSomething = true;
2584 }
2585 }
2586 } else {
2587
2588 /*
2589 * Not start of a block, should be normal fallthru instruction.
2590 */
2591 if (nextFallthruNode != null) throw new Exception ("more than one fallthru from " + gn.ToString ());
2592 nextFallthruNode = nn;
2593 }
2594 }
2595
2596 /*
2597 * Process next instruction if it isn't the start of a block.
2598 */
2599 if (nextFallthruNode == gn) throw new Exception ("can't fallthru to self");
2600 gn = nextFallthruNode;
2601 }
2602 }
2603
2604 /**
2605 * @brief Figure out whether the value in a local var is needed after the given instruction.
2606 * True if we reach the end of the program on all branches before reading it
2607 * True if we write the local var on all branches before reading it
2608 * False otherwise
2609 */
2610 private bool IsLocalNeededAfterThis (GraphNode node, ScriptMyLocal local)
2611 {
2612 do {
2613 GraphNode nextFallthruNode = null;
2614 foreach (GraphNode nn in node.NextNodes) {
2615 if (nn is GraphNodeBlock) {
2616 if (((GraphNodeBlock)nn).localsReadBeforeWritten.Contains (local)) {
2617 return true;
2618 }
2619 } else {
2620 nextFallthruNode = nn;
2621 }
2622 }
2623 node = nextFallthruNode;
2624 if (node == null) return false;
2625 if (node.ReadsLocal () == local) return true;
2626 } while (node.WritesLocal () != local);
2627 return false;
2628 }
2629
2630 public static void PadToLength (StringBuilder sb, int len, string str)
2631 {
2632 int pad = len - sb.Length;
2633 if (pad < 0) pad = 0;
2634 sb.Append (str.PadLeft (pad));
2635 }
2636 }
2637}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompValu.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompValu.cs
new file mode 100644
index 0000000..fd3174d
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompValu.cs
@@ -0,0 +1,1677 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using OpenSim.Region.ScriptEngine.XMREngine;
30using System;
31using System.Collections.Generic;
32using System.IO;
33using System.Reflection;
34using System.Reflection.Emit;
35
36using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
37using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
38using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
39using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
40using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
41using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
42using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
43
44/**
45 * @brief Compute values used during code generation to keep track of where computed values are stored.
46 *
47 * Conceptually holds the memory address and type of the value
48 * such as that used for a local variable, global variable, temporary variable.
49 * Also used for things like constants and function/method entrypoints,
50 * they are basically treated as read-only variables.
51 *
52 * cv.type - type of the value
53 *
54 * cv.PushVal() - pushes the value on the CIL stack
55 * cv.PushRef() - pushes address of the value on the CIL stack
56 *
57 * cv.PopPre() - gets ready to pop from the CIL stack
58 * ...by possibly pushing something
59 * <push value to be popped>
60 * cv.PushPre() - pops value from the CIL stack
61 *
62 * If the type is a TokenTypeSDTypeDelegate, the location is callable,
63 * so you get these additional functions:
64 *
65 * cv.GetRetType() - gets function/method's return value type
66 * TokenTypeVoid if void
67 * null if not a delegate
68 * cv.GetArgTypes() - gets array of argument types
69 * as seen by script level, ie,
70 * does not include any hidden 'this' type
71 * cv.GetArgSig() - gets argument signature eg, "(integer,list)"
72 * null if not a delegate
73 *
74 * cv.CallPre() - gets ready to call the function/method
75 * ...by possibly pushing something
76 * such as a 'this' pointer
77 * <push call args left-to-right>
78 * cv.CallPost() - calls the function/method
79 */
80
81namespace OpenSim.Region.ScriptEngine.XMREngine
82{
83
84 /**
85 * @brief Location of a value
86 * Includes constants, expressions and temp variables.
87 */
88 public abstract class CompValu {
89 protected static readonly MethodInfo gsmdMethodInfo =
90 typeof (XMRInstAbstract).GetMethod ("GetScriptMethodDelegate",
91 new Type[] { typeof (string), typeof (string), typeof (object) });
92
93 private static readonly MethodInfo avpmListMethInfo = typeof (XMRInstArrays).GetMethod ("PopList", new Type[] { typeof (int), typeof (LSL_List) });
94 private static readonly MethodInfo avpmObjectMethInfo = typeof (XMRInstArrays).GetMethod ("PopObject", new Type[] { typeof (int), typeof (object) });
95 private static readonly MethodInfo avpmStringMethInfo = typeof (XMRInstArrays).GetMethod ("PopString", new Type[] { typeof (int), typeof (string) });
96
97 public TokenType type; // type of the value and where in the source it was used
98
99 public CompValu (TokenType type)
100 {
101 this.type = type;
102 }
103
104 public Type ToSysType()
105 {
106 return (type.ToLSLWrapType () != null) ? type.ToLSLWrapType () : type.ToSysType ();
107 }
108
109 // if a field of an XMRInstArrays array cannot be directly written,
110 // get the method that can write it
111 private static MethodInfo ArrVarPopMeth (FieldInfo fi)
112 {
113 if (fi.Name == "iarLists") return avpmListMethInfo;
114 if (fi.Name == "iarObjects") return avpmObjectMethInfo;
115 if (fi.Name == "iarStrings") return avpmStringMethInfo;
116 return null;
117 }
118
119 // emit code to push value onto stack
120 public void PushVal (ScriptCodeGen scg, Token errorAt, TokenType stackType)
121 {
122 this.PushVal (scg, errorAt, stackType, false);
123 }
124 public void PushVal (ScriptCodeGen scg, Token errorAt, TokenType stackType, bool explicitAllowed)
125 {
126 this.PushVal (scg, errorAt);
127 TypeCast.CastTopOfStack (scg, errorAt, this.type, stackType, explicitAllowed);
128 }
129 public abstract void PushVal (ScriptCodeGen scg, Token errorAt);
130 public abstract void PushRef (ScriptCodeGen scg, Token errorAt);
131
132 // emit code to pop value from stack
133 public void PopPost (ScriptCodeGen scg, Token errorAt, TokenType stackType)
134 {
135 TypeCast.CastTopOfStack (scg, errorAt, stackType, this.type, false);
136 this.PopPost (scg, errorAt);
137 }
138 public virtual void PopPre (ScriptCodeGen scg, Token errorAt) { } // call this before pushing value to be popped
139 public abstract void PopPost (ScriptCodeGen scg, Token errorAt); // call this after pushing value to be popped
140
141 // return true: doing a PushVal() does not involve CheckRun()
142 // false: otherwise
143 public virtual bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
144 {
145 return true;
146 }
147
148 /*
149 * These additional functions are available if the type is a delegate
150 */
151 public TokenType GetRetType ()
152 {
153 if (!(type is TokenTypeSDTypeDelegate)) return null;
154 return ((TokenTypeSDTypeDelegate)type).decl.GetRetType ();
155 }
156 public TokenType[] GetArgTypes ()
157 {
158 if (!(type is TokenTypeSDTypeDelegate)) return null;
159 return ((TokenTypeSDTypeDelegate)type).decl.GetArgTypes ();
160 }
161 public string GetArgSig ()
162 {
163 if (!(type is TokenTypeSDTypeDelegate)) return null;
164 return ((TokenTypeSDTypeDelegate)type).decl.GetArgSig ();
165 }
166
167 // These are used only if type is a delegate too
168 // - but it is a real delegate pointer in a global or local variable or a field, etc
169 // ie, PushVal() pushes a delegate pointer
170 // - so we must have CallPre() push the delegate pointer as a 'this' for this.Invoke(...)
171 // - and CallPost() call the delegate's Invoke() method
172 // - we assume the target function is non-trivial so we always use a call label
173 public virtual void CallPre (ScriptCodeGen scg, Token errorAt) // call this before pushing arguments
174 {
175 new ScriptCodeGen.CallLabel (scg, errorAt);
176 this.PushVal (scg, errorAt);
177 }
178 public virtual void CallPost (ScriptCodeGen scg, Token errorAt) // call this after pushing arguments
179 {
180 TokenTypeSDTypeDelegate ttd = (TokenTypeSDTypeDelegate)type;
181 MethodInfo invokeMethodInfo = ttd.decl.GetInvokerInfo ();
182 scg.ilGen.Emit (errorAt, OpCodes.Callvirt, invokeMethodInfo);
183 scg.openCallLabel = null;
184 }
185
186 /*
187 * Utilities used by CompValuGlobalVar and CompValuInstField
188 * where the value is located in a type-dependent array.
189 */
190 protected void EmitFieldPushVal (ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
191 {
192 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, var.vTableArray); // which array
193 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, var.vTableIndex); // which array element
194 if (type is TokenTypeFloat) {
195 scg.ilGen.Emit (errorAt, OpCodes.Ldelem_R8);
196 } else if (type is TokenTypeInt) {
197 scg.ilGen.Emit (errorAt, OpCodes.Ldelem_I4);
198 } else if (type is TokenTypeSDTypeDelegate) {
199 scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (object));
200 scg.ilGen.Emit (errorAt, OpCodes.Castclass, ToSysType ());
201 } else {
202 scg.ilGen.Emit (errorAt, OpCodes.Ldelem, ToSysType ());
203 }
204 }
205
206 protected void EmitFieldPushRef (ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
207 {
208 if (ArrVarPopMeth (var.vTableArray) != null) {
209 scg.ErrorMsg (errorAt, "can't take address of this variable");
210 }
211 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, var.vTableArray);
212 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, var.vTableIndex);
213 scg.ilGen.Emit (errorAt, OpCodes.Ldelema, ToSysType());
214 }
215
216 protected void EmitFieldPopPre (ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
217 {
218 if (ArrVarPopMeth (var.vTableArray) != null) {
219 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, var.vTableIndex);
220 } else {
221 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, var.vTableArray);
222 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, var.vTableIndex);
223 }
224 }
225
226 protected void EmitFieldPopPost (ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
227 {
228 if (ArrVarPopMeth (var.vTableArray) != null) {
229 scg.ilGen.Emit (errorAt, OpCodes.Call, ArrVarPopMeth (var.vTableArray));
230 } else if (type is TokenTypeFloat) {
231 scg.ilGen.Emit (errorAt, OpCodes.Stelem_R8);
232 } else if (type is TokenTypeInt) {
233 scg.ilGen.Emit (errorAt, OpCodes.Stelem_I4);
234 } else if (type is TokenTypeSDTypeDelegate) {
235 scg.ilGen.Emit (errorAt, OpCodes.Stelem, typeof (object));
236 } else {
237 scg.ilGen.Emit (errorAt, OpCodes.Stelem, ToSysType ());
238 }
239 }
240
241 /**
242 * @brief With value pushed on stack, emit code to set a property by calling its setter() method.
243 * @param scg = which script is being compiled
244 * @param errorAt = for error messages
245 * @param type = property type
246 * @param setProp = setter() method
247 */
248 protected void EmitPopPostProp (ScriptCodeGen scg, Token errorAt, TokenType type, CompValu setProp)
249 {
250 ScriptMyLocal temp = scg.ilGen.DeclareLocal (type.ToSysType (), "__spr_" + errorAt.Unique);
251 scg.ilGen.Emit (errorAt, OpCodes.Stloc, temp);
252 setProp.CallPre (scg, errorAt);
253 scg.ilGen.Emit (errorAt, OpCodes.Ldloc, temp);
254 setProp.CallPost (scg, errorAt);
255 }
256 }
257
258 // The value is kept in an (XMR_Array) array element
259 public class CompValuArEle : CompValu {
260 public CompValu arr;
261 private CompValu idx;
262 private TokenTypeObject tto;
263
264 private static readonly MethodInfo getByKeyMethodInfo = typeof (XMR_Array).GetMethod ("GetByKey",
265 new Type[] { typeof (object) });
266 private static readonly MethodInfo setByKeyMethodInfo = typeof (XMR_Array).GetMethod ("SetByKey",
267 new Type[] { typeof (object),
268 typeof (object) });
269
270 // type = TokenTypeObject always, as our array elements are always of type 'object'
271 // arr = where the array object itself is stored
272 // idx = where the index value is stored
273 public CompValuArEle (TokenType type, CompValu arr, CompValu idx) : base (type)
274 {
275 this.arr = arr;
276 this.idx = idx;
277 this.tto = new TokenTypeObject (this.type);
278 }
279 public override void PushVal (ScriptCodeGen scg, Token errorAt)
280 {
281 arr.PushVal (scg, errorAt); // array
282 idx.PushVal (scg, errorAt, this.tto); // key
283 scg.ilGen.Emit (errorAt, OpCodes.Call, getByKeyMethodInfo);
284 }
285 public override void PushRef (ScriptCodeGen scg, Token errorAt)
286 {
287 scg.ErrorMsg (errorAt, "array element not allowed here");
288 scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
289 }
290 public override void PopPre (ScriptCodeGen scg, Token errorAt)
291 {
292 arr.PushVal (scg, errorAt); // array
293 idx.PushVal (scg, errorAt, this.tto); // key
294 }
295 public override void PopPost (ScriptCodeGen scg, Token errorAt)
296 {
297 scg.ilGen.Emit (errorAt, OpCodes.Call, setByKeyMethodInfo);
298 }
299
300 // non-trivial because it needs to be copied into a temp
301 // in case the idiot does dumb-ass side effects tricks
302 // eg, (x = 0) + x + 2
303 // should read old value of x not 0
304 // but if 'xmroption norighttoleft;' in effect,
305 // we can read it in any order so reading an
306 // XMR_Array element is trivial
307 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
308 {
309 return readAt.nr2l;
310 }
311 }
312
313 // The value is kept in the current function's argument list
314 public class CompValuArg : CompValu {
315 public int index;
316 public bool readOnly;
317
318 private static OpCode[] ldargs = { OpCodes.Ldarg_0, OpCodes.Ldarg_1,
319 OpCodes.Ldarg_2, OpCodes.Ldarg_3 };
320
321 public CompValuArg (TokenType type, int index) : base (type)
322 {
323 this.index = index;
324 }
325 public CompValuArg (TokenType type, int index, bool ro) : base (type)
326 {
327 this.index = index;
328 this.readOnly = ro;
329 }
330 public override void PushVal (ScriptCodeGen scg, Token errorAt)
331 {
332 if (index < ldargs.Length) scg.ilGen.Emit (errorAt, ldargs[index]);
333 else if (index <= 255) scg.ilGen.Emit (errorAt, OpCodes.Ldarg_S, index);
334 else scg.ilGen.Emit (errorAt, OpCodes.Ldarg, index);
335 }
336 public override void PushRef (ScriptCodeGen scg, Token errorAt)
337 {
338 if (readOnly) {
339 scg.ErrorMsg (errorAt, "location cannot be written to");
340 }
341 if (index <= 255) scg.ilGen.Emit (errorAt, OpCodes.Ldarga_S, index);
342 else scg.ilGen.Emit (errorAt, OpCodes.Ldarga, index);
343 }
344 public override void PopPost (ScriptCodeGen scg, Token errorAt)
345 {
346 if (readOnly) {
347 scg.ErrorMsg (errorAt, "location cannot be written to");
348 }
349 scg.ilGen.Emit (errorAt, OpCodes.Starg, index);
350 }
351
352 // non-trivial because it needs to be copied into a temp
353 // in case the idiot does dumb-ass side effects tricks
354 // eg, (x = 0) + x + 2
355 // should read old value of x not 0
356 // but if 'xmroption norighttoleft;' in effect,
357 // we can read it in any order so reading an
358 // argument is trivial
359 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
360 {
361 return readAt.nr2l;
362 }
363 }
364
365 // The value is a character constant
366 public class CompValuChar : CompValu {
367 public char x;
368
369 public CompValuChar (TokenType type, char x) : base (type)
370 {
371 if (!(this.type is TokenTypeChar)) {
372 this.type = new TokenTypeChar (this.type);
373 }
374 this.x = x;
375 }
376 public override void PushVal (ScriptCodeGen scg, Token errorAt)
377 {
378 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, (int)x);
379 }
380 public override void PushRef (ScriptCodeGen scg, Token errorAt)
381 {
382 throw new Exception ("cannot get constant's address");
383 }
384 public override void PopPost (ScriptCodeGen scg, Token errorAt)
385 {
386 throw new Exception ("cannot store into contant");
387 }
388 }
389
390 // The value is kept in a struct/class field of an internal struct/class
391 public class CompValuField : CompValu {
392 CompValu obj;
393 FieldInfo field;
394
395 public CompValuField (TokenType type, CompValu obj, FieldInfo field) : base (type)
396 {
397 this.obj = obj;
398 this.field = field;
399 }
400 public override void PushVal (ScriptCodeGen scg, Token errorAt)
401 {
402 if (field.ReflectedType.IsValueType) {
403 obj.PushRef (scg, errorAt);
404 } else {
405 obj.PushVal (scg, errorAt);
406 }
407 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, field);
408 }
409 public override void PushRef (ScriptCodeGen scg, Token errorAt)
410 {
411 if (field.ReflectedType.IsValueType) {
412 obj.PushRef (scg, errorAt);
413 } else {
414 obj.PushVal (scg, errorAt);
415 }
416 scg.ilGen.Emit (errorAt, OpCodes.Ldflda, field);
417 }
418 public override void PopPre (ScriptCodeGen scg, Token errorAt)
419 {
420 if (field.ReflectedType.IsValueType) {
421 obj.PushRef (scg, errorAt);
422 } else {
423 obj.PushVal (scg, errorAt);
424 }
425 }
426 public override void PopPost (ScriptCodeGen scg, Token errorAt)
427 {
428 scg.ilGen.Emit (errorAt, OpCodes.Stfld, field);
429 }
430
431 // non-trivial because it needs to be copied into a temp
432 // in case the idiot does dumb-ass side effects tricks
433 // eg, (x = 0) + x + 2
434 // should read old value of x not 0
435 // but if 'xmroption norighttoleft;' in effect,
436 // we can read it in any order so reading an
437 // field of a class/struct is trivial
438 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
439 {
440 return readAt.nr2l;
441 }
442 }
443
444 // Accessing an element of a fixed-dimension array
445 public class CompValuFixArEl : CompValu {
446 private CompValu baseRVal;
447 private CompValu[] subRVals;
448
449 private int nSubs;
450 private TokenDeclVar getFunc;
451 private TokenDeclVar setFunc;
452 private TokenTypeInt tokenTypeInt;
453
454 /**
455 * @brief Set up to access an element of an array.
456 * @param scg = what script we are compiling
457 * @param baseRVal = what array we are accessing
458 * @param subRVals = the subscripts being applied
459 */
460 public CompValuFixArEl (ScriptCodeGen scg, CompValu baseRVal, CompValu[] subRVals) : base (GetElementType (scg, baseRVal, subRVals))
461 {
462 this.baseRVal = baseRVal; // location of the array itself
463 this.subRVals = subRVals; // subscript values
464 this.nSubs = subRVals.Length;
465
466 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseRVal.type;
467 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
468 tokenTypeInt = new TokenTypeInt (sdtType);
469
470 TokenName name = new TokenName (sdtType, "Get");
471 TokenType[] argsig = new TokenType[nSubs];
472 for (int i = 0; i < nSubs; i ++) {
473 argsig[i] = tokenTypeInt;
474 }
475 getFunc = scg.FindThisMember (sdtDecl, name, argsig);
476
477 name = new TokenName (sdtType, "Set");
478 argsig = new TokenType[nSubs+1];
479 for (int i = 0; i < nSubs; i ++) {
480 argsig[i] = tokenTypeInt;
481 }
482 argsig[nSubs] = getFunc.retType;
483 setFunc = scg.FindThisMember (sdtDecl, name, argsig);
484 }
485
486 /**
487 * @brief Read array element and push value on stack.
488 */
489 public override void PushVal (ScriptCodeGen scg, Token errorAt)
490 {
491 // call script-defined class' Get() method to fetch the value
492 baseRVal.PushVal (scg, errorAt);
493 for (int i = 0; i < nSubs; i ++) {
494 subRVals[i].PushVal (scg, errorAt, tokenTypeInt);
495 }
496 scg.ilGen.Emit (errorAt, OpCodes.Call, getFunc.ilGen);
497 }
498
499 /**
500 * @brief Push address of array element on stack.
501 */
502 public override void PushRef (ScriptCodeGen scg, Token errorAt)
503 {
504 throw new Exception ("tu stOOpid to get array element address");
505 }
506
507 /**
508 * @brief Prepare to write array element.
509 */
510 public override void PopPre (ScriptCodeGen scg, Token errorAt)
511 {
512 // set up call to script-defined class' Set() method to write the value
513 baseRVal.PushVal (scg, errorAt);
514 for (int i = 0; i < nSubs; i ++) {
515 subRVals[i].PushVal (scg, errorAt, tokenTypeInt);
516 }
517 }
518
519 /**
520 * @brief Pop value from stack and write array element.
521 */
522 public override void PopPost (ScriptCodeGen scg, Token errorAt)
523 {
524 // call script-defined class' Set() method to write the value
525 scg.ilGen.Emit (errorAt, OpCodes.Call, setFunc.ilGen);
526 }
527
528 /**
529 * @brief Get the array element type by getting the Get() functions return type.
530 * Crude but effective.
531 * @param scg = what script we are compiling
532 * @param baseRVal = what array we are accessing
533 * @param subRVals = the subscripts being applied
534 * @returns array element type
535 */
536 private static TokenType GetElementType (ScriptCodeGen scg, CompValu baseRVal, CompValu[] subRVals)
537 {
538 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseRVal.type;
539 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
540 TokenName name = new TokenName (sdtType, "Get");
541 int nSubs = subRVals.Length;
542 TokenType[] argsig = new TokenType[nSubs];
543 argsig[0] = new TokenTypeInt (sdtType);
544 for (int i = 0; ++ i < nSubs;) {
545 argsig[i] = argsig[0];
546 }
547 TokenDeclVar getFunc = scg.FindThisMember (sdtDecl, name, argsig);
548 return getFunc.retType;
549 }
550
551 // non-trivial because it needs to be copied into a temp
552 // in case the idiot does dumb-ass side effects tricks
553 // eg, (x = 0) + x + 2
554 // should read old value of x not 0
555 // but if 'xmroption norighttoleft;' in effect,
556 // we can read it in any order so reading an
557 // fixed-dimension array element is trivial
558 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
559 {
560 return readAt.nr2l;
561 }
562 }
563
564 // The value is a float constant
565 public class CompValuFloat : CompValu {
566 public double x;
567
568 public CompValuFloat (TokenType type, double x) : base (type)
569 {
570 if (!(this.type is TokenTypeFloat)) {
571 this.type = new TokenTypeFloat (this.type);
572 }
573 this.x = x;
574 }
575 public override void PushVal (ScriptCodeGen scg, Token errorAt)
576 {
577 scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, x);
578 }
579 public override void PushRef (ScriptCodeGen scg, Token errorAt)
580 {
581 throw new Exception ("cannot get constant's address");
582 }
583 public override void PopPost (ScriptCodeGen scg, Token errorAt)
584 {
585 throw new Exception ("cannot store into constant");
586 }
587 }
588
589 // The value is the entrypoint of a script-defined global function.
590 // These are also used for script-defined type static methods as the calling convention is the same,
591 // ie, the XMRInstance pointer is a hidden first argument.
592 // There is just one of these created when the function is being compiled as there is only one value
593 // of the function.
594 public class CompValuGlobalMeth : CompValu {
595 private TokenDeclVar func;
596
597 public CompValuGlobalMeth (TokenDeclVar declFunc) : base (declFunc.GetDelType ())
598 {
599 this.func = declFunc;
600 }
601
602 /**
603 * @brief PushVal for a function/method means push a delegate on the stack.
604 * We build a call to the DynamicMethod's CreateDelegate() function
605 * to create the delegate. Slip the scriptinstance pointer as the
606 * function's arg 0 so it will get passed to the function when called.
607 */
608 public override void PushVal (ScriptCodeGen scg, Token errorAt)
609 {
610 string dtn = type.ToString ();
611 if (dtn.StartsWith ("delegate ")) dtn = dtn.Substring (9);
612
613 // delegateinstance = (signature)scriptinstance.GetScriptMethodDelegate (methName, signature, arg0);
614 // where methName = [<sdtclass>.]<methname>(<argtypes>)
615 // signature = <rettype>(<argtypes>)
616 // arg0 = scriptinstance (XMRInstance)
617 scg.PushXMRInst (); // [0] scriptinstance
618 scg.ilGen.Emit (errorAt, OpCodes.Ldstr, func.ilGen.methName); // [1] method name
619 scg.ilGen.Emit (errorAt, OpCodes.Ldstr, dtn); // [2] delegate type name
620 scg.PushXMRInst (); // [3] scriptinstance
621 scg.ilGen.Emit (errorAt, OpCodes.Callvirt, gsmdMethodInfo); // [0] delegate instance
622 scg.ilGen.Emit (errorAt, OpCodes.Castclass, type.ToSysType ()); // [0] cast to correct delegate class
623 }
624 public override void PushRef (ScriptCodeGen scg, Token errorAt)
625 {
626 throw new Exception ("cannot get ref to global method");
627 }
628 public override void PopPost (ScriptCodeGen scg, Token errorAt)
629 {
630 throw new Exception ("cannot store into global method");
631 }
632
633 /**
634 * @brief A direct call is much simpler than pushing a delegate.
635 * Just push the XMRInstance pointer, push the args and finally call the function.
636 */
637 public override void CallPre (ScriptCodeGen scg, Token errorAt)
638 {
639 if (!this.func.IsFuncTrivial (scg)) new ScriptCodeGen.CallLabel (scg, errorAt);
640
641 // all script-defined global functions are static methods created by DynamicMethod()
642 // and the first argument is always the XMR_Instance pointer
643 scg.PushXMRInst ();
644 }
645 public override void CallPost (ScriptCodeGen scg, Token errorAt)
646 {
647 scg.ilGen.Emit (errorAt, OpCodes.Call, func.ilGen);
648 if (!this.func.IsFuncTrivial (scg)) scg.openCallLabel = null;
649 }
650 }
651
652 // The value is in a script-global variable = ScriptModule instance variable
653 // It could also be a script-global property
654 public class CompValuGlobalVar : CompValu {
655 private static readonly FieldInfo glblVarsFieldInfo = typeof (XMRInstAbstract).GetField ("glblVars");
656
657 private TokenDeclVar declVar;
658
659 public CompValuGlobalVar (TokenDeclVar declVar, XMRInstArSizes glblSizes) : base (declVar.type)
660 {
661 this.declVar = declVar;
662 if ((declVar.getProp == null) && (declVar.setProp == null)) {
663 declVar.type.AssignVarSlot (declVar, glblSizes);
664 }
665 }
666 public override void PushVal (ScriptCodeGen scg, Token errorAt)
667 {
668 if ((declVar.getProp == null) && (declVar.setProp == null)) {
669 scg.PushXMRInst ();
670 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, glblVarsFieldInfo);
671 EmitFieldPushVal (scg, errorAt, declVar);
672 } else if (declVar.getProp != null) {
673 declVar.getProp.location.CallPre (scg, errorAt);
674 declVar.getProp.location.CallPost (scg, errorAt);
675 } else {
676 scg.ErrorMsg (errorAt, "property not readable");
677 scg.PushDefaultValue (declVar.type);
678 }
679 }
680 public override void PushRef (ScriptCodeGen scg, Token errorAt)
681 {
682 if ((declVar.getProp == null) && (declVar.setProp == null)) {
683 scg.PushXMRInst ();
684 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, glblVarsFieldInfo);
685 EmitFieldPushRef (scg, errorAt, declVar);
686 } else {
687 scg.ErrorMsg (errorAt, "cannot get address of property");
688 }
689 }
690 public override void PopPre (ScriptCodeGen scg, Token errorAt)
691 {
692 if ((declVar.getProp == null) && (declVar.setProp == null)) {
693 scg.PushXMRInst ();
694 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, glblVarsFieldInfo);
695 EmitFieldPopPre (scg, errorAt, declVar);
696 } else if (declVar.setProp == null) {
697 scg.ErrorMsg (errorAt, "property not writable");
698 }
699 }
700 public override void PopPost (ScriptCodeGen scg, Token errorAt)
701 {
702 if ((declVar.getProp == null) && (declVar.setProp == null)) {
703 EmitFieldPopPost (scg, errorAt, declVar);
704 } else if (declVar.setProp != null) {
705 EmitPopPostProp (scg, errorAt, declVar.type, declVar.setProp.location);
706 } else {
707 scg.ilGen.Emit (errorAt, OpCodes.Pop);
708 }
709 }
710
711 // non-trivial because it needs to be copied into a temp
712 // in case the idiot does dumb-ass side effects tricks
713 // eg, (x = 0) + x + 2
714 // should read old value of x not 0
715 // but if 'xmroption norighttoleft;' in effect,
716 // we can read it in any order so reading an
717 // global variable is trivial provided it is
718 // not a property or the property function is
719 // trivial.
720 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
721 {
722 return readAt.nr2l && ((declVar.getProp == null) || declVar.getProp.IsFuncTrivial (scg));
723 }
724 }
725
726 // The value is in an $idxprop property of a script-defined type class or interface instance.
727 // Reading and writing is via a method call.
728 public class CompValuIdxProp : CompValu {
729 private TokenDeclVar idxProp; // $idxprop property within baseRVal
730 private CompValu baseRVal; // pointer to class or interface object containing property
731 private TokenType[] argTypes; // argument types as required by $idxprop declaration
732 private CompValu[] indices; // actual index values to pass to getter/setter method
733 private CompValu setProp; // location of setter method
734
735 public CompValuIdxProp (TokenDeclVar idxProp, CompValu baseRVal, TokenType[] argTypes, CompValu[] indices) : base (idxProp.type)
736 {
737 this.idxProp = idxProp;
738 this.baseRVal = baseRVal;
739 this.argTypes = argTypes;
740 this.indices = indices;
741 }
742
743 /**
744 * @brief Pushing the property's value is a matter of calling the getter method
745 * with the supplied argument list as is.
746 */
747 public override void PushVal (ScriptCodeGen scg, Token errorAt)
748 {
749 if (idxProp.getProp != null) {
750 if (!idxProp.getProp.IsFuncTrivial (scg)) {
751 for (int i = indices.Length; -- i >= 0;) {
752 indices[i] = scg.Trivialize (indices[i], errorAt);
753 }
754 }
755 CompValu getProp = GetIdxPropMeth (idxProp.getProp);
756 getProp.CallPre (scg, errorAt);
757 for (int i = 0; i < indices.Length; i ++) {
758 indices[i].PushVal (scg, errorAt, argTypes[i]);
759 }
760 getProp.CallPost (scg, errorAt);
761 } else {
762 // write-only property
763 scg.ErrorMsg (errorAt, "member not readable");
764 scg.PushDefaultValue (idxProp.type);
765 }
766 }
767
768 /**
769 * @brief A property does not have a memory address.
770 */
771 public override void PushRef (ScriptCodeGen scg, Token errorAt)
772 {
773 scg.ErrorMsg (errorAt, "member has no address");
774 scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
775 }
776
777 /**
778 * @brief Preparing to write a property consists of preparing to call the setter method
779 * then pushing the index arguments.
780 */
781 public override void PopPre (ScriptCodeGen scg, Token errorAt)
782 {
783 if (idxProp.setProp != null) {
784 if (!idxProp.setProp.IsFuncTrivial (scg)) {
785 for (int i = indices.Length; -- i >= 0;) {
786 indices[i] = scg.Trivialize (indices[i], errorAt);
787 }
788 }
789 this.setProp = GetIdxPropMeth (idxProp.setProp);
790 this.setProp.CallPre (scg, errorAt);
791 for (int i = 0; i < indices.Length; i ++) {
792 indices[i].PushVal (scg, errorAt, argTypes[i]);
793 }
794 } else {
795 // read-only property
796 scg.ErrorMsg (errorAt, "member not writable");
797 }
798 }
799
800 /**
801 * @brief Finishing writing a property consists of finishing the call to the setter method
802 * now that the value to be written has been pushed by our caller.
803 */
804 public override void PopPost (ScriptCodeGen scg, Token errorAt)
805 {
806 if (idxProp.setProp != null) {
807 this.setProp.CallPost (scg, errorAt);
808 } else {
809 scg.ilGen.Emit (errorAt, OpCodes.Pop);
810 }
811 }
812
813 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
814 {
815 // if no getter, reading would throw an error, so doesn't really matter what we say
816 if (idxProp.getProp == null) return true;
817
818 // assume interface methods are always non-trivial because we don't know anything about the actual implementation
819 if (baseRVal.type is TokenTypeSDTypeInterface) return false;
820
821 // accessing it in any way can't be trivial if reading the pointer isn't trivial
822 if (!baseRVal.IsReadTrivial (scg, readAt)) return false;
823
824 // likewise with the indices
825 foreach (CompValu idx in indices) {
826 if (!idx.IsReadTrivial (scg, readAt)) return false;
827 }
828
829 // now the only way it can be non-trivial to read is if the getter() method itself is non-trivial.
830 return idxProp.getProp.IsFuncTrivial (scg);
831 }
832
833 /**
834 * @brief Get how to call the getter or setter method.
835 */
836 private CompValu GetIdxPropMeth (TokenDeclVar meth)
837 {
838 if (baseRVal.type is TokenTypeSDTypeClass) {
839 return new CompValuInstMember (meth, baseRVal, false);
840 }
841 return new CompValuIntfMember (meth, baseRVal);
842 }
843 }
844
845 // This represents the type and location of an internally-defined function
846 // that a script can call
847 public class CompValuInline : CompValu {
848 public TokenDeclInline declInline;
849
850 public CompValuInline (TokenDeclInline declInline) : base (declInline.GetDelType ())
851 {
852 this.declInline = declInline;
853 }
854
855 public override void PushVal (ScriptCodeGen scg, Token errorAt)
856 {
857 scg.ErrorMsg (errorAt, "cannot use built-in for delegate, wrap it");
858 scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
859 }
860 public override void PushRef (ScriptCodeGen scg, Token errorAt)
861 {
862 scg.ErrorMsg (errorAt, "cannot use built-in for delegate, wrap it");
863 scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
864 }
865 public override void PopPre (ScriptCodeGen scg, Token errorAt)
866 {
867 scg.ErrorMsg (errorAt, "cannot use built-in for delegate, wrap it");
868 }
869 public override void PopPost (ScriptCodeGen scg, Token errorAt)
870 {
871 scg.ErrorMsg (errorAt, "cannot use built-in for delegate, wrap it");
872 scg.ilGen.Emit (errorAt, OpCodes.Pop);
873 }
874 }
875
876 // The value is the entrypoint of a script-defined type's interface method combined with
877 // the pointer used to access the method. Thus there is one of these per call site.
878 // They also handle accessing interface properties.
879 public class CompValuIntfMember : CompValu {
880 private TokenDeclVar declVar;
881 private CompValu baseRVal;
882
883 public CompValuIntfMember (TokenDeclVar declVar, CompValu baseRVal) : base (declVar.type)
884 {
885 if (this.type == null) throw new Exception ("interface member type is null");
886 this.declVar = declVar; // which element of the baseRVal vector to be accessed
887 this.baseRVal = baseRVal; // the vector of delegates implementing the interface
888 }
889
890 /**
891 * @brief Reading a method's value means getting a delegate to that method.
892 * Reading a property's value means calling the getter method for that property.
893 */
894 public override void PushVal (ScriptCodeGen scg, Token errorAt)
895 {
896 if (declVar.retType != null) {
897 baseRVal.PushVal (scg, errorAt); // push pointer to delegate array on stack
898 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, declVar.vTableIndex); // select which delegate to access
899 scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (Delegate)); // push delegate on stack
900 scg.ilGen.Emit (errorAt, OpCodes.Castclass, type.ToSysType ()); // cast to correct delegate class
901 } else if (declVar.getProp != null) {
902 CompValu getProp = new CompValuIntfMember (declVar.getProp, baseRVal);
903 getProp.CallPre (scg, errorAt); // reading property, call its getter
904 getProp.CallPost (scg, errorAt); // ... with no arguments
905 } else {
906 scg.ErrorMsg (errorAt, "member not readable");
907 scg.PushDefaultValue (declVar.type);
908 }
909 }
910
911 /**
912 * @brief Can't get the address of either a method or a property.
913 */
914 public override void PushRef (ScriptCodeGen scg, Token errorAt)
915 {
916 scg.ErrorMsg (errorAt, "member has no address");
917 scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
918 }
919
920 /**
921 * @brief Can't write a method.
922 * For property, it means calling the setter method for that property.
923 */
924 public override void PopPre (ScriptCodeGen scg, Token errorAt)
925 {
926 if (declVar.setProp == null) {
927 // read-only property
928 scg.ErrorMsg (errorAt, "member not writable");
929 }
930 }
931 public override void PopPost (ScriptCodeGen scg, Token errorAt)
932 {
933 if (declVar.setProp != null) {
934 CompValu setProp = new CompValuIntfMember (declVar.setProp, baseRVal);
935 EmitPopPostProp (scg, errorAt, declVar.type, setProp);
936 } else {
937 scg.ilGen.Emit (errorAt, OpCodes.Pop);
938 }
939 }
940
941 /**
942 * @brief Reading a method (ie, it's delegate) is always trivial, it's just retrieving
943 * an element from the delegate array that make up the interface object.
944 *
945 * Reading a property is always non-trivial because we don't know which implementation
946 * the interface is pointing to, so we don't know if it's trivial or not, so assume
947 * the worst, ie, that it is non-trivial and might call CheckRun().
948 *
949 * But all that assumes that locating the interface object in the first place is
950 * trivial, ie, baseRVal.PushVal() must not call CheckRun() either.
951 */
952 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
953 {
954 return baseRVal.IsReadTrivial (scg, readAt) && (declVar.getProp == null);
955 }
956
957 /**
958 * @brief We just defer to the default CallPre() and CallPost() methods.
959 * They expect this.PushVal() to push a delegate to the method to be called.
960 * If this member is a method, our PushVal() will read the correct element
961 * of the iTable array and push it on the stack, ready for Invoke() to be
962 * called. If this member is a property, the only way it can be called is
963 * if the property is a delegate, in which case PushVal() will retrieve the
964 * delegate by calling the property's getter method.
965 */
966 }
967
968 // The value is the entrypoint of an internal instance method
969 // such as XMR_Array.index()
970 public class CompValuIntInstMeth : CompValu {
971 private TokenTypeSDTypeDelegate delType;
972 private CompValu baseRVal;
973 private MethodInfo methInfo;
974
975 public CompValuIntInstMeth (TokenTypeSDTypeDelegate delType, CompValu baseRVal, MethodInfo methInfo) : base (delType)
976 {
977 this.delType = delType;
978 this.baseRVal = baseRVal;
979 this.methInfo = methInfo;
980 }
981
982 public override void PushVal (ScriptCodeGen scg, Token errorAt)
983 {
984 // its value, ie, without applying the (arglist), is a delegate...
985 baseRVal.PushVal (scg, errorAt);
986 scg.ilGen.Emit (errorAt, OpCodes.Ldftn, methInfo);
987 scg.ilGen.Emit (errorAt, OpCodes.Newobj, delType.decl.GetConstructorInfo ());
988 }
989 public override void PushRef (ScriptCodeGen scg, Token errorAt)
990 {
991 throw new Exception ("cannot get ref to instance method");
992 }
993 public override void PopPost (ScriptCodeGen scg, Token errorAt)
994 {
995 throw new Exception ("cannot store into instance method");
996 }
997
998 public override void CallPre (ScriptCodeGen scg, Token errorAt)
999 {
1000 // internal instance methods are always trivial so never need a CallLabel.
1001 baseRVal.PushVal (scg, errorAt);
1002 }
1003 public override void CallPost (ScriptCodeGen scg, Token errorAt)
1004 {
1005 scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
1006 }
1007 }
1008
1009 // The value is fetched by calling an internal instance method
1010 // such as XMR_Array.count
1011 public class CompValuIntInstROProp : CompValu {
1012 private CompValu baseRVal;
1013 private MethodInfo methInfo;
1014
1015 public CompValuIntInstROProp (TokenType valType, CompValu baseRVal, MethodInfo methInfo) : base (valType)
1016 {
1017 this.baseRVal = baseRVal;
1018 this.methInfo = methInfo;
1019 }
1020
1021 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1022 {
1023 baseRVal.PushVal (scg, errorAt);
1024 scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
1025 }
1026 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1027 {
1028 scg.ErrorMsg (errorAt, "cannot get ref to read-only property");
1029 scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
1030 }
1031 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1032 {
1033 scg.ErrorMsg (errorAt, "cannot store into read-only property");
1034 scg.ilGen.Emit (errorAt, OpCodes.Pop);
1035 }
1036 }
1037
1038 // The value is in a member of a script-defined type class instance.
1039 // field: value is in one of the arrays contained within XMRSDTypeClObj.instVars
1040 // method: value is a delegate; can be called
1041 // property: reading and writing is via a method call
1042 public class CompValuInstMember : CompValu {
1043 private static readonly FieldInfo instVarsFieldInfo = typeof (XMRSDTypeClObj).GetField ("instVars");
1044 private static readonly FieldInfo vTableFieldInfo = typeof (XMRSDTypeClObj).GetField ("sdtcVTable");
1045
1046 private TokenDeclVar declVar; // member being accessed
1047 private CompValu baseRVal; // pointer to particular object instance
1048 private bool ignoreVirt; // ignore virtual attribute; use declVar's non-virtual method/property
1049
1050 public CompValuInstMember (TokenDeclVar declVar, CompValu baseRVal, bool ignoreVirt) : base (declVar.type)
1051 {
1052 this.declVar = declVar;
1053 this.baseRVal = baseRVal;
1054 this.ignoreVirt = ignoreVirt;
1055 }
1056
1057 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1058 {
1059 if (declVar.retType != null) {
1060 // a method's value, ie, without applying the (arglist), is a delegate...
1061 PushValMethod (scg, errorAt);
1062 } else if (declVar.vTableArray != null) {
1063 // a field's value is its XMRSDTypeClObj.instVars array element
1064 baseRVal.PushVal (scg, errorAt);
1065 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, instVarsFieldInfo);
1066 EmitFieldPushVal (scg, errorAt, declVar);
1067 } else if (declVar.getProp != null) {
1068 // a property's value is calling its get method with no arguments
1069 CompValu getProp = new CompValuInstMember (declVar.getProp, baseRVal, ignoreVirt);
1070 getProp.CallPre (scg, errorAt);
1071 getProp.CallPost (scg, errorAt);
1072 } else {
1073 // write-only property
1074 scg.ErrorMsg (errorAt, "member not readable");
1075 scg.PushDefaultValue (declVar.type);
1076 }
1077 }
1078 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1079 {
1080 if (declVar.vTableArray != null) {
1081 // a field's value is its XMRSDTypeClObj.instVars array element
1082 baseRVal.PushVal (scg, errorAt);
1083 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, instVarsFieldInfo);
1084 EmitFieldPushRef (scg, errorAt, declVar);
1085 } else {
1086 scg.ErrorMsg (errorAt, "member has no address");
1087 scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
1088 }
1089 }
1090 public override void PopPre (ScriptCodeGen scg, Token errorAt)
1091 {
1092 if (declVar.vTableArray != null) {
1093 // a field's value is its XMRSDTypeClObj.instVars array element
1094 baseRVal.PushVal (scg, errorAt);
1095 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, instVarsFieldInfo);
1096 EmitFieldPopPre (scg, errorAt, declVar);
1097 } else if (declVar.setProp == null) {
1098 // read-only property
1099 scg.ErrorMsg (errorAt, "member not writable");
1100 }
1101 }
1102 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1103 {
1104 if (declVar.vTableArray != null) {
1105 EmitFieldPopPost (scg, errorAt, declVar);
1106 } else if (declVar.setProp != null) {
1107 CompValu setProp = new CompValuInstMember (declVar.setProp, baseRVal, ignoreVirt);
1108 EmitPopPostProp (scg, errorAt, declVar.type, setProp);
1109 } else {
1110 scg.ilGen.Emit (errorAt, OpCodes.Pop);
1111 }
1112 }
1113
1114 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
1115 {
1116 // accessing it in any way can't be trivial if reading the pointer isn't trivial.
1117 // this also handles strict right-to-left mode detection as the side-effect can
1118 // only apply to the pointer (it can't change which field or method we access).
1119 if (!baseRVal.IsReadTrivial (scg, readAt)) return false;
1120
1121 // now the only way it can be non-trivial to read is if it is a property and the
1122 // getter() method is non-trivial. reading a method means getting a delegate
1123 // which is always trivial, and reading a simple field is always trivial, ie, no
1124 // CheckRun() call can possibly be involved.
1125 if (declVar.retType != null) {
1126 // a method's value, ie, without applying the (arglist), is a delegate...
1127 return true;
1128 }
1129 if (declVar.vTableArray != null) {
1130 // a field's value is its XMRSDTypeClObj.instVars array element
1131 return true;
1132 }
1133 if (declVar.getProp != null) {
1134 // a property's value is calling its get method with no arguments
1135 return declVar.getProp.IsFuncTrivial (scg);
1136 }
1137
1138 // write-only property
1139 return true;
1140 }
1141
1142 public override void CallPre (ScriptCodeGen scg, Token errorAt)
1143 {
1144 if (declVar.retType != null) {
1145 CallPreMethod (scg, errorAt);
1146 } else {
1147 base.CallPre (scg, errorAt);
1148 }
1149 }
1150 public override void CallPost (ScriptCodeGen scg, Token errorAt)
1151 {
1152 if (declVar.retType != null) {
1153 CallPostMethod (scg, errorAt);
1154 } else {
1155 base.CallPost (scg, errorAt);
1156 }
1157 }
1158
1159 /**
1160 * @brief A PushVal() for a method means to push a delegate for the method on the stack.
1161 */
1162 private void PushValMethod (ScriptCodeGen scg, Token errorAt)
1163 {
1164 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) throw new Exception ("dont use for statics");
1165
1166 if (ignoreVirt || (declVar.vTableIndex < 0)) {
1167
1168 /*
1169 * Non-virtual instance method, create a delegate that references the method.
1170 */
1171 string dtn = type.ToString ();
1172
1173 // delegateinstance = (signature)scriptinstance.GetScriptMethodDelegate (methName, signature, arg0);
1174 // where methName = <sdtclass>.<methname>(<argtypes>)
1175 // signature = <rettype>(<argtypes>)
1176 // arg0 = sdt istance (XMRSDTypeClObj) 'this' value
1177 scg.PushXMRInst (); // [0] scriptinstance
1178 scg.ilGen.Emit (errorAt, OpCodes.Ldstr, declVar.ilGen.methName); // [1] method name
1179 scg.ilGen.Emit (errorAt, OpCodes.Ldstr, dtn); // [2] delegate type name
1180 baseRVal.PushVal (scg, errorAt); // [3] sdtinstance
1181 scg.ilGen.Emit (errorAt, OpCodes.Callvirt, gsmdMethodInfo); // [0] delegate instance
1182 scg.ilGen.Emit (errorAt, OpCodes.Castclass, type.ToSysType ()); // [0] cast to correct delegate class
1183 } else {
1184
1185 /*
1186 * Virtual instance method, get the delegate from the vtable.
1187 */
1188 baseRVal.PushVal (scg, errorAt); // 'this' selecting the instance
1189 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, vTableFieldInfo); // get pointer to instance's vtable array
1190 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, declVar.vTableIndex); // select vtable element
1191 scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (Delegate)); // get delegate pointer = 'this' for 'Invoke()'
1192 scg.ilGen.Emit (errorAt, OpCodes.Castclass, type.ToSysType ()); // cast to correct delegate class
1193 }
1194 }
1195
1196 private void CallPreMethod (ScriptCodeGen scg, Token errorAt)
1197 {
1198 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) throw new Exception ("dont use for statics");
1199
1200 if (!this.declVar.IsFuncTrivial (scg)) new ScriptCodeGen.CallLabel (scg, errorAt);
1201
1202 if (ignoreVirt || (declVar.vTableIndex < 0)) {
1203 baseRVal.PushVal (scg, errorAt); // 'this' being passed directly to method
1204 } else {
1205 baseRVal.PushVal (scg, errorAt); // 'this' selecting the instance
1206 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, vTableFieldInfo); // get pointer to instance's vtable array
1207 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, declVar.vTableIndex); // select vtable element
1208 scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (Delegate)); // get delegate pointer = 'this' for 'Invoke()'
1209 scg.ilGen.Emit (errorAt, OpCodes.Castclass, type.ToSysType ()); // cast to correct delegate class
1210 }
1211 }
1212 private void CallPostMethod (ScriptCodeGen scg, Token errorAt)
1213 {
1214 if (ignoreVirt || (declVar.vTableIndex < 0)) {
1215 // non-virt instance, just call function directly
1216 scg.ilGen.Emit (errorAt, OpCodes.Call, declVar.ilGen);
1217 } else {
1218 // virtual, call via delegate Invoke(...) method
1219 TokenTypeSDTypeDelegate ttd = (TokenTypeSDTypeDelegate)type;
1220 MethodInfo invokeMethodInfo = ttd.decl.GetInvokerInfo ();
1221 scg.ilGen.Emit (errorAt, OpCodes.Callvirt, invokeMethodInfo);
1222 }
1223
1224 if (!this.declVar.IsFuncTrivial (scg)) scg.openCallLabel = null;
1225 }
1226 }
1227
1228 // The value is an integer constant
1229 public class CompValuInteger : CompValu {
1230 public int x;
1231
1232 public CompValuInteger (TokenType type, int x) : base (type)
1233 {
1234 if (!(this.type is TokenTypeInt)) {
1235 this.type = new TokenTypeInt (this.type);
1236 }
1237 this.x = x;
1238 }
1239 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1240 {
1241 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, x);
1242 }
1243 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1244 {
1245 throw new Exception ("cannot get constant's address");
1246 }
1247 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1248 {
1249 throw new Exception ("cannot store into constant");
1250 }
1251 }
1252
1253 // The value is an element of a list
1254 public class CompValuListEl : CompValu {
1255 private static readonly MethodInfo getElementFromListMethodInfo =
1256 typeof (CompValuListEl).GetMethod ("GetElementFromList", new Type[] { typeof (LSL_List), typeof (int) });
1257
1258 private CompValu theList;
1259 private CompValu subscript;
1260
1261 public CompValuListEl (TokenType type, CompValu theList, CompValu subscript) : base (type)
1262 {
1263 this.theList = theList;
1264 this.subscript = subscript;
1265 }
1266 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1267 {
1268 theList.PushVal (scg, errorAt, new TokenTypeList (type));
1269 subscript.PushVal (scg, errorAt, new TokenTypeInt (type));
1270 scg.ilGen.Emit (errorAt, OpCodes.Call, getElementFromListMethodInfo);
1271 }
1272 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1273 {
1274 throw new Exception ("cannot get list element's address");
1275 }
1276 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1277 {
1278 scg.ErrorMsg (errorAt, "cannot store into list element");
1279 scg.ilGen.Emit (errorAt, OpCodes.Pop);
1280 }
1281
1282 public static object GetElementFromList (LSL_List lis, int idx)
1283 {
1284 object element = lis.Data[idx];
1285 if (element is LSL_Float) return TypeCast.EHArgUnwrapFloat (element);
1286 if (element is LSL_Integer) return TypeCast.EHArgUnwrapInteger (element);
1287 if (element is LSL_String) return TypeCast.EHArgUnwrapString (element);
1288 if (element is OpenMetaverse.Quaternion) return TypeCast.EHArgUnwrapRotation (element);
1289 if (element is OpenMetaverse.Vector3) return TypeCast.EHArgUnwrapVector (element);
1290 return element;
1291 }
1292 }
1293
1294 // The value is kept in a script-addressable local variable
1295 public class CompValuLocalVar : CompValu {
1296 private static int htpopseq = 0;
1297
1298 private ScriptMyLocal localBuilder;
1299
1300 public CompValuLocalVar (TokenType type, string name, ScriptCodeGen scg) : base (type)
1301 {
1302 if (type.ToHeapTrackerType () != null) {
1303 this.localBuilder = scg.ilGen.DeclareLocal (type.ToHeapTrackerType (), name);
1304 scg.PushXMRInst ();
1305 scg.ilGen.Emit (type, OpCodes.Newobj, type.GetHeapTrackerCtor ());
1306 scg.ilGen.Emit (type, OpCodes.Stloc, localBuilder);
1307 } else {
1308 this.localBuilder = scg.ilGen.DeclareLocal (ToSysType (), name);
1309 }
1310 }
1311
1312 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1313 {
1314 scg.ilGen.Emit (errorAt, OpCodes.Ldloc, localBuilder);
1315 if (type.ToHeapTrackerType () != null) {
1316 scg.ilGen.Emit (errorAt, OpCodes.Call, type.GetHeapTrackerPushMeth ());
1317 }
1318 }
1319 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1320 {
1321 if (type.ToHeapTrackerType () != null) {
1322 scg.ErrorMsg (errorAt, "can't take ref of heap-tracked type " + type.ToString ());
1323 scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
1324 } else {
1325 scg.ilGen.Emit (errorAt, OpCodes.Ldloca, localBuilder);
1326 }
1327 }
1328
1329 public override void PopPre (ScriptCodeGen scg, Token errorAt)
1330 {
1331 if (type.ToHeapTrackerType () != null) {
1332 scg.ilGen.Emit (errorAt, OpCodes.Ldloc, localBuilder);
1333 }
1334 }
1335 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1336 {
1337 if (type.ToHeapTrackerType () != null) {
1338 scg.ilGen.Emit (errorAt, OpCodes.Call, type.GetHeapTrackerPopMeth ());
1339 } else {
1340 scg.ilGen.Emit (errorAt, OpCodes.Stloc, localBuilder);
1341 }
1342 }
1343
1344 public void Pop (ScriptCodeGen scg, Token errorAt)
1345 {
1346 if (type.ToHeapTrackerType () != null) {
1347 /*
1348 * Popping into a heap tracker wrapped local variable.
1349 * First pop value into a temp var, then call the heap tracker's pop method.
1350 */
1351 ScriptMyLocal htpop = scg.ilGen.DeclareLocal (type.ToSysType (), "htpop$" + (++ htpopseq).ToString ());
1352 scg.ilGen.Emit (errorAt, OpCodes.Stloc, htpop);
1353 scg.ilGen.Emit (errorAt, OpCodes.Ldloc, localBuilder);
1354 scg.ilGen.Emit (errorAt, OpCodes.Ldloc, htpop);
1355 scg.ilGen.Emit (errorAt, OpCodes.Call, type.GetHeapTrackerPopMeth ());
1356 } else {
1357
1358 /*
1359 * Not a heap-tracked local var, just pop directly into it.
1360 */
1361 scg.ilGen.Emit (errorAt, OpCodes.Stloc, localBuilder);
1362 }
1363 }
1364
1365 // non-trivial because it needs to be copied into a temp
1366 // in case the idiot does dumb-ass side effects tricks
1367 // eg, (x = 0) + x + 2
1368 // should read old value of x not 0
1369 // but if 'xmroption norighttoleft;' in effect,
1370 // we can read it in any order so reading a
1371 // local variable is trivial.
1372 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
1373 {
1374 return readAt.nr2l;
1375 }
1376 }
1377
1378 // The value is a null
1379 public class CompValuNull : CompValu {
1380 public CompValuNull (TokenType type) : base (type) { }
1381 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1382 {
1383 scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
1384 }
1385 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1386 {
1387 throw new Exception ("cannot get null's address");
1388 }
1389 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1390 {
1391 throw new Exception ("cannot store into null");
1392 }
1393 }
1394
1395 // The value is a rotation
1396 public class CompValuRot : CompValu {
1397 public CompValu x;
1398 public CompValu y;
1399 public CompValu z;
1400 public CompValu w;
1401
1402 private static readonly ConstructorInfo lslRotConstructorInfo =
1403 typeof (LSL_Rotation).GetConstructor (new Type[] { typeof (double),
1404 typeof (double),
1405 typeof (double),
1406 typeof (double) });
1407
1408 public CompValuRot (TokenType type, CompValu x, CompValu y, CompValu z, CompValu w) :
1409 base (type)
1410 {
1411 if (!(type is TokenTypeRot)) {
1412 this.type = new TokenTypeRot (type);
1413 }
1414 this.x = x;
1415 this.y = y;
1416 this.z = z;
1417 this.w = w;
1418 }
1419 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1420 {
1421 this.x.PushVal (scg, errorAt, new TokenTypeFloat (this.x.type));
1422 this.y.PushVal (scg, errorAt, new TokenTypeFloat (this.y.type));
1423 this.z.PushVal (scg, errorAt, new TokenTypeFloat (this.z.type));
1424 this.w.PushVal (scg, errorAt, new TokenTypeFloat (this.w.type));
1425 scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslRotConstructorInfo);
1426 }
1427 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1428 {
1429 throw new Exception ("cannot get constant's address");
1430 }
1431 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1432 {
1433 throw new Exception ("cannot store into constant");
1434 }
1435
1436 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
1437 {
1438 // the supplied values must be trivial because when we call their PushVal()s
1439 // there will be stuff on the stack for all but the first PushVal() and so
1440 // they would have a non-empty stack at their call label.
1441 if (!this.w.IsReadTrivial (scg, readAt) ||
1442 !this.x.IsReadTrivial (scg, readAt) ||
1443 !this.y.IsReadTrivial (scg, readAt) ||
1444 !this.z.IsReadTrivial (scg, readAt)) {
1445 throw new Exception ("rotation values must be trivial");
1446 }
1447
1448 return true;
1449 }
1450 }
1451
1452 // The value is in a static field of an internally defined struct/class
1453 public class CompValuSField : CompValu {
1454 public FieldInfo field;
1455
1456 public CompValuSField (TokenType type, FieldInfo field) : base (type)
1457 {
1458 this.field = field;
1459 }
1460 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1461 {
1462 if ((field.Attributes & FieldAttributes.Literal) == 0) {
1463 scg.ilGen.Emit (errorAt, OpCodes.Ldsfld, field);
1464 return;
1465 }
1466 if (field.FieldType == typeof (LSL_Rotation)) {
1467 LSL_Rotation rot = (LSL_Rotation)field.GetValue (null);
1468 scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, rot.x);
1469 scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, rot.y);
1470 scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, rot.z);
1471 scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, rot.s);
1472 scg.ilGen.Emit (errorAt, OpCodes.Newobj, ScriptCodeGen.lslRotationConstructorInfo);
1473 return;
1474 }
1475 if (field.FieldType == typeof (LSL_Vector)) {
1476 LSL_Vector vec = (LSL_Vector)field.GetValue (null);
1477 scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, vec.x);
1478 scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, vec.y);
1479 scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, vec.z);
1480 scg.ilGen.Emit (errorAt, OpCodes.Newobj, ScriptCodeGen.lslRotationConstructorInfo);
1481 return;
1482 }
1483 if (field.FieldType == typeof (string)) {
1484 string str = (string)field.GetValue (null);
1485 scg.ilGen.Emit (errorAt, OpCodes.Ldstr, str);
1486 return;
1487 }
1488 throw new Exception ("unsupported literal type " + field.FieldType.Name);
1489 }
1490 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1491 {
1492 if ((field.Attributes & FieldAttributes.Literal) != 0) {
1493 throw new Exception ("can't write a constant");
1494 }
1495 scg.ilGen.Emit (errorAt, OpCodes.Ldflda, field);
1496 }
1497 public override void PopPre (ScriptCodeGen scg, Token errorAt)
1498 {
1499 }
1500 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1501 {
1502 if ((field.Attributes & FieldAttributes.Literal) != 0) {
1503 throw new Exception ("can't write a constant");
1504 }
1505 scg.ilGen.Emit (errorAt, OpCodes.Stsfld, field);
1506 }
1507
1508 // non-trivial because it needs to be copied into a temp
1509 // in case the idiot does dumb-ass side effects tricks
1510 // eg, (x = 0) + x + 2
1511 // should read old value of x not 0
1512 // but if 'xmroption norighttoleft;' in effect,
1513 // we can read it in any order so reading a
1514 // local variable is trivial.
1515 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
1516 {
1517 return readAt.nr2l;
1518 }
1519 }
1520
1521 // The value is a character within a string
1522 public class CompValuStrChr : CompValu {
1523 private static readonly MethodInfo getCharFromStringMethodInfo =
1524 typeof (CompValuStrChr).GetMethod ("GetCharFromString", new Type[] { typeof (string), typeof (int) });
1525
1526 private CompValu theString;
1527 private CompValu subscript;
1528
1529 public CompValuStrChr (TokenType type, CompValu theString, CompValu subscript) : base (type)
1530 {
1531 this.theString = theString;
1532 this.subscript = subscript;
1533 }
1534 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1535 {
1536 theString.PushVal (scg, errorAt, new TokenTypeStr (type));
1537 subscript.PushVal (scg, errorAt, new TokenTypeInt (type));
1538 scg.ilGen.Emit (errorAt, OpCodes.Call, getCharFromStringMethodInfo);
1539 }
1540 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1541 {
1542 throw new Exception ("cannot get string character's address");
1543 }
1544 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1545 {
1546 scg.ErrorMsg (errorAt, "cannot store into string character");
1547 scg.ilGen.Emit (errorAt, OpCodes.Pop);
1548 }
1549
1550 public static char GetCharFromString (string s, int i)
1551 {
1552 return s[i];
1553 }
1554 }
1555
1556 // The value is a key or string constant
1557 public class CompValuString : CompValu {
1558 public string x;
1559
1560 public CompValuString (TokenType type, string x) : base (type)
1561 {
1562 if (!(type is TokenTypeKey) && !(this.type is TokenTypeStr)) {
1563 throw new Exception ("bad type " + type.ToString ());
1564 }
1565 this.x = x;
1566 }
1567 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1568 {
1569 scg.ilGen.Emit (errorAt, OpCodes.Ldstr, x);
1570 }
1571 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1572 {
1573 throw new Exception ("cannot get constant's address");
1574 }
1575 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1576 {
1577 throw new Exception ("cannot store into constant");
1578 }
1579 }
1580
1581 // The value is kept in a temp local variable
1582 public class CompValuTemp : CompValu {
1583 public ScriptMyLocal localBuilder;
1584
1585 public CompValuTemp (TokenType type, ScriptCodeGen scg) : base (type)
1586 {
1587 string name = "tmp$" + (++ scg.tempCompValuNum);
1588 this.localBuilder = scg.ilGen.DeclareLocal (ToSysType(), name);
1589 }
1590 protected CompValuTemp (TokenType type) : base (type) { } // CompValuVoid uses this
1591
1592 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1593 {
1594 scg.ilGen.Emit (errorAt, OpCodes.Ldloc, localBuilder);
1595 }
1596 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1597 {
1598 scg.ilGen.Emit (errorAt, OpCodes.Ldloca, localBuilder);
1599 }
1600 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1601 {
1602 scg.ilGen.Emit (errorAt, OpCodes.Stloc, localBuilder);
1603 }
1604 public void Pop (ScriptCodeGen scg, Token errorAt, TokenType stackType)
1605 {
1606 TypeCast.CastTopOfStack (scg, errorAt, stackType, this.type, false);
1607 this.PopPost (scg, errorAt); // in case PopPost() overridden eg by CompValuVoid
1608 }
1609 public void Pop (ScriptCodeGen scg, Token errorAt)
1610 {
1611 this.PopPost (scg, errorAt); // in case PopPost() overridden eg by CompValuVoid
1612 }
1613 }
1614
1615 // The value is a vector
1616 public class CompValuVec : CompValu {
1617 public CompValu x;
1618 public CompValu y;
1619 public CompValu z;
1620
1621 private static readonly ConstructorInfo lslVecConstructorInfo =
1622 typeof (LSL_Vector).GetConstructor (new Type[] { typeof (double),
1623 typeof (double),
1624 typeof (double) });
1625
1626 public CompValuVec (TokenType type, CompValu x, CompValu y, CompValu z) : base (type)
1627 {
1628 if (!(type is TokenTypeVec)) {
1629 this.type = new TokenTypeVec (type);
1630 }
1631 this.x = x;
1632 this.y = y;
1633 this.z = z;
1634 }
1635 public override void PushVal (ScriptCodeGen scg, Token errorAt)
1636 {
1637 this.x.PushVal (scg, errorAt, new TokenTypeFloat (this.x.type));
1638 this.y.PushVal (scg, errorAt, new TokenTypeFloat (this.y.type));
1639 this.z.PushVal (scg, errorAt, new TokenTypeFloat (this.z.type));
1640 scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslVecConstructorInfo);
1641 }
1642 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1643 {
1644 throw new Exception ("cannot get constant's address");
1645 }
1646 public override void PopPost (ScriptCodeGen scg, Token errorAt)
1647 {
1648 throw new Exception ("cannot store into constant");
1649 }
1650
1651 public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
1652 {
1653 // the supplied values must be trivial because when we call their PushVal()s
1654 // there will be stuff on the stack for all but the first PushVal() and so
1655 // they would have a non-empty stack at their call label.
1656 if (!this.x.IsReadTrivial (scg, readAt) ||
1657 !this.y.IsReadTrivial (scg, readAt) ||
1658 !this.z.IsReadTrivial (scg, readAt)) {
1659 throw new Exception ("vector values must be trivial");
1660 }
1661
1662 return true;
1663 }
1664 }
1665
1666 // Used to indicate value will be discarded (eg, where to put return value from a call)
1667 public class CompValuVoid : CompValuTemp {
1668 public CompValuVoid (Token token) : base ((token is TokenTypeVoid) ? (TokenTypeVoid)token : new TokenTypeVoid (token))
1669 { }
1670 public override void PushVal (ScriptCodeGen scg, Token errorAt) { }
1671 public override void PushRef (ScriptCodeGen scg, Token errorAt)
1672 {
1673 throw new Exception ("cannot get void address");
1674 }
1675 public override void PopPost (ScriptCodeGen scg, Token errorAt) { }
1676 }
1677}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompile.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompile.cs
new file mode 100644
index 0000000..017d2c5
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompile.cs
@@ -0,0 +1,216 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/**
29 * @brief Compile a script to produce a ScriptObjCode object
30 */
31
32using System;
33using System.IO;
34using System.IO.Compression;
35using System.Reflection;
36using System.Security.Cryptography;
37using System.Text;
38
39namespace OpenSim.Region.ScriptEngine.XMREngine
40{
41 public partial class XMRInstance
42 {
43 /**
44 * @brief Compile a script to produce a ScriptObjCode object
45 * @returns object code pointer or null if compile error
46 * also can throw compile error exception
47 */
48 public ScriptObjCode Compile ()
49 {
50 bool oldObjFile = false;
51 Stream objFileStream = null;
52 StreamWriter asmFileWriter = null;
53 string envar = null;
54 string sourceHash = null;
55 TextWriter saveSource = null;
56
57 string asmFileName = GetScriptFileName (m_ScriptObjCodeKey + ".xmrasm");
58 string lslFileName = GetScriptFileName (m_ScriptObjCodeKey + ".lsl");
59 string objFileName = GetScriptFileName (m_ScriptObjCodeKey + ".xmrobj");
60 string tmpFileName = GetScriptFileName (m_ScriptObjCodeKey + ".xmrtmp");
61
62 /*
63 * If we already have an object file, don't bother compiling.
64 */
65 if (!m_ForceRecomp && File.Exists (objFileName)) {
66 objFileStream = File.OpenRead (objFileName);
67 oldObjFile = true;
68 } else {
69
70 /*
71 * If source file empty, try to read from asset server.
72 */
73 if (EmptySource (m_SourceCode)) {
74 m_SourceCode = FetchSource (m_CameFrom);
75 }
76
77 /*
78 * Maybe write script source to a file for debugging.
79 */
80 envar = Environment.GetEnvironmentVariable ("MMRScriptCompileSaveSource");
81 if ((envar != null) && ((envar[0] & 1) != 0)) {
82 m_log.Debug ("[XMREngine]: MMRScriptCompileSaveSource: saving to " + lslFileName);
83 saveSource = File.CreateText (lslFileName);
84 }
85
86 /*
87 * Parse source string into tokens.
88 */
89 TokenBegin tokenBegin;
90 try {
91 tokenBegin = TokenBegin.Construct(m_CameFrom, saveSource, ErrorHandler, m_SourceCode, out sourceHash);
92 } finally {
93 if (saveSource != null) saveSource.Close ();
94 }
95 if (tokenBegin == null) {
96 m_log.Debug ("[XMREngine]: parsing errors on " + m_ScriptObjCodeKey);
97 return null;
98 }
99
100 /*
101 * Create object file one way or another.
102 */
103 try {
104 objFileStream = File.Create (tmpFileName);
105
106 /*
107 * Create abstract syntax tree from raw tokens.
108 */
109 TokenScript tokenScript = ScriptReduce.Reduce(tokenBegin);
110 if (tokenScript == null) {
111 m_log.Warn ("[XMREngine]: reduction errors on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")");
112 PrintCompilerErrors ();
113 return null;
114 }
115
116 /*
117 * Compile abstract syntax tree to write object file.
118 */
119 BinaryWriter objFileWriter = new BinaryWriter (objFileStream);
120 bool ok = ScriptCodeGen.CodeGen(tokenScript, objFileWriter, sourceHash);
121 if (!ok) {
122 m_log.Warn ("[XMREngine]: compile error on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")");
123 PrintCompilerErrors ();
124 objFileStream.Close ();
125 return null;
126 }
127 objFileStream.Close ();
128
129 /*
130 * File has been completely written.
131 * If there is an old one laying around, delete it now.
132 * Then re-open the new file for reading from the beginning.
133 */
134 if (File.Exists (objFileName)) {
135 File.Replace (tmpFileName, objFileName, null);
136 } else {
137 File.Move (tmpFileName, objFileName);
138 }
139 objFileStream = File.OpenRead (objFileName);
140 } finally {
141
142 /*
143 * In case something went wrong writing temp file, delete it.
144 */
145 try {
146 File.Delete (tmpFileName);
147 } catch {
148 }
149 }
150
151 /*
152 * Since we just wrote the .xmrobj file, maybe save disassembly.
153 */
154 envar = Environment.GetEnvironmentVariable ("MMRScriptCompileSaveILGen");
155 if ((envar != null) && ((envar[0] & 1) != 0)) {
156 m_log.Debug ("[XMREngine]: MMRScriptCompileSaveILGen: saving to " + asmFileName);
157 asmFileWriter = File.CreateText (asmFileName);
158 }
159 }
160
161 /*
162 * Read object file to create ScriptObjCode object.
163 * Maybe also write disassembly to a file for debugging.
164 */
165 BinaryReader objFileReader = new BinaryReader (objFileStream);
166 ScriptObjCode scriptObjCode = null;
167 try {
168 scriptObjCode = new ScriptObjCode (objFileReader, asmFileWriter, null);
169 if (scriptObjCode != null) {
170 scriptObjCode.fileDateUtc = File.GetLastWriteTimeUtc (objFileName);
171 }
172 } finally {
173 objFileReader.Close ();
174 if (asmFileWriter != null) {
175 asmFileWriter.Flush ();
176 asmFileWriter.Close ();
177 }
178 }
179
180 /*
181 * Maybe an old object file has reached its expiration date.
182 */
183 if (oldObjFile && (scriptObjCode != null) && scriptObjCode.IsExpired ()) {
184 m_log.Debug ("[XMREngine]: expiration reached on " + m_ScriptObjCodeKey + ", reloading");
185 m_ForceRecomp = true;
186 scriptObjCode = Compile ();
187 }
188
189 return scriptObjCode;
190 }
191
192 private void PrintCompilerErrors ()
193 {
194 m_log.Info ("[XMREngine]: - " + m_Part.GetWorldPosition () + " " + m_DescName);
195 foreach (string error in m_CompilerErrors) {
196 m_log.Info ("[XMREngine]: - " + error);
197 }
198 }
199
200 /**
201 * @brief Check for empty source, allowing for a first line of //... script engine selector.
202 */
203 public static bool EmptySource (string source)
204 {
205 int len = source.Length;
206 bool skipeol = false;
207 for (int i = 0; i < len; i ++) {
208 char c = source[i];
209 skipeol &= c != '\n';
210 skipeol |= (c == '/') && (i + 1 < len) && (source[i+1] == '/');
211 if ((c > ' ') && !skipeol) return false;
212 }
213 return true;
214 }
215 }
216}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptConsts.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptConsts.cs
new file mode 100644
index 0000000..4cbb19c
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptConsts.cs
@@ -0,0 +1,250 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Text;
32
33using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
34using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
35using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
36using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
37using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
38using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
39using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
40
41namespace OpenSim.Region.ScriptEngine.XMREngine {
42
43 public class ScriptConst {
44
45 public static Dictionary<string, ScriptConst> scriptConstants = Init ();
46
47 /**
48 * @brief look up the value of a given built-in constant.
49 * @param name = name of constant
50 * @returns null: no constant by that name defined
51 * else: pointer to ScriptConst struct
52 */
53 public static ScriptConst Lookup (string name)
54 {
55 ScriptConst sc;
56 if (!scriptConstants.TryGetValue (name, out sc)) sc = null;
57 return sc;
58 }
59
60 private static Dictionary<string, ScriptConst> Init ()
61 {
62 Dictionary<string, ScriptConst> sc = new Dictionary<string, ScriptConst> ();
63
64 /*
65 * For every event code, define XMREVENTCODE_<eventname> and XMREVENTMASKn_<eventname> symbols.
66 */
67 for (int i = 0; i < 64; i ++) {
68 try {
69 string s = ((ScriptEventCode)i).ToString ();
70 if ((s.Length > 0) && (s[0] >= 'a') && (s[0] <= 'z')) {
71 new ScriptConst (sc,
72 "XMREVENTCODE_" + s,
73 new CompValuInteger (new TokenTypeInt (null), i));
74 int n = i / 32 + 1;
75 int m = 1 << (i % 32);
76 new ScriptConst (sc,
77 "XMREVENTMASK" + n + "_" + s,
78 new CompValuInteger (new TokenTypeInt (null), m));
79 }
80 } catch { }
81 }
82
83 /*
84 * Also get all the constants from XMRInstAbstract and ScriptBaseClass etc as well.
85 */
86 for (Type t = typeof (XMRInstAbstract); t != typeof (object); t = t.BaseType) {
87 AddInterfaceConstants (sc, t.GetFields ());
88 }
89
90 return sc;
91 }
92
93 /**
94 * @brief Add all constants defined by the given interface.
95 */
96 // this one accepts only upper-case named fields
97 public static void AddInterfaceConstants (Dictionary<string, ScriptConst> sc, FieldInfo[] allFields)
98 {
99 List<FieldInfo> ucfs = new List<FieldInfo> (allFields.Length);
100 foreach (FieldInfo f in allFields) {
101 string fieldName = f.Name;
102 int i;
103 for (i = fieldName.Length; -- i >= 0;) {
104 if ("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".IndexOf (fieldName[i]) < 0) break;
105 }
106 if (i < 0) ucfs.Add (f);
107 }
108 AddInterfaceConstants (sc, ucfs.GetEnumerator ());
109 }
110
111 // this one accepts all fields given to it
112 public static void AddInterfaceConstants (Dictionary<string, ScriptConst> sc, IEnumerator<FieldInfo> fields)
113 {
114 if (sc == null) sc = scriptConstants;
115
116 for (fields.Reset (); fields.MoveNext ();) {
117 FieldInfo constField = fields.Current;
118 Type fieldType = constField.FieldType;
119 CompValu cv;
120
121 /*
122 * The location of a simple number is the number itself.
123 * Access to the value gets compiled as an ldc instruction.
124 */
125 if (fieldType == typeof (double)) {
126 cv = new CompValuFloat (new TokenTypeFloat (null),
127 (double)(double)constField.GetValue (null));
128 } else if (fieldType == typeof (int)) {
129 cv = new CompValuInteger (new TokenTypeInt (null),
130 (int)constField.GetValue (null));
131 } else if (fieldType == typeof (LSL_Integer)) {
132 cv = new CompValuInteger (new TokenTypeInt (null),
133 ((LSL_Integer)constField.GetValue (null)).value);
134 }
135
136 /*
137 * The location of a string is the string itself.
138 * Access to the value gets compiled as an ldstr instruction.
139 */
140 else if (fieldType == typeof (string)) {
141 cv = new CompValuString (new TokenTypeStr (null),
142 (string)constField.GetValue (null));
143 } else if (fieldType == typeof (LSL_String)) {
144 cv = new CompValuString (new TokenTypeStr (null),
145 (string)(LSL_String)constField.GetValue (null));
146 }
147
148 /*
149 * The location of everything else (objects) is the static field in the interface definition.
150 * Access to the value gets compiled as an ldsfld instruction.
151 */
152 else {
153 cv = new CompValuSField (TokenType.FromSysType (null, fieldType), constField);
154 }
155
156 /*
157 * Add to dictionary.
158 */
159 new ScriptConst (sc, constField.Name, cv);
160 }
161 }
162
163 /**
164 * @brief Add arbitrary constant available to script compilation.
165 * CAUTION: These values get compiled-in to a script and must not
166 * change over time as previously compiled scripts will
167 * still have the old values.
168 */
169 public static ScriptConst AddConstant (string name, object value)
170 {
171 CompValu cv = null;
172
173 if (value is char) {
174 cv = new CompValuChar (new TokenTypeChar (null), (char)value);
175 }
176 if (value is double) {
177 cv = new CompValuFloat (new TokenTypeFloat (null), (double)(double)value);
178 }
179 if (value is float) {
180 cv = new CompValuFloat (new TokenTypeFloat (null), (double)(float)value);
181 }
182 if (value is int) {
183 cv = new CompValuInteger (new TokenTypeInt (null), (int)value);
184 }
185 if (value is string) {
186 cv = new CompValuString (new TokenTypeStr (null), (string)value);
187 }
188
189 if (value is LSL_Float) {
190 cv = new CompValuFloat (new TokenTypeFloat (null), (double)((LSL_Float)value).value);
191 }
192 if (value is LSL_Integer) {
193 cv = new CompValuInteger (new TokenTypeInt (null), ((LSL_Integer)value).value);
194 }
195 if (value is LSL_Rotation) {
196 LSL_Rotation r = (LSL_Rotation)value;
197 CompValu x = new CompValuFloat (new TokenTypeFloat (null), r.x);
198 CompValu y = new CompValuFloat (new TokenTypeFloat (null), r.y);
199 CompValu z = new CompValuFloat (new TokenTypeFloat (null), r.z);
200 CompValu s = new CompValuFloat (new TokenTypeFloat (null), r.s);
201 cv = new CompValuRot (new TokenTypeRot (null), x, y, z, s);
202 }
203 if (value is LSL_String) {
204 cv = new CompValuString (new TokenTypeStr (null), (string)(LSL_String)value);
205 }
206 if (value is LSL_Vector) {
207 LSL_Vector v = (LSL_Vector)value;
208 CompValu x = new CompValuFloat (new TokenTypeFloat (null), v.x);
209 CompValu y = new CompValuFloat (new TokenTypeFloat (null), v.y);
210 CompValu z = new CompValuFloat (new TokenTypeFloat (null), v.z);
211 cv = new CompValuVec (new TokenTypeVec (null), x, y, z);
212 }
213
214 if (value is OpenMetaverse.Quaternion) {
215 OpenMetaverse.Quaternion r = (OpenMetaverse.Quaternion)value;
216 CompValu x = new CompValuFloat (new TokenTypeFloat (null), r.X);
217 CompValu y = new CompValuFloat (new TokenTypeFloat (null), r.Y);
218 CompValu z = new CompValuFloat (new TokenTypeFloat (null), r.Z);
219 CompValu s = new CompValuFloat (new TokenTypeFloat (null), r.W);
220 cv = new CompValuRot (new TokenTypeRot (null), x, y, z, s);
221 }
222 if (value is OpenMetaverse.UUID) {
223 cv = new CompValuString (new TokenTypeKey (null), value.ToString ());
224 }
225 if (value is OpenMetaverse.Vector3) {
226 OpenMetaverse.Vector3 v = (OpenMetaverse.Vector3)value;
227 CompValu x = new CompValuFloat (new TokenTypeFloat (null), v.X);
228 CompValu y = new CompValuFloat (new TokenTypeFloat (null), v.Y);
229 CompValu z = new CompValuFloat (new TokenTypeFloat (null), v.Z);
230 cv = new CompValuVec (new TokenTypeVec (null), x, y, z);
231 }
232
233 if (cv == null) throw new Exception ("bad type " + value.GetType ().Name);
234 return new ScriptConst (scriptConstants, name, cv);
235 }
236
237 /*
238 * Instance variables
239 */
240 public string name;
241 public CompValu rVal;
242
243 private ScriptConst (Dictionary<string, ScriptConst> lc, string name, CompValu rVal)
244 {
245 lc.Add (name, this);
246 this.name = name;
247 this.rVal = rVal;
248 }
249 }
250}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptEventCode.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptEventCode.cs
new file mode 100644
index 0000000..8e8b755
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptEventCode.cs
@@ -0,0 +1,95 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28namespace OpenSim.Region.ScriptEngine.XMREngine {
29
30 /**
31 * @brief List of event codes that can be passed to StartEventHandler().
32 * Must have same name as corresponding event handler name, so
33 * the compiler will know what column in the seht to put the
34 * event handler entrypoint in.
35 *
36 * Also, ScriptConst.Init() builds symbols of name XMREVENTCODE_<name>
37 * and XMREVENTMASK<n>_<name> with the values and masks of all symbols
38 * in range 0..63 that begin with a lower-case letter for scripts to
39 * reference.
40 */
41 public enum ScriptEventCode : int {
42
43 // used by XMRInstance to indicate no event being processed
44 None = -1,
45
46 // must be bit numbers of equivalent values in ...
47 // OpenSim.Region.ScriptEngine.Shared.ScriptBase.scriptEvents
48 // ... so they can be passed to m_Part.SetScriptEvents().
49 attach = 0,
50 state_exit = 1,
51 timer = 2,
52 touch = 3,
53 collision = 4,
54 collision_end = 5,
55 collision_start = 6,
56 control = 7,
57 dataserver = 8,
58 email = 9,
59 http_response = 10,
60 land_collision = 11,
61 land_collision_end = 12,
62 land_collision_start = 13,
63 at_target = 14,
64 listen = 15,
65 money = 16,
66 moving_end = 17,
67 moving_start = 18,
68 not_at_rot_target = 19,
69 not_at_target = 20,
70 touch_start = 21,
71 object_rez = 22,
72 remote_data = 23,
73 at_rot_target = 24,
74 transaction_result = 25,
75 run_time_permissions = 28,
76 touch_end = 29,
77 state_entry = 30,
78
79 // events not passed to m_Part.SetScriptEvents().
80 changed = 33,
81 link_message = 34,
82 no_sensor = 35,
83 on_rez = 36,
84 sensor = 37,
85 http_request = 38,
86
87 path_update = 40,
88
89 // XMRE specific
90 region_cross = 63,
91
92 // marks highest numbered event, ie, number of columns in seht.
93 Size = 64
94 }
95}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs
new file mode 100644
index 0000000..dc00001
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs
@@ -0,0 +1,664 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Reflection.Emit;
32using System.Text;
33
34using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
35using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
36using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
37using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
38using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
39using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
40using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
41
42/**
43 * @brief Generate code for the backend API calls.
44 */
45namespace OpenSim.Region.ScriptEngine.XMREngine
46{
47 public abstract class TokenDeclInline : TokenDeclVar {
48 public static VarDict inlineFunctions = CreateDictionary ();
49
50 public abstract void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args);
51
52 private static string[] noCheckRuns;
53 private static string[] keyReturns;
54
55 protected bool isTaggedCallsCheckRun;
56
57 /**
58 * @brief Create a dictionary of inline backend API functions.
59 */
60 private static VarDict CreateDictionary ()
61 {
62 /*
63 * For those listed in noCheckRun, we just generate the call (simple computations).
64 * For all others, we generate the call then a call to CheckRun().
65 */
66 noCheckRuns = new string[] {
67 "llBase64ToString",
68 "llCSV2List",
69 "llDeleteSubList",
70 "llDeleteSubString",
71 "llDumpList2String",
72 "llEscapeURL",
73 "llEuler2Rot",
74 "llGetListEntryType",
75 "llGetListLength",
76 "llGetSubString",
77 "llGetUnixTime",
78 "llInsertString",
79 "llList2CSV",
80 "llList2Float",
81 "llList2Integer",
82 "llList2Key",
83 "llList2List",
84 "llList2ListStrided",
85 "llList2Rot",
86 "llList2String",
87 "llList2Vector",
88 "llListFindList",
89 "llListInsertList",
90 "llListRandomize",
91 "llListReplaceList",
92 "llListSort",
93 "llListStatistics",
94 "llMD5String",
95 "llParseString2List",
96 "llParseStringKeepNulls",
97 "llRot2Euler",
98 "llStringLength",
99 "llStringToBase64",
100 "llStringTrim",
101 "llSubStringIndex",
102 "llUnescapeURL"
103 };
104
105 /*
106 * These functions really return a 'key' even though we see them as
107 * returning 'string' because OpenSim has key and string as same type.
108 */
109 keyReturns = new string[] {
110 "llAvatarOnLinkSitTarget",
111 "llAvatarOnSitTarget",
112 "llDetectedKey",
113 "llDetectedOwner",
114 "llGenerateKey",
115 "llGetCreator",
116 "llGetInventoryCreator",
117 "llGetInventoryKey",
118 "llGetKey",
119 "llGetLandOwnerAt",
120 "llGetLinkKey",
121 "llGetNotecardLine",
122 "llGetNumberOfNotecardLines",
123 "llGetOwner",
124 "llGetOwnerKey",
125 "llGetPermissionsKey",
126 "llHTTPRequest",
127 "llList2Key",
128 "llRequestAgentData",
129 "llRequestDisplayName",
130 "llRequestInventoryData",
131 "llRequestSecureURL",
132 "llRequestSimulatorData",
133 "llRequestURL",
134 "llRequestUsername",
135 "llSendRemoteData",
136 "llTransferLindenDollars"
137 };
138
139 VarDict ifd = new VarDict (false);
140
141 Type[] oneDoub = new Type[] { typeof (double) };
142 Type[] twoDoubs = new Type[] { typeof (double), typeof (double) };
143
144 /*
145 * Mono generates an FPU instruction for many math calls.
146 */
147 new TokenDeclInline_LLAbs (ifd);
148 new TokenDeclInline_Math (ifd, "llAcos(float)", "Acos", oneDoub);
149 new TokenDeclInline_Math (ifd, "llAsin(float)", "Asin", oneDoub);
150 new TokenDeclInline_Math (ifd, "llAtan2(float,float)", "Atan2", twoDoubs);
151 new TokenDeclInline_Math (ifd, "llCos(float)", "Cos", oneDoub);
152 new TokenDeclInline_Math (ifd, "llFabs(float)", "Abs", oneDoub);
153 new TokenDeclInline_Math (ifd, "llLog(float)", "Log", oneDoub);
154 new TokenDeclInline_Math (ifd, "llLog10(float)", "Log10", oneDoub);
155 new TokenDeclInline_Math (ifd, "llPow(float,float)", "Pow", twoDoubs);
156 new TokenDeclInline_LLRound (ifd);
157 new TokenDeclInline_Math (ifd, "llSin(float)", "Sin", oneDoub);
158 new TokenDeclInline_Math (ifd, "llSqrt(float)", "Sqrt", oneDoub);
159 new TokenDeclInline_Math (ifd, "llTan(float)", "Tan", oneDoub);
160
161 /*
162 * Something weird about the code generation for these calls, so they all have their own handwritten code generators.
163 */
164 new TokenDeclInline_GetFreeMemory (ifd);
165 new TokenDeclInline_GetUsedMemory (ifd);
166
167 /*
168 * These are all the xmr...() calls directly in XMRInstAbstract.
169 * Includes the calls from ScriptBaseClass that has all the stubs
170 * which convert XMRInstAbstract to the various <NAME>_Api contexts.
171 */
172 MethodInfo[] absmeths = typeof (XMRInstAbstract).GetMethods ();
173 AddInterfaceMethods (ifd, absmeths, null);
174
175 return ifd;
176 }
177
178 /**
179 * @brief Add API functions from the given interface to list of built-in functions.
180 * Only functions beginning with a lower-case letter are entered, all others ignored.
181 * @param ifd = internal function dictionary to add them to
182 * @param ifaceMethods = list of API functions
183 * @param acf = which field in XMRInstanceSuperType holds method's 'this' pointer
184 */
185 // this one accepts only names beginning with a lower-case letter
186 public static void AddInterfaceMethods (VarDict ifd, MethodInfo[] ifaceMethods, FieldInfo acf)
187 {
188 List<MethodInfo> lcms = new List<MethodInfo> (ifaceMethods.Length);
189 foreach (MethodInfo meth in ifaceMethods)
190 {
191 string name = meth.Name;
192 if ((name[0] >= 'a') && (name[0] <= 'z')) {
193 lcms.Add (meth);
194 }
195 }
196 AddInterfaceMethods (ifd, lcms.GetEnumerator (), acf);
197 }
198
199 // this one accepts all methods given to it
200 public static void AddInterfaceMethods (VarDict ifd, IEnumerator<MethodInfo> ifaceMethods, FieldInfo acf)
201 {
202 if (ifd == null) ifd = inlineFunctions;
203
204 for (ifaceMethods.Reset (); ifaceMethods.MoveNext ();) {
205 MethodInfo ifaceMethod = ifaceMethods.Current;
206 string key = ifaceMethod.Name;
207
208 try {
209 /*
210 * See if we will generate a call to CheckRun() right
211 * after we generate a call to the function.
212 * If function begins with xmr, assume we will not call CheckRun()
213 * Otherwise, assume we will call CheckRun()
214 */
215 bool dcr = !key.StartsWith ("xmr");
216 foreach (string ncr in noCheckRuns) {
217 if (ncr == key) {
218 dcr = false;
219 break;
220 }
221 }
222
223 /*
224 * Add function to dictionary.
225 */
226 new TokenDeclInline_BEApi (ifd, dcr, ifaceMethod, acf);
227 } catch {
228 ///??? IGNORE ANY THAT FAIL - LIKE UNRECOGNIZED TYPE ???///
229 ///??? and OVERLOADED NAMES ???///
230 }
231 }
232 }
233
234 /**
235 * @brief Add an inline function definition to the dictionary.
236 * @param ifd = dictionary to add inline definition to
237 * @param doCheckRun = true iff the generated code or the function itself can possibly call CheckRun()
238 * @param nameArgSig = inline function signature string, in form <name>(<arglsltypes>,...)
239 * @param retType = return type, use TokenTypeVoid if no return value
240 */
241 protected TokenDeclInline (VarDict ifd,
242 bool doCheckRun,
243 string nameArgSig,
244 TokenType retType)
245 : base (null, null, null)
246 {
247 this.retType = retType;
248 this.triviality = doCheckRun ? Triviality.complex : Triviality.trivial;
249
250 int j = nameArgSig.IndexOf ('(');
251 this.name = new TokenName (null, nameArgSig.Substring (0, j ++));
252
253 this.argDecl = new TokenArgDecl (null);
254 if (nameArgSig[j] != ')') {
255 int i;
256 TokenName name;
257 TokenType type;
258
259 for (i = j; nameArgSig[i] != ')'; i ++) {
260 if (nameArgSig[i] == ',') {
261 type = TokenType.FromLSLType (null, nameArgSig.Substring (j, i - j));
262 name = new TokenName (null, "arg" + this.argDecl.varDict.Count);
263 this.argDecl.AddArg (type, name);
264 j = i + 1;
265 }
266 }
267
268 type = TokenType.FromLSLType (null, nameArgSig.Substring (j, i - j));
269 name = new TokenName (null, "arg" + this.argDecl.varDict.Count);
270 this.argDecl.AddArg (type, name);
271 }
272
273 this.location = new CompValuInline (this);
274 if (ifd == null) ifd = inlineFunctions;
275 ifd.AddEntry (this);
276 }
277
278 protected TokenDeclInline (VarDict ifd,
279 bool doCheckRun,
280 MethodInfo methInfo)
281 : base (null, null, null)
282 {
283 TokenType retType = TokenType.FromSysType (null, methInfo.ReturnType);
284
285 this.isTaggedCallsCheckRun = IsTaggedCallsCheckRun (methInfo);
286 this.name = new TokenName (null, methInfo.Name);
287 this.retType = GetRetType (methInfo, retType);
288 this.argDecl = GetArgDecl (methInfo.GetParameters ());
289 this.triviality = (doCheckRun || this.isTaggedCallsCheckRun) ? Triviality.complex : Triviality.trivial;
290 this.location = new CompValuInline (this);
291
292 if (ifd == null) ifd = inlineFunctions;
293 ifd.AddEntry (this);
294 }
295
296 private static TokenArgDecl GetArgDecl (ParameterInfo[] parameters)
297 {
298 TokenArgDecl argDecl = new TokenArgDecl (null);
299 foreach (ParameterInfo pi in parameters) {
300 TokenType type = TokenType.FromSysType (null, pi.ParameterType);
301 TokenName name = new TokenName (null, pi.Name);
302 argDecl.AddArg (type, name);
303 }
304 return argDecl;
305 }
306
307 /**
308 * @brief The above code assumes all methods beginning with 'xmr' are trivial, ie,
309 * they do not call CheckRun() and also we do not generate a CheckRun()
310 * call after they return. So if an 'xmr' method does call CheckRun(), it
311 * must be tagged with attribute 'xmrMethodCallsCheckRunAttribute' so we know
312 * the method is not trivial. But in neither case do we emit our own call
313 * to CheckRun(), the 'xmr' method must do its own. We do however set up a
314 * call label before the call to the non-trivial 'xmr' method so when we are
315 * restoring the call stack, the restore will call directly in to the 'xmr'
316 * method without re-executing any code before the call to the 'xmr' method.
317 */
318 private static bool IsTaggedCallsCheckRun (MethodInfo methInfo)
319 {
320 return (methInfo != null) &&
321 Attribute.IsDefined (methInfo, typeof (xmrMethodCallsCheckRunAttribute));
322 }
323
324 /**
325 * @brief The dumbass OpenSim has key and string as the same type so non-ll
326 * methods must be tagged with xmrMethodReturnsKeyAttribute if we
327 * are to think they return a key type, otherwise we will think they
328 * return string.
329 */
330 private static TokenType GetRetType (MethodInfo methInfo, TokenType retType)
331 {
332 if ((methInfo != null) && (retType != null) && (retType is TokenTypeStr)) {
333 if (Attribute.IsDefined (methInfo, typeof (xmrMethodReturnsKeyAttribute))) {
334 return ChangeToKeyType (retType);
335 }
336
337 string mn = methInfo.Name;
338 foreach (string kr in keyReturns) {
339 if (kr == mn) return ChangeToKeyType (retType);
340 }
341
342 }
343 return retType;
344 }
345 private static TokenType ChangeToKeyType (TokenType retType)
346 {
347 if (retType is TokenTypeLSLString) {
348 retType = new TokenTypeLSLKey (null);
349 } else {
350 retType = new TokenTypeKey (null);
351 }
352 return retType;
353 }
354
355 public virtual MethodInfo GetMethodInfo ()
356 {
357 return null;
358 }
359
360 /**
361 * @brief Print out a list of all the built-in functions and constants.
362 */
363 public delegate void WriteLine (string str);
364 public static void PrintBuiltins (bool inclNoisyTag, WriteLine writeLine)
365 {
366 writeLine ("\nBuilt-in functions:\n");
367 SortedDictionary<string, TokenDeclInline> bifs = new SortedDictionary<string, TokenDeclInline> ();
368 foreach (TokenDeclVar bif in TokenDeclInline.inlineFunctions) {
369 bifs.Add (bif.fullName, (TokenDeclInline)bif);
370 }
371 foreach (TokenDeclInline bif in bifs.Values) {
372 char noisy = (!inclNoisyTag || !IsTaggedNoisy (bif.GetMethodInfo ())) ? ' ' : (bif.retType is TokenTypeVoid) ? 'N' : 'R';
373 writeLine (noisy + " " + bif.retType.ToString ().PadLeft (8) + " " + bif.fullName);
374 }
375 if (inclNoisyTag) {
376 writeLine ("\nN - stub that writes name and arguments to stdout");
377 writeLine ("R - stub that writes name and arguments to stdout then reads return value from stdin");
378 writeLine (" format is: function_name : return_value");
379 writeLine (" example: llKey2Name:\"Kunta Kinte\"");
380 }
381
382 writeLine ("\nBuilt-in constants:\n");
383 SortedDictionary<string, ScriptConst> scs = new SortedDictionary<string, ScriptConst> ();
384 int widest = 0;
385 foreach (ScriptConst sc in ScriptConst.scriptConstants.Values) {
386 if (widest < sc.name.Length) widest = sc.name.Length;
387 scs.Add (sc.name, sc);
388 }
389 foreach (ScriptConst sc in scs.Values) {
390 writeLine (" " + sc.rVal.type.ToString ().PadLeft (8) + " " + sc.name.PadRight (widest) + " = " + BuiltInConstVal (sc.rVal));
391 }
392 }
393
394 public static bool IsTaggedNoisy (MethodInfo methInfo)
395 {
396 return (methInfo != null) && Attribute.IsDefined (methInfo, typeof (xmrMethodIsNoisyAttribute));
397 }
398
399 public static string BuiltInConstVal (CompValu rVal)
400 {
401 if (rVal is CompValuInteger) {
402 int x = ((CompValuInteger)rVal).x;
403 return "0x" + x.ToString ("X8") + " = " + x.ToString ().PadLeft (11);
404 }
405 if (rVal is CompValuFloat) return ((CompValuFloat)rVal).x.ToString ();
406 if (rVal is CompValuString) {
407 StringBuilder sb = new StringBuilder ();
408 PrintParam (sb, ((CompValuString)rVal).x);
409 return sb.ToString ();
410 }
411 if (rVal is CompValuSField) {
412 FieldInfo fi = ((CompValuSField)rVal).field;
413 StringBuilder sb = new StringBuilder ();
414 PrintParam (sb, fi.GetValue (null));
415 return sb.ToString ();
416 }
417 return rVal.ToString (); // just prints the type
418 }
419
420 public static void PrintParam (StringBuilder sb, object p)
421 {
422 if (p == null) {
423 sb.Append ("null");
424 } else if (p is LSL_List) {
425 sb.Append ('[');
426 object[] d = ((LSL_List)p).Data;
427 for (int i = 0; i < d.Length; i ++) {
428 if (i > 0) sb.Append (',');
429 PrintParam (sb, d[i]);
430 }
431 sb.Append (']');
432 } else if (p is LSL_Rotation) {
433 LSL_Rotation r = (LSL_Rotation)p;
434 sb.Append ('<');
435 sb.Append (r.x);
436 sb.Append (',');
437 sb.Append (r.y);
438 sb.Append (',');
439 sb.Append (r.z);
440 sb.Append (',');
441 sb.Append (r.s);
442 sb.Append ('>');
443 } else if (p is LSL_String) {
444 PrintParamString (sb, (string)(LSL_String)p);
445 } else if (p is LSL_Vector) {
446 LSL_Vector v = (LSL_Vector)p;
447 sb.Append ('<');
448 sb.Append (v.x);
449 sb.Append (',');
450 sb.Append (v.y);
451 sb.Append (',');
452 sb.Append (v.z);
453 sb.Append ('>');
454 } else if (p is string) {
455 PrintParamString (sb, (string)p);
456 } else {
457 sb.Append (p.ToString ());
458 }
459 }
460
461 public static void PrintParamString (StringBuilder sb, string p)
462 {
463 sb.Append ('"');
464 foreach (char c in p) {
465 if (c == '\b') {
466 sb.Append ("\\b");
467 continue;
468 }
469 if (c == '\n') {
470 sb.Append ("\\n");
471 continue;
472 }
473 if (c == '\r') {
474 sb.Append ("\\r");
475 continue;
476 }
477 if (c == '\t') {
478 sb.Append ("\\t");
479 continue;
480 }
481 if (c == '"') {
482 sb.Append ("\\\"");
483 continue;
484 }
485 if (c == '\\') {
486 sb.Append ("\\\\");
487 continue;
488 }
489 sb.Append (c);
490 }
491 sb.Append ('"');
492 }
493 }
494
495 /**
496 * @brief Code generators...
497 * @param scg = script we are generating code for
498 * @param result = type/location for result (type matches function definition)
499 * @param args = type/location of arguments (types match function definition)
500 */
501
502 public class TokenDeclInline_LLAbs : TokenDeclInline {
503 public TokenDeclInline_LLAbs (VarDict ifd)
504 : base (ifd, false, "llAbs(integer)", new TokenTypeInt (null)) { }
505
506 public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
507 {
508 ScriptMyLabel itsPosLabel = scg.ilGen.DefineLabel ("llAbstemp");
509
510 args[0].PushVal (scg, errorAt);
511 scg.ilGen.Emit (errorAt, OpCodes.Dup);
512 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
513 scg.ilGen.Emit (errorAt, OpCodes.Bge_S, itsPosLabel);
514 scg.ilGen.Emit (errorAt, OpCodes.Neg);
515 scg.ilGen.MarkLabel (itsPosLabel);
516 result.Pop (scg, errorAt, retType);
517 }
518 }
519
520 public class TokenDeclInline_Math : TokenDeclInline {
521 private MethodInfo methInfo;
522
523 public TokenDeclInline_Math (VarDict ifd, string sig, string name, Type[] args)
524 : base (ifd, false, sig, new TokenTypeFloat (null))
525 {
526 methInfo = ScriptCodeGen.GetStaticMethod (typeof (System.Math), name, args);
527 }
528
529 public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
530 {
531 for (int i = 0; i < args.Length; i ++) {
532 args[i].PushVal (scg, errorAt, argDecl.types[i]);
533 }
534 scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
535 result.Pop (scg, errorAt, retType);
536 }
537 }
538
539 public class TokenDeclInline_LLRound : TokenDeclInline {
540
541 private static MethodInfo roundMethInfo = ScriptCodeGen.GetStaticMethod (typeof (System.Math), "Round",
542 new Type[] { typeof (double), typeof (MidpointRounding) });
543
544 public TokenDeclInline_LLRound (VarDict ifd)
545 : base (ifd, false, "llRound(float)", new TokenTypeInt (null)) { }
546
547 public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
548 {
549 args[0].PushVal (scg, errorAt, new TokenTypeFloat (null));
550 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, (int)System.MidpointRounding.AwayFromZero);
551 scg.ilGen.Emit (errorAt, OpCodes.Call, roundMethInfo);
552 result.Pop (scg, errorAt, new TokenTypeFloat (null));
553 }
554 }
555
556 public class TokenDeclInline_GetFreeMemory : TokenDeclInline {
557 private static readonly MethodInfo getFreeMemMethInfo = typeof (XMRInstAbstract).GetMethod ("xmrHeapLeft", new Type[] { });
558
559 public TokenDeclInline_GetFreeMemory (VarDict ifd)
560 : base (ifd, false, "llGetFreeMemory()", new TokenTypeInt (null)) { }
561
562 // appears as llGetFreeMemory() in script source code
563 // but actually calls xmrHeapLeft()
564 public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
565 {
566 scg.PushXMRInst ();
567 scg.ilGen.Emit (errorAt, OpCodes.Call, getFreeMemMethInfo);
568 result.Pop (scg, errorAt, new TokenTypeInt (null));
569 }
570 }
571
572 public class TokenDeclInline_GetUsedMemory : TokenDeclInline {
573 private static readonly MethodInfo getUsedMemMethInfo = typeof (XMRInstAbstract).GetMethod ("xmrHeapUsed", new Type[] { });
574
575 public TokenDeclInline_GetUsedMemory (VarDict ifd)
576 : base (ifd, false, "llGetUsedMemory()", new TokenTypeInt (null)) { }
577
578 // appears as llGetUsedMemory() in script source code
579 // but actually calls xmrHeapUsed()
580 public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
581 {
582 scg.PushXMRInst ();
583 scg.ilGen.Emit (errorAt, OpCodes.Call, getUsedMemMethInfo);
584 result.Pop (scg, errorAt, new TokenTypeInt (null));
585 }
586 }
587
588 /**
589 * @brief Generate code for the usual ll...() functions.
590 */
591 public class TokenDeclInline_BEApi : TokenDeclInline {
592 private static readonly MethodInfo fixLLParcelMediaQuery = ScriptCodeGen.GetStaticMethod
593 (typeof (XMRInstAbstract), "FixLLParcelMediaQuery", new Type[] { typeof (LSL_List) });
594
595 private static readonly MethodInfo fixLLParcelMediaCommandList = ScriptCodeGen.GetStaticMethod
596 (typeof (XMRInstAbstract), "FixLLParcelMediaCommandList", new Type[] { typeof (LSL_List) });
597
598 public bool doCheckRun;
599 private FieldInfo apiContextField;
600 private MethodInfo methInfo;
601
602 /**
603 * @brief Constructor
604 * @param ifd = dictionary to add the function to
605 * @param dcr = append a call to CheckRun()
606 * @param methInfo = ll...() method to be called
607 */
608 public TokenDeclInline_BEApi (VarDict ifd, bool dcr, MethodInfo methInfo, FieldInfo acf)
609 : base (ifd, dcr, methInfo)
610 {
611 this.methInfo = methInfo;
612 doCheckRun = dcr;
613 apiContextField = acf;
614 }
615
616 public override MethodInfo GetMethodInfo ()
617 {
618 return methInfo;
619 }
620
621 /**
622 * @brief Generate call to backend API function (eg llSay()) maybe followed by a call to CheckRun().
623 * @param scg = script being compiled
624 * @param result = where to place result (might be void)
625 * @param args = script-visible arguments to pass to API function
626 */
627 public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
628 {
629 if (isTaggedCallsCheckRun) { // see if 'xmr' method that calls CheckRun() internally
630 new ScriptCodeGen.CallLabel (scg, errorAt); // if so, put a call label immediately before it
631 // .. so restoring the frame will jump immediately to the
632 // .. call without re-executing any code before this
633 }
634 if (!methInfo.IsStatic) {
635 scg.PushXMRInst (); // XMRInstanceSuperType pointer
636 if (apiContextField != null) {
637 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, apiContextField);
638 // 'this' pointer for API function
639 }
640 }
641 for (int i = 0; i < args.Length; i ++) { // push arguments, boxing/unboxing as needed
642 args[i].PushVal (scg, errorAt, argDecl.types[i]);
643 }
644 if (methInfo.Name == "llParcelMediaQuery") {
645 scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaQuery);
646 }
647 if (methInfo.Name == "llParcelMediaCommandList") {
648 scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaCommandList);
649 }
650 if (methInfo.IsVirtual) { // call API function
651 scg.ilGen.Emit (errorAt, OpCodes.Callvirt, methInfo);
652 } else {
653 scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
654 }
655 result.Pop (scg, errorAt, retType); // pop result, boxing/unboxing as needed
656 if (isTaggedCallsCheckRun) {
657 scg.openCallLabel = null;
658 }
659 if (doCheckRun) {
660 scg.EmitCallCheckRun (errorAt, false); // maybe call CheckRun()
661 }
662 }
663 }
664}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptMyILGen.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptMyILGen.cs
new file mode 100644
index 0000000..ecc217e
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptMyILGen.cs
@@ -0,0 +1,81 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Reflection;
30using System.Reflection.Emit;
31
32namespace OpenSim.Region.ScriptEngine.XMREngine
33{
34 public interface ScriptMyILGen
35 {
36 string methName { get; }
37 ScriptMyLocal DeclareLocal (Type type, string name);
38 ScriptMyLabel DefineLabel (string name);
39 void BeginExceptionBlock ();
40 void BeginCatchBlock (Type excType);
41 void BeginFinallyBlock ();
42 void EndExceptionBlock ();
43 void Emit (Token errorAt, OpCode opcode);
44 void Emit (Token errorAt, OpCode opcode, FieldInfo field);
45 void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal);
46 void Emit (Token errorAt, OpCode opcode, Type type);
47 void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel);
48 void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels);
49 void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method);
50 void Emit (Token errorAt, OpCode opcode, MethodInfo method);
51 void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor);
52 void Emit (Token errorAt, OpCode opcode, double value);
53 void Emit (Token errorAt, OpCode opcode, float value);
54 void Emit (Token errorAt, OpCode opcode, int value);
55 void Emit (Token errorAt, OpCode opcode, string value);
56 void MarkLabel (ScriptMyLabel myLabel);
57 }
58
59 /**
60 * @brief One of these per label defined in the function.
61 */
62 public class ScriptMyLabel {
63 public string name;
64 public int number;
65
66 public GraphNodeMarkLabel whereAmI;
67 public Type[] stackDepth;
68 public bool[] stackBoxeds;
69 }
70
71 /**
72 * @brief One of these per local variable defined in the function.
73 */
74 public class ScriptMyLocal {
75 public string name;
76 public Type type;
77 public int number;
78
79 public bool isReferenced;
80 }
81}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjCode.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjCode.cs
new file mode 100644
index 0000000..038dfcd
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjCode.cs
@@ -0,0 +1,256 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.XMREngine;
29using System;
30using System.Collections.Generic;
31using System.IO;
32using System.Reflection;
33using System.Reflection.Emit;
34
35namespace OpenSim.Region.ScriptEngine.XMREngine
36{
37 public delegate void ScriptEventHandler (XMRInstAbstract instance);
38
39 /*
40 * This object represents the output of the compilation.
41 * Once the compilation is complete, its contents should be
42 * considered 'read-only', so it can be shared among multiple
43 * instances of the script.
44 *
45 * It gets created by ScriptCodeGen.
46 * It gets used by XMRInstance to create script instances.
47 */
48 public class ScriptObjCode
49 {
50 public string sourceHash; // source text hash code
51
52 public XMRInstArSizes glblSizes = new XMRInstArSizes ();
53 // number of global variables of various types
54
55 public string[] stateNames; // convert state number to corresponding string
56
57 public ScriptEventHandler[,] scriptEventHandlerTable;
58 // entrypoints to all event handler functions
59 // 1st subscript = state code number (0=default)
60 // 2nd subscript = event code number
61 // null entry means no handler defined for that state,event
62
63 public Dictionary<string, TokenDeclSDType> sdObjTypesName;
64 // all script-defined types by name
65 public TokenDeclSDType[] sdObjTypesIndx;
66 // all script-defined types by sdTypeIndex
67
68 public Dictionary<Type, string> sdDelTypes;
69 // all script-defined delegates (including anonymous)
70
71 public Dictionary<string, DynamicMethod> dynamicMethods;
72 // all dyanmic methods
73
74 public Dictionary<string, KeyValuePair<int, ScriptSrcLoc>[]> scriptSrcLocss;
75 // method,iloffset -> source file,line,posn
76
77 public int refCount; // used by engine to keep track of number of
78 // instances that are using this object code
79
80 public Dictionary<string,Dictionary<int,string>> globalVarNames = new Dictionary<string,Dictionary<int,string>> ();
81
82 public DateTime fileDateUtc;
83 public int expiryDays = Int32.MaxValue;
84 public bool IsExpired ()
85 {
86 return (DateTime.UtcNow.Ticks - fileDateUtc.Ticks) / 10000000 / 86400 >= expiryDays;
87 }
88
89 /**
90 * @brief Fill in ScriptObjCode from an XMREngine object file.
91 * 'objFileReader' is a serialized form of the CIL code we generated
92 * 'asmFileWriter' is where we write the disassembly to (or null if not wanted)
93 * 'srcFileWriter' is where we write the decompilation to (or null if not wanted)
94 * Throws an exception if there is any error (theoretically).
95 */
96 public ScriptObjCode (BinaryReader objFileReader, TextWriter asmFileWriter, TextWriter srcFileWriter)
97 {
98 /*
99 * Check version number to make sure we know how to process file contents.
100 */
101 char[] ocm = objFileReader.ReadChars (ScriptCodeGen.OBJECT_CODE_MAGIC.Length);
102 if (new String (ocm) != ScriptCodeGen.OBJECT_CODE_MAGIC) {
103 throw new Exception ("not an XMR object file (bad magic)");
104 }
105 int cvv = objFileReader.ReadInt32 ();
106 if (cvv != ScriptCodeGen.COMPILED_VERSION_VALUE) {
107 throw new CVVMismatchException (cvv, ScriptCodeGen.COMPILED_VERSION_VALUE);
108 }
109
110 /*
111 * Fill in simple parts of scriptObjCode object.
112 */
113 sourceHash = objFileReader.ReadString ();
114 expiryDays = objFileReader.ReadInt32 ();
115 glblSizes.ReadFromFile (objFileReader);
116
117 int nStates = objFileReader.ReadInt32 ();
118
119 stateNames = new string[nStates];
120 for (int i = 0; i < nStates; i ++) {
121 stateNames[i] = objFileReader.ReadString ();
122 if (asmFileWriter != null) {
123 asmFileWriter.WriteLine (" state[{0}] = {1}", i, stateNames[i]);
124 }
125 }
126
127 if (asmFileWriter != null) {
128 glblSizes.WriteAsmFile (asmFileWriter, "numGbl");
129 }
130
131 string gblName;
132 while ((gblName = objFileReader.ReadString ()) != "") {
133 string gblType = objFileReader.ReadString ();
134 int gblIndex = objFileReader.ReadInt32 ();
135 Dictionary<int,string> names;
136 if (!globalVarNames.TryGetValue (gblType, out names)) {
137 names = new Dictionary<int,string> ();
138 globalVarNames.Add (gblType, names);
139 }
140 names.Add (gblIndex, gblName);
141 if (asmFileWriter != null) {
142 asmFileWriter.WriteLine (" {0} = {1}[{2}]", gblName, gblType, gblIndex);
143 }
144 }
145
146 /*
147 * Read in script-defined types.
148 */
149 sdObjTypesName = new Dictionary<string, TokenDeclSDType> ();
150 sdDelTypes = new Dictionary<Type, string> ();
151 int maxIndex = -1;
152 while ((gblName = objFileReader.ReadString ()) != "") {
153 TokenDeclSDType sdt = TokenDeclSDType.ReadFromFile (sdObjTypesName,
154 gblName, objFileReader, asmFileWriter);
155 sdObjTypesName.Add (gblName, sdt);
156 if (maxIndex < sdt.sdTypeIndex) maxIndex = sdt.sdTypeIndex;
157 if (sdt is TokenDeclSDTypeDelegate) {
158 sdDelTypes.Add (sdt.GetSysType (), gblName);
159 }
160 }
161 sdObjTypesIndx = new TokenDeclSDType[maxIndex+1];
162 foreach (TokenDeclSDType sdt in sdObjTypesName.Values) {
163 sdObjTypesIndx[sdt.sdTypeIndex] = sdt;
164 }
165
166 /*
167 * Now fill in the methods (the hard part).
168 */
169 scriptEventHandlerTable = new ScriptEventHandler[nStates,(int)ScriptEventCode.Size];
170 dynamicMethods = new Dictionary<string, DynamicMethod> ();
171 scriptSrcLocss = new Dictionary<string, KeyValuePair<int, ScriptSrcLoc>[]> ();
172
173 ObjectTokens objectTokens = null;
174 if (asmFileWriter != null) {
175 objectTokens = new OTDisassemble (this, asmFileWriter);
176 } else if (srcFileWriter != null) {
177 objectTokens = new OTDecompile (this, srcFileWriter);
178 }
179
180 try {
181 ScriptObjWriter.CreateObjCode (sdObjTypesName, objFileReader, this, objectTokens);
182 } finally {
183 if (objectTokens != null) objectTokens.Close ();
184 }
185
186 /*
187 * We enter all script event handler methods in the ScriptEventHandler table.
188 * They are named: <statename> <eventname>
189 */
190 foreach (KeyValuePair<string, DynamicMethod> kvp in dynamicMethods) {
191 string methName = kvp.Key;
192 int i = methName.IndexOf (' ');
193 if (i < 0) continue;
194 string stateName = methName.Substring (0, i);
195 string eventName = methName.Substring (++ i);
196 int stateCode;
197 for (stateCode = stateNames.Length; -- stateCode >= 0;) {
198 if (stateNames[stateCode] == stateName) break;
199 }
200 int eventCode = (int)Enum.Parse (typeof (ScriptEventCode), eventName);
201 scriptEventHandlerTable[stateCode,eventCode] =
202 (ScriptEventHandler)kvp.Value.CreateDelegate (typeof (ScriptEventHandler));
203 }
204
205 /*
206 * Fill in all script-defined class vtables.
207 */
208 foreach (TokenDeclSDType sdt in sdObjTypesIndx) {
209 if ((sdt != null) && (sdt is TokenDeclSDTypeClass)) {
210 TokenDeclSDTypeClass sdtc = (TokenDeclSDTypeClass)sdt;
211 sdtc.FillVTables (this);
212 }
213 }
214 }
215
216 /**
217 * @brief Called once for every method found in objFileReader file.
218 * It enters the method in the ScriptObjCode object table so it can be called.
219 */
220 public void EndMethod (DynamicMethod method, Dictionary<int, ScriptSrcLoc> srcLocs)
221 {
222 /*
223 * Save method object code pointer.
224 */
225 dynamicMethods.Add (method.Name, method);
226
227 /*
228 * Build and sort iloffset -> source code location array.
229 */
230 int n = srcLocs.Count;
231 KeyValuePair<int, ScriptSrcLoc>[] srcLocArray = new KeyValuePair<int, ScriptSrcLoc>[n];
232 n = 0;
233 foreach (KeyValuePair<int, ScriptSrcLoc> kvp in srcLocs) srcLocArray[n++] = kvp;
234 Array.Sort (srcLocArray, endMethodWrapper);
235
236 /*
237 * Save sorted array.
238 */
239 scriptSrcLocss.Add (method.Name, srcLocArray);
240 }
241
242 /**
243 * @brief Called once for every method found in objFileReader file.
244 * It enters the method in the ScriptObjCode object table so it can be called.
245 */
246 private static EndMethodWrapper endMethodWrapper = new EndMethodWrapper ();
247 private class EndMethodWrapper : System.Collections.IComparer {
248 public int Compare (object x, object y)
249 {
250 KeyValuePair<int, ScriptSrcLoc> kvpx = (KeyValuePair<int, ScriptSrcLoc>)x;
251 KeyValuePair<int, ScriptSrcLoc> kvpy = (KeyValuePair<int, ScriptSrcLoc>)y;
252 return kvpx.Key - kvpy.Key;
253 }
254 }
255 }
256}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjWriter.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjWriter.cs
new file mode 100644
index 0000000..e4e0ac8
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjWriter.cs
@@ -0,0 +1,947 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using System;
30using System.Collections.Generic;
31using System.IO;
32using System.Reflection;
33using System.Reflection.Emit;
34using System.Text;
35
36using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
37using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
38using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
39using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
40using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
41using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
42using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
43
44/**
45 * @brief Wrapper class for ILGenerator.
46 * It writes the object code to a file and can then make real ILGenerator calls
47 * based on the file's contents.
48 */
49namespace OpenSim.Region.ScriptEngine.XMREngine
50{
51 public enum ScriptObjWriterCode : byte {
52 BegMethod, EndMethod, TheEnd,
53 DclLabel, DclLocal, DclMethod, MarkLabel,
54 EmitNull, EmitField, EmitLocal, EmitType, EmitLabel, EmitMethodExt,
55 EmitMethodInt, EmitCtor, EmitDouble, EmitFloat, EmitInteger, EmitString,
56 EmitLabels,
57 BegExcBlk, BegCatBlk, BegFinBlk, EndExcBlk
58 }
59
60 public class ScriptObjWriter : ScriptMyILGen
61 {
62 private static Dictionary<short, OpCode> opCodes = PopulateOpCodes ();
63 private static Dictionary<string, Type> string2Type = PopulateS2T ();
64 private static Dictionary<Type, string> type2String = PopulateT2S ();
65
66 private static MethodInfo monoGetCurrentOffset = typeof (ILGenerator).GetMethod ("Mono_GetCurrentOffset",
67 BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null,
68 new Type[] { typeof (ILGenerator) }, null);
69
70 private static readonly OpCode[] opCodesLdcI4M1P8 = new OpCode[] {
71 OpCodes.Ldc_I4_M1, OpCodes.Ldc_I4_0, OpCodes.Ldc_I4_1, OpCodes.Ldc_I4_2, OpCodes.Ldc_I4_3,
72 OpCodes.Ldc_I4_4, OpCodes.Ldc_I4_5, OpCodes.Ldc_I4_6, OpCodes.Ldc_I4_7, OpCodes.Ldc_I4_8
73 };
74
75 private BinaryWriter objFileWriter;
76 private string lastErrorAtFile = "";
77 private int lastErrorAtLine = 0;
78 private int lastErrorAtPosn = 0;
79
80 private Dictionary<Type, string> sdTypesRev = new Dictionary<Type, string> ();
81 public int labelNumber = 0;
82 public int localNumber = 0;
83
84 private string _methName;
85 public string methName { get { return _methName; } }
86
87 public Type retType;
88 public Type[] argTypes;
89
90 /**
91 * @brief Begin function declaration
92 * @param sdTypes = script-defined types
93 * @param methName = name of the method being declared, eg, "Verify(array,list,string)"
94 * @param retType = its return value type
95 * @param argTypes[] = its argument types
96 * @param objFileWriter = file to write its object code to
97 *
98 * After calling this function, the following functions should be called:
99 * this.BegMethod ();
100 * this.<as required> ();
101 * this.EndMethod ();
102 *
103 * The design of this object is such that many constructors may be called,
104 * but once a BegMethod() is called for one of the objects, no method may
105 * called for any of the other objects until EndMethod() is called (or it
106 * would break up the object stream for that method). But we need to have
107 * many constructors possible so we get function headers at the beginning
108 * of the object file in case there are forward references to the functions.
109 */
110 public ScriptObjWriter (TokenScript tokenScript, string methName, Type retType, Type[] argTypes, string[] argNames, BinaryWriter objFileWriter)
111 {
112 this._methName = methName;
113 this.retType = retType;
114 this.argTypes = argTypes;
115 this.objFileWriter = objFileWriter;
116
117 /*
118 * Build list that translates system-defined types to script defined types.
119 */
120 foreach (TokenDeclSDType sdt in tokenScript.sdSrcTypesValues) {
121 Type sys = sdt.GetSysType();
122 if (sys != null) sdTypesRev[sys] = sdt.longName.val;
123 }
124
125 /*
126 * This tells the reader to call 'new DynamicMethod()' to create
127 * the function header. Then any forward reference calls to this
128 * method will have a MethodInfo struct to call.
129 */
130 objFileWriter.Write ((byte)ScriptObjWriterCode.DclMethod);
131 objFileWriter.Write (methName);
132 objFileWriter.Write (GetStrFromType (retType));
133
134 int nArgs = argTypes.Length;
135 objFileWriter.Write (nArgs);
136 for (int i = 0; i < nArgs; i ++) {
137 objFileWriter.Write (GetStrFromType (argTypes[i]));
138 objFileWriter.Write (argNames[i]);
139 }
140 }
141
142 /**
143 * @brief Begin outputting object code for the function
144 */
145 public void BegMethod ()
146 {
147 /*
148 * This tells the reader to call methodInfo.GetILGenerator()
149 * so it can start writing CIL code for the method.
150 */
151 objFileWriter.Write ((byte)ScriptObjWriterCode.BegMethod);
152 objFileWriter.Write (methName);
153 }
154
155 /**
156 * @brief End of object code for the function
157 */
158 public void EndMethod ()
159 {
160 /*
161 * This tells the reader that all code for the method has
162 * been written and so it will typically call CreateDelegate()
163 * to finalize the method and create an entrypoint.
164 */
165 objFileWriter.Write ((byte)ScriptObjWriterCode.EndMethod);
166
167 objFileWriter = null;
168 }
169
170 /**
171 * @brief Declare a local variable for use by the function
172 */
173 public ScriptMyLocal DeclareLocal (Type type, string name)
174 {
175 ScriptMyLocal myLocal = new ScriptMyLocal ();
176 myLocal.type = type;
177 myLocal.name = name;
178 myLocal.number = localNumber ++;
179 myLocal.isReferenced = true; // so ScriptCollector won't optimize references away
180 return DeclareLocal (myLocal);
181 }
182 public ScriptMyLocal DeclareLocal (ScriptMyLocal myLocal)
183 {
184 objFileWriter.Write ((byte)ScriptObjWriterCode.DclLocal);
185 objFileWriter.Write (myLocal.number);
186 objFileWriter.Write (myLocal.name);
187 objFileWriter.Write (GetStrFromType (myLocal.type));
188 return myLocal;
189 }
190
191 /**
192 * @brief Define a label for use by the function
193 */
194 public ScriptMyLabel DefineLabel (string name)
195 {
196 ScriptMyLabel myLabel = new ScriptMyLabel ();
197 myLabel.name = name;
198 myLabel.number = labelNumber ++;
199 return DefineLabel (myLabel);
200 }
201 public ScriptMyLabel DefineLabel (ScriptMyLabel myLabel)
202 {
203 objFileWriter.Write ((byte)ScriptObjWriterCode.DclLabel);
204 objFileWriter.Write (myLabel.number);
205 objFileWriter.Write (myLabel.name);
206 return myLabel;
207 }
208
209 /**
210 * @brief try/catch blocks.
211 */
212 public void BeginExceptionBlock ()
213 {
214 objFileWriter.Write ((byte)ScriptObjWriterCode.BegExcBlk);
215 }
216
217 public void BeginCatchBlock (Type excType)
218 {
219 objFileWriter.Write ((byte)ScriptObjWriterCode.BegCatBlk);
220 objFileWriter.Write (GetStrFromType (excType));
221 }
222
223 public void BeginFinallyBlock ()
224 {
225 objFileWriter.Write ((byte)ScriptObjWriterCode.BegFinBlk);
226 }
227
228 public void EndExceptionBlock ()
229 {
230 objFileWriter.Write ((byte)ScriptObjWriterCode.EndExcBlk);
231 }
232
233 public void Emit (Token errorAt, OpCode opcode)
234 {
235 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitNull);
236 WriteOpCode (errorAt, opcode);
237 }
238
239 public void Emit (Token errorAt, OpCode opcode, FieldInfo field)
240 {
241 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitField);
242 WriteOpCode (errorAt, opcode);
243 objFileWriter.Write (GetStrFromType (field.ReflectedType));
244 objFileWriter.Write (field.Name);
245 }
246
247 public void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal)
248 {
249 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitLocal);
250 WriteOpCode (errorAt, opcode);
251 objFileWriter.Write (myLocal.number);
252 }
253
254 public void Emit (Token errorAt, OpCode opcode, Type type)
255 {
256 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitType);
257 WriteOpCode (errorAt, opcode);
258 objFileWriter.Write (GetStrFromType (type));
259 }
260
261 public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel)
262 {
263 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitLabel);
264 WriteOpCode (errorAt, opcode);
265 objFileWriter.Write (myLabel.number);
266 }
267
268 public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels)
269 {
270 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitLabels);
271 WriteOpCode (errorAt, opcode);
272 int nLabels = myLabels.Length;
273 objFileWriter.Write (nLabels);
274 for (int i = 0; i < nLabels; i ++) {
275 objFileWriter.Write (myLabels[i].number);
276 }
277 }
278
279 public void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method)
280 {
281 if (method == null) throw new ArgumentNullException ("method");
282 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitMethodInt);
283 WriteOpCode (errorAt, opcode);
284 objFileWriter.Write (method.methName);
285 }
286
287 public void Emit (Token errorAt, OpCode opcode, MethodInfo method)
288 {
289 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitMethodExt);
290 WriteOpCode (errorAt, opcode);
291 objFileWriter.Write (method.Name);
292 objFileWriter.Write (GetStrFromType (method.ReflectedType));
293 ParameterInfo[] parms = method.GetParameters ();
294 int nArgs = parms.Length;
295 objFileWriter.Write (nArgs);
296 for (int i = 0; i < nArgs; i ++) {
297 objFileWriter.Write (GetStrFromType (parms[i].ParameterType));
298 }
299 }
300
301 public void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor)
302 {
303 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitCtor);
304 WriteOpCode (errorAt, opcode);
305 objFileWriter.Write (GetStrFromType (ctor.ReflectedType));
306 ParameterInfo[] parms = ctor.GetParameters ();
307 int nArgs = parms.Length;
308 objFileWriter.Write (nArgs);
309 for (int i = 0; i < nArgs; i ++) {
310 objFileWriter.Write (GetStrFromType (parms[i].ParameterType));
311 }
312 }
313
314 public void Emit (Token errorAt, OpCode opcode, double value)
315 {
316 if (opcode != OpCodes.Ldc_R8) {
317 throw new Exception ("bad opcode " + opcode.ToString ());
318 }
319 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitDouble);
320 WriteOpCode (errorAt, opcode);
321 objFileWriter.Write (value);
322 }
323
324 public void Emit (Token errorAt, OpCode opcode, float value)
325 {
326 if (opcode != OpCodes.Ldc_R4) {
327 throw new Exception ("bad opcode " + opcode.ToString ());
328 }
329 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitFloat);
330 WriteOpCode (errorAt, opcode);
331 objFileWriter.Write (value);
332 }
333
334 public void Emit (Token errorAt, OpCode opcode, int value)
335 {
336 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitInteger);
337 WriteOpCode (errorAt, opcode);
338 objFileWriter.Write (value);
339 }
340
341 public void Emit (Token errorAt, OpCode opcode, string value)
342 {
343 objFileWriter.Write ((byte)ScriptObjWriterCode.EmitString);
344 WriteOpCode (errorAt, opcode);
345 objFileWriter.Write (value);
346 }
347
348 /**
349 * @brief Declare that the target of a label is the next instruction.
350 */
351 public void MarkLabel (ScriptMyLabel myLabel)
352 {
353 objFileWriter.Write ((byte)ScriptObjWriterCode.MarkLabel);
354 objFileWriter.Write (myLabel.number);
355 }
356
357 /**
358 * @brief Write end-of-file marker to binary file.
359 */
360 public static void TheEnd (BinaryWriter objFileWriter)
361 {
362 objFileWriter.Write ((byte)ScriptObjWriterCode.TheEnd);
363 }
364
365 /**
366 * @brief Take an object file created by ScriptObjWriter() and convert it to a series of dynamic methods.
367 * @param sdTypes = script-defined types
368 * @param objReader = where to read object file from (as written by ScriptObjWriter above).
369 * @param scriptObjCode.EndMethod = called for each method defined at the end of the methods definition
370 * @param objectTokens = write disassemble/decompile data (or null if not wanted)
371 */
372 public static void CreateObjCode (Dictionary<string, TokenDeclSDType> sdTypes, BinaryReader objReader,
373 ScriptObjCode scriptObjCode, ObjectTokens objectTokens)
374 {
375 Dictionary<string, DynamicMethod> methods = new Dictionary<string, DynamicMethod> ();
376 DynamicMethod method = null;
377 ILGenerator ilGen = null;
378 Dictionary<int, Label> labels = new Dictionary<int, Label> ();
379 Dictionary<int, LocalBuilder> locals = new Dictionary<int, LocalBuilder> ();
380 Dictionary<int, string> labelNames = new Dictionary<int, string> ();
381 Dictionary<int, string> localNames = new Dictionary<int, string> ();
382 object[] ilGenArg = new object[1];
383 int offset = 0;
384 Dictionary<int, ScriptSrcLoc> srcLocs = null;
385 string srcFile = "";
386 int srcLine = 0;
387 int srcPosn = 0;
388
389 while (true) {
390
391 /*
392 * Get IL instruction offset at beginning of instruction.
393 */
394 offset = 0;
395 if ((ilGen != null) && (monoGetCurrentOffset != null)) {
396 offset = (int)monoGetCurrentOffset.Invoke (null, ilGenArg);
397 }
398
399 /*
400 * Read and decode next internal format code from input file (.xmrobj file).
401 */
402 ScriptObjWriterCode code = (ScriptObjWriterCode)objReader.ReadByte ();
403 switch (code) {
404
405 /*
406 * Reached end-of-file so we are all done.
407 */
408 case ScriptObjWriterCode.TheEnd: {
409 return;
410 }
411
412 /*
413 * Beginning of method's contents.
414 * Method must have already been declared via DclMethod
415 * so all we need is its name to retrieve from methods[].
416 */
417 case ScriptObjWriterCode.BegMethod: {
418 string methName = objReader.ReadString ();
419
420 method = methods[methName];
421 ilGen = method.GetILGenerator ();
422 ilGenArg[0] = ilGen;
423
424 labels.Clear ();
425 locals.Clear ();
426 labelNames.Clear ();
427 localNames.Clear ();
428
429 srcLocs = new Dictionary<int, ScriptSrcLoc> ();
430 if (objectTokens != null) objectTokens.BegMethod (method);
431 break;
432 }
433
434 /*
435 * End of method's contents (ie, an OpCodes.Ret was probably just output).
436 * Call the callback to tell it the method is complete, and it can do whatever
437 * it wants with the method.
438 */
439 case ScriptObjWriterCode.EndMethod: {
440 ilGen = null;
441 ilGenArg[0] = null;
442 scriptObjCode.EndMethod (method, srcLocs);
443 srcLocs = null;
444 if (objectTokens != null) objectTokens.EndMethod ();
445 break;
446 }
447
448 /*
449 * Declare a label for branching to.
450 */
451 case ScriptObjWriterCode.DclLabel: {
452 int number = objReader.ReadInt32 ();
453 string name = objReader.ReadString ();
454
455 labels.Add (number, ilGen.DefineLabel ());
456 labelNames.Add (number, name + "_" + number.ToString ());
457 if (objectTokens != null) objectTokens.DefineLabel (number, name);
458 break;
459 }
460
461 /*
462 * Declare a local variable to store into.
463 */
464 case ScriptObjWriterCode.DclLocal: {
465 int number = objReader.ReadInt32 ();
466 string name = objReader.ReadString ();
467 string type = objReader.ReadString ();
468 Type syType = GetTypeFromStr (sdTypes, type);
469
470 locals.Add (number, ilGen.DeclareLocal (syType));
471 localNames.Add (number, name + "_" + number.ToString ());
472 if (objectTokens != null) objectTokens.DefineLocal (number, name, type, syType);
473 break;
474 }
475
476 /*
477 * Declare a method that will subsequently be defined.
478 * We create the DynamicMethod object at this point in case there
479 * are forward references from other method bodies.
480 */
481 case ScriptObjWriterCode.DclMethod: {
482 string methName = objReader.ReadString ();
483 Type retType = GetTypeFromStr (sdTypes, objReader.ReadString ());
484 int nArgs = objReader.ReadInt32 ();
485
486 Type[] argTypes = new Type[nArgs];
487 string[] argNames = new string[nArgs];
488 for (int i = 0; i < nArgs; i ++) {
489 argTypes[i] = GetTypeFromStr (sdTypes, objReader.ReadString ());
490 argNames[i] = objReader.ReadString ();
491 }
492 methods.Add (methName, new DynamicMethod (methName, retType, argTypes));
493 if (objectTokens != null) objectTokens.DefineMethod (methName, retType, argTypes, argNames);
494 break;
495 }
496
497 /*
498 * Mark a previously declared label at this spot.
499 */
500 case ScriptObjWriterCode.MarkLabel: {
501 int number = objReader.ReadInt32 ();
502
503 ilGen.MarkLabel (labels[number]);
504
505 if (objectTokens != null) objectTokens.MarkLabel (offset, number);
506 break;
507 }
508
509 /*
510 * Try/Catch blocks.
511 */
512 case ScriptObjWriterCode.BegExcBlk: {
513 ilGen.BeginExceptionBlock ();
514 if (objectTokens != null) objectTokens.BegExcBlk (offset);
515 break;
516 }
517
518 case ScriptObjWriterCode.BegCatBlk: {
519 Type excType = GetTypeFromStr (sdTypes, objReader.ReadString ());
520 ilGen.BeginCatchBlock (excType);
521 if (objectTokens != null) objectTokens.BegCatBlk (offset, excType);
522 break;
523 }
524
525 case ScriptObjWriterCode.BegFinBlk: {
526 ilGen.BeginFinallyBlock ();
527 if (objectTokens != null) objectTokens.BegFinBlk (offset);
528 break;
529 }
530
531 case ScriptObjWriterCode.EndExcBlk: {
532 ilGen.EndExceptionBlock ();
533 if (objectTokens != null) objectTokens.EndExcBlk (offset);
534 break;
535 }
536
537 /*
538 * Emit an opcode with no operand.
539 */
540 case ScriptObjWriterCode.EmitNull: {
541 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
542
543 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
544 ilGen.Emit (opCode);
545
546 if (objectTokens != null) objectTokens.EmitNull (offset, opCode);
547 break;
548 }
549
550 /*
551 * Emit an opcode with a FieldInfo operand.
552 */
553 case ScriptObjWriterCode.EmitField: {
554 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
555 Type reflectedType = GetTypeFromStr (sdTypes, objReader.ReadString ());
556 string fieldName = objReader.ReadString ();
557
558 FieldInfo field = reflectedType.GetField (fieldName);
559 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
560 ilGen.Emit (opCode, field);
561
562 if (objectTokens != null) objectTokens.EmitField (offset, opCode, field);
563 break;
564 }
565
566 /*
567 * Emit an opcode with a LocalBuilder operand.
568 */
569 case ScriptObjWriterCode.EmitLocal: {
570 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
571 int number = objReader.ReadInt32 ();
572 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
573 ilGen.Emit (opCode, locals[number]);
574
575 if (objectTokens != null) objectTokens.EmitLocal (offset, opCode, number);
576 break;
577 }
578
579 /*
580 * Emit an opcode with a Type operand.
581 */
582 case ScriptObjWriterCode.EmitType: {
583 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
584 string name = objReader.ReadString ();
585 Type type = GetTypeFromStr (sdTypes, name);
586
587 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
588 ilGen.Emit (opCode, type);
589
590 if (objectTokens != null) objectTokens.EmitType (offset, opCode, type);
591 break;
592 }
593
594 /*
595 * Emit an opcode with a Label operand.
596 */
597 case ScriptObjWriterCode.EmitLabel: {
598 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
599 int number = objReader.ReadInt32 ();
600
601 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
602 ilGen.Emit (opCode, labels[number]);
603
604 if (objectTokens != null) objectTokens.EmitLabel (offset, opCode, number);
605 break;
606 }
607
608 /*
609 * Emit an opcode with a Label array operand.
610 */
611 case ScriptObjWriterCode.EmitLabels: {
612 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
613 int nLabels = objReader.ReadInt32 ();
614 Label[] lbls = new Label[nLabels];
615 int[] nums = new int[nLabels];
616 for (int i = 0; i < nLabels; i ++) {
617 nums[i] = objReader.ReadInt32 ();
618 lbls[i] = labels[nums[i]];
619 }
620
621 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
622 ilGen.Emit (opCode, lbls);
623
624 if (objectTokens != null) objectTokens.EmitLabels (offset, opCode, nums);
625 break;
626 }
627
628 /*
629 * Emit an opcode with a MethodInfo operand (such as a call) of an external function.
630 */
631 case ScriptObjWriterCode.EmitMethodExt: {
632 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
633 string methName = objReader.ReadString ();
634 Type methType = GetTypeFromStr (sdTypes, objReader.ReadString ());
635 int nArgs = objReader.ReadInt32 ();
636
637 Type[] argTypes = new Type[nArgs];
638 for (int i = 0; i < nArgs; i ++) {
639 argTypes[i] = GetTypeFromStr (sdTypes, objReader.ReadString ());
640 }
641 MethodInfo methInfo = methType.GetMethod (methName, argTypes);
642 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
643 ilGen.Emit (opCode, methInfo);
644
645 if (objectTokens != null) objectTokens.EmitMethod (offset, opCode, methInfo);
646 break;
647 }
648
649 /*
650 * Emit an opcode with a MethodInfo operand of an internal function
651 * (previously declared via DclMethod).
652 */
653 case ScriptObjWriterCode.EmitMethodInt: {
654 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
655 string methName = objReader.ReadString ();
656
657 MethodInfo methInfo = methods[methName];
658 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
659 ilGen.Emit (opCode, methInfo);
660
661 if (objectTokens != null) objectTokens.EmitMethod (offset, opCode, methInfo);
662 break;
663 }
664
665 /*
666 * Emit an opcode with a ConstructorInfo operand.
667 */
668 case ScriptObjWriterCode.EmitCtor: {
669 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
670 Type ctorType = GetTypeFromStr (sdTypes, objReader.ReadString ());
671 int nArgs = objReader.ReadInt32 ();
672 Type[] argTypes = new Type[nArgs];
673 for (int i = 0; i < nArgs; i ++) {
674 argTypes[i] = GetTypeFromStr (sdTypes, objReader.ReadString ());
675 }
676
677 ConstructorInfo ctorInfo = ctorType.GetConstructor (argTypes);
678 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
679 ilGen.Emit (opCode, ctorInfo);
680
681 if (objectTokens != null) objectTokens.EmitCtor (offset, opCode, ctorInfo);
682 break;
683 }
684
685 /*
686 * Emit an opcode with a constant operand of various types.
687 */
688 case ScriptObjWriterCode.EmitDouble: {
689 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
690 double value = objReader.ReadDouble ();
691
692 if (opCode != OpCodes.Ldc_R8) {
693 throw new Exception ("bad opcode " + opCode.ToString ());
694 }
695 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
696 ilGen.Emit (opCode, value);
697
698 if (objectTokens != null) objectTokens.EmitDouble (offset, opCode, value);
699 break;
700 }
701
702 case ScriptObjWriterCode.EmitFloat: {
703 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
704 float value = objReader.ReadSingle ();
705
706 if (opCode != OpCodes.Ldc_R4) {
707 throw new Exception ("bad opcode " + opCode.ToString ());
708 }
709 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
710 ilGen.Emit (opCode, value);
711
712 if (objectTokens != null) objectTokens.EmitFloat (offset, opCode, value);
713 break;
714 }
715
716 case ScriptObjWriterCode.EmitInteger: {
717 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
718 int value = objReader.ReadInt32 ();
719
720 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
721
722 if (opCode == OpCodes.Ldc_I4) {
723 if ((value >= -1) && (value <= 8)) {
724 opCode = opCodesLdcI4M1P8[value+1];
725 ilGen.Emit (opCode);
726 if (objectTokens != null) objectTokens.EmitNull (offset, opCode);
727 break;
728 }
729 if ((value >= 0) && (value <= 127)) {
730 opCode = OpCodes.Ldc_I4_S;
731 ilGen.Emit (OpCodes.Ldc_I4_S, (sbyte)value);
732 goto pemitint;
733 }
734 }
735
736 ilGen.Emit (opCode, value);
737 pemitint:
738 if (objectTokens != null) objectTokens.EmitInteger (offset, opCode, value);
739 break;
740 }
741
742 case ScriptObjWriterCode.EmitString: {
743 OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
744 string value = objReader.ReadString ();
745
746 SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
747 ilGen.Emit (opCode, value);
748
749 if (objectTokens != null) objectTokens.EmitString (offset, opCode, value);
750 break;
751 }
752
753 /*
754 * Who knows what?
755 */
756 default: throw new Exception ("bad ScriptObjWriterCode " + ((byte)code).ToString ());
757 }
758 }
759 }
760
761 /**
762 * @brief Generate array to quickly translate OpCode.Value to full OpCode struct.
763 */
764 private static Dictionary<short, OpCode> PopulateOpCodes ()
765 {
766 Dictionary<short, OpCode> opCodeDict = new Dictionary<short, OpCode> ();
767 FieldInfo[] fields = typeof (OpCodes).GetFields ();
768 for (int i = 0; i < fields.Length; i ++) {
769 OpCode opcode = (OpCode)fields[i].GetValue (null);
770 opCodeDict.Add (opcode.Value, opcode);
771 }
772 return opCodeDict;
773 }
774
775 /**
776 * @brief Write opcode out to file.
777 */
778 private void WriteOpCode (Token errorAt, OpCode opcode)
779 {
780 if (errorAt == null) {
781 objFileWriter.Write ("");
782 objFileWriter.Write (lastErrorAtLine);
783 objFileWriter.Write (lastErrorAtPosn);
784 } else {
785 if (errorAt.file != lastErrorAtFile) {
786 objFileWriter.Write (errorAt.file);
787 lastErrorAtFile = errorAt.file;
788 } else {
789 objFileWriter.Write ("");
790 }
791 objFileWriter.Write (errorAt.line);
792 objFileWriter.Write (errorAt.posn);
793 lastErrorAtLine = errorAt.line;
794 lastErrorAtPosn = errorAt.posn;
795 }
796 objFileWriter.Write (opcode.Value);
797 }
798
799 /**
800 * @brief Read opcode in from file.
801 */
802 private static OpCode ReadOpCode (BinaryReader objReader, ref string srcFile, ref int srcLine, ref int srcPosn)
803 {
804 string f = objReader.ReadString ();
805 if (f != "") srcFile = f;
806 srcLine = objReader.ReadInt32 ();
807 srcPosn = objReader.ReadInt32 ();
808
809 short value = objReader.ReadInt16 ();
810 return opCodes[value];
811 }
812
813 /**
814 * @brief Save an IL_offset -> source location translation entry
815 * @param srcLocs = saved entries for the current function
816 * @param offset = offset in IL object code for next instruction
817 * @param src{File,Line,Posn} = location in source file corresponding to opcode
818 * @returns with entry added to srcLocs
819 */
820 private static void SaveSrcLoc (Dictionary<int, ScriptSrcLoc> srcLocs, int offset, string srcFile, int srcLine, int srcPosn)
821 {
822 ScriptSrcLoc srcLoc = new ScriptSrcLoc ();
823 srcLoc.file = srcFile;
824 srcLoc.line = srcLine;
825 srcLoc.posn = srcPosn;
826 srcLocs[offset] = srcLoc;
827 }
828
829 /**
830 * @brief Create type<->string conversions.
831 * Using Type.AssemblyQualifiedName is horribly inefficient
832 * and all our types should be known.
833 */
834 private static Dictionary<string, Type> PopulateS2T ()
835 {
836 Dictionary<string, Type> s2t = new Dictionary<string, Type> ();
837
838 s2t.Add ("badcallx", typeof (ScriptBadCallNoException));
839 s2t.Add ("binopstr", typeof (BinOpStr));
840 s2t.Add ("bool", typeof (bool));
841 s2t.Add ("char", typeof (char));
842 s2t.Add ("delegate", typeof (Delegate));
843 s2t.Add ("delarr[]", typeof (Delegate[]));
844 s2t.Add ("double", typeof (double));
845 s2t.Add ("exceptn", typeof (Exception));
846 s2t.Add ("float", typeof (float));
847 s2t.Add ("htlist", typeof (HeapTrackerList));
848 s2t.Add ("htobject", typeof (HeapTrackerObject));
849 s2t.Add ("htstring", typeof (HeapTrackerString));
850 s2t.Add ("inlfunc", typeof (CompValuInline));
851 s2t.Add ("int", typeof (int));
852 s2t.Add ("int*", typeof (int).MakeByRefType ());
853 s2t.Add ("intrlokd", typeof (System.Threading.Interlocked));
854 s2t.Add ("lslfloat", typeof (LSL_Float));
855 s2t.Add ("lslint", typeof (LSL_Integer));
856 s2t.Add ("lsllist", typeof (LSL_List));
857 s2t.Add ("lslrot", typeof (LSL_Rotation));
858 s2t.Add ("lslstr", typeof (LSL_String));
859 s2t.Add ("lslvec", typeof (LSL_Vector));
860 s2t.Add ("math", typeof (Math));
861 s2t.Add ("midround", typeof (MidpointRounding));
862 s2t.Add ("object", typeof (object));
863 s2t.Add ("object*", typeof (object).MakeByRefType ());
864 s2t.Add ("object[]", typeof (object[]));
865 s2t.Add ("scrbase", typeof (ScriptBaseClass));
866 s2t.Add ("scrcode", typeof (ScriptCodeGen));
867 s2t.Add ("sdtclobj", typeof (XMRSDTypeClObj));
868 s2t.Add ("string", typeof (string));
869 s2t.Add ("typecast", typeof (TypeCast));
870 s2t.Add ("undstatx", typeof (ScriptUndefinedStateException));
871 s2t.Add ("void", typeof (void));
872 s2t.Add ("xmrarray", typeof (XMR_Array));
873 s2t.Add ("xmrinst", typeof (XMRInstAbstract));
874
875 return s2t;
876 }
877
878 private static Dictionary<Type, string> PopulateT2S ()
879 {
880 Dictionary<string, Type> s2t = PopulateS2T ();
881 Dictionary<Type, string> t2s = new Dictionary<Type, string> ();
882 foreach (KeyValuePair<string, Type> kvp in s2t) {
883 t2s.Add (kvp.Value, kvp.Key);
884 }
885 return t2s;
886 }
887
888 /**
889 * @brief Add to list of internally recognized types.
890 */
891 public static void DefineInternalType (string name, Type type)
892 {
893 if (!string2Type.ContainsKey(name))
894 {
895 string2Type.Add (name, type);
896 type2String.Add (type, name);
897 }
898 }
899
900 private string GetStrFromType (Type t)
901 {
902 string s = GetStrFromTypeWork (t);
903 return s;
904 }
905 private string GetStrFromTypeWork (Type t)
906 {
907 string s;
908
909 // internal fixed types like int and xmrarray etc
910 if (type2String.TryGetValue (t, out s)) return s;
911
912 // script-defined types
913 if (sdTypesRev.TryGetValue (t, out s)) return "sdt$" + s;
914
915 // inline function types
916 s = TokenDeclSDTypeDelegate.TryGetInlineName (t);
917 if (s != null) return s;
918
919 // last resort
920 return t.AssemblyQualifiedName;
921 }
922
923 private static Type GetTypeFromStr (Dictionary<string, TokenDeclSDType> sdTypes, string s)
924 {
925 Type t;
926
927 // internal fixed types like int and xmrarray etc
928 if (string2Type.TryGetValue (s, out t)) return t;
929
930 // script-defined types
931 if (s.StartsWith ("sdt$")) return sdTypes[s.Substring(4)].GetSysType ();
932
933 // inline function types
934 t = TokenDeclSDTypeDelegate.TryGetInlineSysType (s);
935 if (t != null) return t;
936
937 // last resort
938 return Type.GetType (s, true);
939 }
940 }
941
942 public class ScriptSrcLoc {
943 public string file;
944 public int line;
945 public int posn;
946 }
947}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs
new file mode 100644
index 0000000..a8af740
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs
@@ -0,0 +1,7719 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/**
29 * @brief Reduce parser tokens to abstract syntax tree tokens.
30 *
31 * Usage:
32 *
33 * tokenBegin = returned by TokenBegin.Analyze ()
34 * representing the whole script source
35 * as a flat list of tokens
36 *
37 * TokenScript tokenScript = Reduce.Analyze (TokenBegin tokenBegin);
38 *
39 * tokenScript = represents the whole script source
40 * as a tree of tokens
41 */
42
43using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
44using System;
45using System.Collections.Generic;
46using System.IO;
47using System.Reflection;
48using System.Reflection.Emit;
49using System.Text;
50
51using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
52using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
53using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
54using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
55using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
56using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
57using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
58
59namespace OpenSim.Region.ScriptEngine.XMREngine {
60
61 public class ScriptReduce {
62 public const uint SDT_PRIVATE = 1;
63 public const uint SDT_PROTECTED = 2;
64 public const uint SDT_PUBLIC = 4;
65 public const uint SDT_ABSTRACT = 8;
66 public const uint SDT_FINAL = 16;
67 public const uint SDT_NEW = 32;
68 public const uint SDT_OVERRIDE = 64;
69 public const uint SDT_STATIC = 128;
70 public const uint SDT_VIRTUAL = 256;
71
72 private const int ASNPR = 50;
73
74 private static Dictionary<Type, int> precedence = PrecedenceInit ();
75
76 private static readonly Type[] brkCloseOnly = new Type[] { typeof (TokenKwBrkClose) };
77 private static readonly Type[] cmpGTOnly = new Type[] { typeof (TokenKwCmpGT) };
78 private static readonly Type[] colonOnly = new Type[] { typeof (TokenKwColon) };
79 private static readonly Type[] commaOrBrcClose = new Type[] { typeof (TokenKwComma), typeof (TokenKwBrcClose) };
80 private static readonly Type[] colonOrDotDotDot = new Type[] { typeof (TokenKwColon), typeof (TokenKwDotDotDot) };
81 private static readonly Type[] parCloseOnly = new Type[] { typeof (TokenKwParClose) };
82 private static readonly Type[] semiOnly = new Type[] { typeof (TokenKwSemi) };
83
84 /**
85 * @brief Initialize operator precedence table
86 * @returns with precedence table pointer
87 */
88 private static Dictionary<Type, int> PrecedenceInit ()
89 {
90 Dictionary<Type, int> p = new Dictionary<Type, int> ();
91
92 // http://www.lslwiki.net/lslwiki/wakka.php?wakka=operators
93
94 p.Add (typeof (TokenKwComma), 30);
95
96 p.Add (typeof (TokenKwAsnLSh), ASNPR); // all assignment operators of equal precedence
97 p.Add (typeof (TokenKwAsnRSh), ASNPR); // ... so they get processed strictly right-to-left
98 p.Add (typeof (TokenKwAsnAdd), ASNPR);
99 p.Add (typeof (TokenKwAsnAnd), ASNPR);
100 p.Add (typeof (TokenKwAsnSub), ASNPR);
101 p.Add (typeof (TokenKwAsnMul), ASNPR);
102 p.Add (typeof (TokenKwAsnDiv), ASNPR);
103 p.Add (typeof (TokenKwAsnMod), ASNPR);
104 p.Add (typeof (TokenKwAsnOr), ASNPR);
105 p.Add (typeof (TokenKwAsnXor), ASNPR);
106 p.Add (typeof (TokenKwAssign), ASNPR);
107
108 p.Add (typeof (TokenKwQMark), 60);
109
110 p.Add (typeof (TokenKwOrOrOr), 70);
111 p.Add (typeof (TokenKwAndAndAnd), 80);
112
113 p.Add (typeof (TokenKwOrOr), 100);
114
115 p.Add (typeof (TokenKwAndAnd), 120);
116
117 p.Add (typeof (TokenKwOr), 140);
118
119 p.Add (typeof (TokenKwXor), 160);
120
121 p.Add (typeof (TokenKwAnd), 180);
122
123 p.Add (typeof (TokenKwCmpEQ), 200);
124 p.Add (typeof (TokenKwCmpNE), 200);
125
126 p.Add (typeof (TokenKwCmpLT), 240);
127 p.Add (typeof (TokenKwCmpLE), 240);
128 p.Add (typeof (TokenKwCmpGT), 240);
129 p.Add (typeof (TokenKwCmpGE), 240);
130
131 p.Add (typeof (TokenKwRSh), 260);
132 p.Add (typeof (TokenKwLSh), 260);
133
134 p.Add (typeof (TokenKwAdd), 280);
135 p.Add (typeof (TokenKwSub), 280);
136
137 p.Add (typeof (TokenKwMul), 320);
138 p.Add (typeof (TokenKwDiv), 320);
139 p.Add (typeof (TokenKwMod), 320);
140
141 return p;
142 }
143
144 /**
145 * @brief Reduce raw token stream to a single script token.
146 * Performs a little semantic testing, ie, undefined variables, etc.
147 * @param tokenBegin = points to a TokenBegin
148 * followed by raw tokens
149 * and last token is a TokenEnd
150 * @returns null: not a valid script, error messages have been output
151 * else: valid script top token
152 */
153 public static TokenScript Reduce (TokenBegin tokenBegin)
154 {
155 return new ScriptReduce (tokenBegin).tokenScript;
156 }
157
158 /*
159 * Instance variables.
160 */
161 private bool errors = false;
162 private string lastErrorFile = "";
163 private int lastErrorLine = 0;
164 private int numTypedefs = 0;
165 private TokenDeclVar currentDeclFunc = null;
166 private TokenDeclSDType currentDeclSDType = null;
167 private TokenScript tokenScript;
168 private TokenStmtBlock currentStmtBlock = null;
169
170 /**
171 * @brief the constructor does all the processing.
172 * @param token = first token of script after the TokenBegin token
173 * @returns tokenScript = null: there were errors
174 * else: successful
175 */
176 private ScriptReduce (TokenBegin tokenBegin)
177 {
178 /*
179 * Create a place to put the top-level script components,
180 * eg, state bodies, functions, global variables.
181 */
182 tokenScript = new TokenScript (tokenBegin.nextToken);
183 tokenScript.expiryDays = tokenBegin.expiryDays;
184
185 /*
186 * 'class', 'delegate', 'instance' all define types.
187 * So we pre-scan the source tokens for those keywords
188 * to build a script-defined type table and substitute
189 * type tokens for those names in the source. This is
190 * done as a separate scan so they can cross-reference
191 * each other. Also does likewise for fixed array types.
192 *
193 * Also, all 'typedef's are processed here. Their definitions
194 * remain in the source token stream after this, but they can
195 * be skipped over, because their bodies have been substituted
196 * in the source for any references.
197 */
198 ParseSDTypePreScanPassOne (tokenBegin); // catalog definitions
199 ParseSDTypePreScanPassTwo (tokenBegin); // substitute references
200
201 /*
202 int braces = 0;
203 Token prevTok = null;
204 for (Token token = tokenBegin; token != null; token = token.nextToken) {
205 if (token is TokenKwParClose) braces -= 2;
206 if (token is TokenKwBrcClose) braces -= 4;
207 StringBuilder sb = new StringBuilder ("ScriptReduce*: ");
208 sb.Append (token.GetHashCode ().ToString ("X8"));
209 sb.Append (" ");
210 sb.Append (token.line.ToString ().PadLeft (3));
211 sb.Append (".");
212 sb.Append (token.posn.ToString ().PadLeft (3));
213 sb.Append (" ");
214 sb.Append (token.GetType ().Name.PadRight (24));
215 sb.Append (" : ");
216 for (int i = 0; i < braces; i ++) sb.Append (' ');
217 token.DebString (sb);
218 Console.WriteLine (sb.ToString ());
219 if (token.prevToken != prevTok) {
220 Console.WriteLine ("ScriptReduce*: -- prevToken link bad => " + token.prevToken.GetHashCode ().ToString ("X8"));
221 }
222 if (token is TokenKwBrcOpen) braces += 4;
223 if (token is TokenKwParOpen) braces += 2;
224 prevTok = token;
225 }
226 */
227
228 /*
229 * Create a function $globalvarinit to hold all explicit
230 * global variable initializations.
231 */
232 TokenDeclVar gviFunc = new TokenDeclVar (tokenBegin, null, tokenScript);
233 gviFunc.name = new TokenName (gviFunc, "$globalvarinit");
234 gviFunc.retType = new TokenTypeVoid (gviFunc);
235 gviFunc.argDecl = new TokenArgDecl (gviFunc);
236 TokenStmtBlock gviBody = new TokenStmtBlock (gviFunc);
237 gviBody.function = gviFunc;
238 gviFunc.body = gviBody;
239 tokenScript.globalVarInit = gviFunc;
240 tokenScript.AddVarEntry (gviFunc);
241
242 /*
243 * Scan through the tokens until we reach the end.
244 */
245 for (Token token = tokenBegin.nextToken; !(token is TokenEnd);) {
246 if (token is TokenKwSemi) {
247 token = token.nextToken;
248 continue;
249 }
250
251 /*
252 * Script-defined type declarations.
253 */
254 if (ParseDeclSDTypes (ref token, null, SDT_PUBLIC)) continue;
255
256 /*
257 * constant <name> = <rval> ;
258 */
259 if (token is TokenKwConst) {
260 ParseDeclVar (ref token, null);
261 continue;
262 }
263
264 /*
265 * <type> <name> ;
266 * <type> <name> = <rval> ;
267 */
268 if ((token is TokenType) &&
269 (token.nextToken is TokenName) &&
270 ((token.nextToken.nextToken is TokenKwSemi) ||
271 (token.nextToken.nextToken is TokenKwAssign))) {
272 TokenDeclVar var = ParseDeclVar (ref token, gviFunc);
273 if (var != null) {
274 // <name> = <init>;
275 TokenLValName left = new TokenLValName (var.name, tokenScript.variablesStack);
276 DoVarInit (gviFunc, left, var.init);
277 }
278 continue;
279 }
280
281 /*
282 * <type> <name> { [ get { <body> } ] [ set { <body> } ] }
283 */
284 if ((token is TokenType) &&
285 (token.nextToken is TokenName) &&
286 (token.nextToken.nextToken is TokenKwBrcOpen)) {
287 ParseProperty (ref token, false, true);
288 continue;
289 }
290
291 /*
292 * <type> <name> <funcargs> <funcbody>
293 * global function returning specified type
294 */
295 if (token is TokenType) {
296 TokenType tokenType = (TokenType)token;
297
298 token = token.nextToken;
299 if (!(token is TokenName)) {
300 ErrorMsg (token, "expecting variable/function name");
301 token = SkipPastSemi (token);
302 continue;
303 }
304 TokenName tokenName = (TokenName)token;
305 token = token.nextToken;
306 if (!(token is TokenKwParOpen)) {
307 ErrorMsg (token, "<type> <name> must be followed by ; = or (");
308 token = SkipPastSemi (token);
309 continue;
310 }
311 token = tokenType;
312 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, false, false, false);
313 if (tokenDeclFunc == null) continue;
314 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
315 ErrorMsg (tokenName, "duplicate function " + tokenDeclFunc.funcNameSig.val);
316 }
317 continue;
318 }
319
320 /*
321 * <name> <funcargs> <funcbody>
322 * global function returning void
323 */
324 if (token is TokenName) {
325 TokenName tokenName = (TokenName)token;
326 token = token.nextToken;
327 if (!(token is TokenKwParOpen)) {
328 ErrorMsg (token, "looking for open paren after assuming " +
329 tokenName.val + " is a function name");
330 token = SkipPastSemi (token);
331 continue;
332 }
333 token = tokenName;
334 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, false, false, false);
335 if (tokenDeclFunc == null) continue;
336 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
337 ErrorMsg (tokenName, "duplicate function " + tokenDeclFunc.funcNameSig.val);
338 }
339 continue;
340 }
341
342 /*
343 * default <statebody>
344 */
345 if (token is TokenKwDefault) {
346 TokenDeclState tokenDeclState = new TokenDeclState (token);
347 token = token.nextToken;
348 tokenDeclState.body = ParseStateBody (ref token);
349 if (tokenDeclState.body == null) continue;
350 if (tokenScript.defaultState != null) {
351 ErrorMsg (tokenDeclState, "default state already declared");
352 continue;
353 }
354 tokenScript.defaultState = tokenDeclState;
355 continue;
356 }
357
358 /*
359 * state <name> <statebody>
360 */
361 if (token is TokenKwState) {
362 TokenDeclState tokenDeclState = new TokenDeclState (token);
363 token = token.nextToken;
364 if (!(token is TokenName)) {
365 ErrorMsg (token, "state must be followed by state name");
366 token = SkipPastSemi (token);
367 continue;
368 }
369 tokenDeclState.name = (TokenName)token;
370 token = token.nextToken;
371 tokenDeclState.body = ParseStateBody (ref token);
372 if (tokenDeclState.body == null) continue;
373 if (tokenScript.states.ContainsKey (tokenDeclState.name.val)) {
374 ErrorMsg (tokenDeclState.name, "duplicate state definition");
375 continue;
376 }
377 tokenScript.states.Add (tokenDeclState.name.val, tokenDeclState);
378 continue;
379 }
380
381 /*
382 * Doesn't fit any of those forms, output message and skip to next statement.
383 */
384 ErrorMsg (token, "looking for var name, type, state or default, script-defined type declaration");
385 token = SkipPastSemi (token);
386 continue;
387 }
388
389 /*
390 * Must have a default state to start in.
391 */
392 if (!errors && (tokenScript.defaultState == null)) {
393 ErrorMsg (tokenScript, "no default state defined");
394 }
395
396 /*
397 * If any error messages were written out, set return value to null.
398 */
399 if (errors) tokenScript = null;
400 }
401
402 /**
403 * @brief Pre-scan the source for class, delegate, interface, typedef definition keywords.
404 * Clump the keywords and name being defined together, but leave the body intact.
405 * In the case of a delegate with an explicit return type, it reverses the name and return type.
406 * After this completes there shouldn't be any TokenKw{Class,Delegate,Interface,Typedef}
407 * keywords in the source, they are all replaced by TokenDeclSDType{Class,Delegate,Interface,
408 * Typedef} tokens which also encapsulate the name of the type being defined and any generic
409 * parameter names. The body remains intact in the source token stream following the
410 * TokenDeclSDType* token.
411 */
412 private void ParseSDTypePreScanPassOne (Token tokenBegin)
413 {
414 Stack<int> braceLevels = new Stack<int> ();
415 Stack<TokenDeclSDType> outerLevels = new Stack<TokenDeclSDType> ();
416 int openBraceLevel = 0;
417 braceLevels.Push (-1);
418 outerLevels.Push (null);
419
420 for (Token t = tokenBegin; !((t = t.nextToken) is TokenEnd);) {
421
422 /*
423 * Keep track of nested definitions so we can link them up.
424 * We also need to detect the end of class and interface definitions.
425 */
426 if (t is TokenKwBrcOpen) {
427 openBraceLevel ++;
428 continue;
429 }
430 if (t is TokenKwBrcClose) {
431 if (-- openBraceLevel < 0) {
432 ErrorMsg (t, "{ } mismatch");
433 return;
434 }
435 if (braceLevels.Peek () == openBraceLevel) {
436 braceLevels.Pop ();
437 outerLevels.Pop ().endToken = t;
438 }
439 continue;
440 }
441
442 /*
443 * Check for 'class' or 'interface'.
444 * They always define a new class or interface.
445 * They can contain nested script-defined type definitions.
446 */
447 if ((t is TokenKwClass) || (t is TokenKwInterface)) {
448 Token kw = t;
449 t = t.nextToken;
450 if (!(t is TokenName)) {
451 ErrorMsg (t, "expecting class or interface name");
452 t = SkipPastSemi (t).prevToken;
453 continue;
454 }
455 TokenName name = (TokenName)t;
456 t = t.nextToken;
457
458 /*
459 * Malloc the script-defined type object.
460 */
461 TokenDeclSDType decl;
462 if (kw is TokenKwClass) decl = new TokenDeclSDTypeClass (name, kw.prevToken is TokenKwPartial);
463 else decl = new TokenDeclSDTypeInterface (name);
464 decl.outerSDType = outerLevels.Peek ();
465
466 /*
467 * Check for generic parameter list.
468 */
469 if (!ParseGenProtoParamList (ref t, decl)) continue;
470
471 /*
472 * Splice in a TokenDeclSDType token that replaces the keyword and the name tokens
473 * and any generic parameters including the '<', ','s and '>'.
474 * kw = points to 'class' or 'interface' keyword.
475 * t = points to just past last part of class name parsed, hopefully a ':' or '{'.
476 */
477 decl.prevToken = decl.isPartial ? kw.prevToken.prevToken : kw.prevToken;
478 decl.nextToken = t;
479 decl.prevToken.nextToken = decl;
480 decl.nextToken.prevToken = decl;
481
482 /*
483 * Enter it in name lists so it can be seen by others.
484 */
485 Token partialNewBody = CatalogSDTypeDecl (decl);
486
487 /*
488 * Start inner type definitions.
489 */
490 braceLevels.Push (openBraceLevel);
491 outerLevels.Push (decl);
492
493 /*
494 * Scan the body starting on for before the '{'.
495 *
496 * If this body had an old partial merged into it,
497 * resume scanning at the beginning of the new body,
498 * ie, what used to be the first token after the '{'
499 * before the old body was spliced in.
500 */
501 if (partialNewBody != null) {
502
503 /*
504 * We have a partial that has had old partial body merged
505 * into new partial body. So resume scanning at the beginning
506 * of the new partial body so we don't get any duplicate scanning
507 * of the old partial body.
508 *
509 * <decl> ... { <oldbody> <newbody> }
510 * ^- resume scanning here
511 * but inc openBraceLevel because
512 * we skipped scanning the '{'
513 */
514 openBraceLevel ++;
515 t = partialNewBody;
516 }
517 t = t.prevToken;
518 continue;
519 }
520
521 /*
522 * Check for 'delegate'.
523 * It always defines a new delegate.
524 * Delegates never define nested types.
525 */
526 if (t is TokenKwDelegate) {
527 Token kw = t;
528 t = t.nextToken;
529
530 /*
531 * Next thing might be an explicit return type or the delegate's name.
532 * If it's a type token, then it's the return type, simple enough.
533 * But if it's a name token, it might be the name of some other script-defined type.
534 * The way to tell is that the delegate name is followed by a '(', whereas an
535 * explicit return type is followed by the delegate name.
536 */
537 Token retType = t;
538 TokenName delName = null;
539 Token u;
540 int angles = 0;
541 for (u = t; !(u is TokenKwParOpen); u = u.nextToken) {
542 if ((u is TokenKwSemi) || (u is TokenEnd)) break;
543 if (u is TokenKwCmpLT) angles ++;
544 if (u is TokenKwCmpGT) angles --;
545 if (u is TokenKwRSh) angles -= 2; // idiot >>
546 if ((angles == 0) && (u is TokenName)) delName = (TokenName)u;
547 }
548 if (!(u is TokenKwParOpen)) {
549 ErrorMsg (u, "expecting ( for delegate parameter list");
550 t = SkipPastSemi (t).prevToken;
551 continue;
552 }
553 if (delName == null) {
554 ErrorMsg (u, "expecting delegate name");
555 t = SkipPastSemi (t).prevToken;
556 continue;
557 }
558 if (retType == delName) retType = null;
559
560 /*
561 * Malloc the script-defined type object.
562 */
563 TokenDeclSDTypeDelegate decl = new TokenDeclSDTypeDelegate (delName);
564 decl.outerSDType = outerLevels.Peek ();
565
566 /*
567 * Check for generic parameter list.
568 */
569 t = delName.nextToken;
570 if (!ParseGenProtoParamList (ref t, decl)) continue;
571
572 /*
573 * Enter it in name lists so it can be seen by others.
574 */
575 CatalogSDTypeDecl (decl);
576
577 /*
578 * Splice in the token that replaces the 'delegate' keyword and the whole name
579 * (including the '<' name ... '>' parts). The return type token(s), if any,
580 * follow the splice token and come before the '('.
581 */
582 decl.prevToken = kw.prevToken;
583 kw.prevToken.nextToken = decl;
584
585 if (retType == null) {
586 decl.nextToken = t;
587 t.prevToken = decl;
588 } else {
589 decl.nextToken = retType;
590 retType.prevToken = decl;
591 retType.nextToken = t;
592 t.prevToken = retType;
593 }
594
595 /*
596 * Scan for terminating ';'.
597 * There cannot be an intervening class, delegate, interfate, typedef, { or }.
598 */
599 for (t = decl; !(t is TokenKwSemi); t = u) {
600 u = t.nextToken;
601 if ((u is TokenEnd) ||
602 (u is TokenKwClass) ||
603 (u is TokenKwDelegate) ||
604 (u is TokenKwInterface) ||
605 (u is TokenKwTypedef) ||
606 (u is TokenKwBrcOpen) ||
607 (u is TokenKwBrcClose)) {
608 ErrorMsg (t, "delegate missing terminating ;");
609 break;
610 }
611 }
612 decl.endToken = t;
613 continue;
614 }
615
616 /*
617 * Check for 'typedef'.
618 * It always defines a new macro.
619 * Typedefs never define nested types.
620 */
621 if (t is TokenKwTypedef) {
622 Token kw = t;
623 t = t.nextToken;
624
625 if (!(t is TokenName)) {
626 ErrorMsg (t, "expecting typedef name");
627 t = SkipPastSemi (t).prevToken;
628 continue;
629 }
630 TokenName tdName = (TokenName)t;
631 t = t.nextToken;
632
633 /*
634 * Malloc the script-defined type object.
635 */
636 TokenDeclSDTypeTypedef decl = new TokenDeclSDTypeTypedef (tdName);
637 decl.outerSDType = outerLevels.Peek ();
638
639 /*
640 * Check for generic parameter list.
641 */
642 if (!ParseGenProtoParamList (ref t, decl)) continue;
643
644 /*
645 * Enter it in name lists so it can be seen by others.
646 */
647 CatalogSDTypeDecl (decl);
648 numTypedefs ++;
649
650 /*
651 * Splice in the token that replaces the 'typedef' keyword and the whole name
652 * (including the '<' name ... '>' parts).
653 */
654 decl.prevToken = kw.prevToken;
655 kw.prevToken.nextToken = decl;
656 decl.nextToken = t;
657 t.prevToken = decl;
658
659 /*
660 * Scan for terminating ';'.
661 * There cannot be an intervening class, delegate, interfate, typedef, { or }.
662 */
663 Token u;
664 for (t = decl; !(t is TokenKwSemi); t = u) {
665 u = t.nextToken;
666 if ((u is TokenEnd) ||
667 (u is TokenKwClass) ||
668 (u is TokenKwDelegate) ||
669 (u is TokenKwInterface) ||
670 (u is TokenKwTypedef) ||
671 (u is TokenKwBrcOpen) ||
672 (u is TokenKwBrcClose)) {
673 ErrorMsg (t, "typedef missing terminating ;");
674 break;
675 }
676 }
677 decl.endToken = t;
678 continue;
679 }
680 }
681 }
682
683 /**
684 * @brief Parse a possibly generic type definition's parameter list.
685 * @param t = points to the possible opening '<' on entry
686 * points just past the closing '>' on return
687 * @param decl = the generic type being declared
688 * @returns false: parse error
689 * true: decl.genParams = filled in with parameter list
690 * decl.innerSDTypes = filled in with parameter list
691 */
692 private bool ParseGenProtoParamList (ref Token t, TokenDeclSDType decl)
693 {
694 /*
695 * Maybe there aren't any generic parameters.
696 * If so, leave decl.genParams = null.
697 */
698 if (!(t is TokenKwCmpLT)) return true;
699
700 /*
701 * Build list of generic parameter names.
702 */
703 Dictionary<string, int> parms = new Dictionary<string, int> ();
704 do {
705 t = t.nextToken;
706 if (!(t is TokenName)) {
707 ErrorMsg (t, "expecting generic parameter name");
708 break;
709 }
710 TokenName tn = (TokenName)t;
711 if (parms.ContainsKey (tn.val)) {
712 ErrorMsg (tn, "duplicate use of generic parameter name");
713 } else {
714 parms.Add (tn.val, parms.Count);
715 }
716 t = t.nextToken;
717 } while (t is TokenKwComma);
718 if (!(t is TokenKwCmpGT)) {
719 ErrorMsg (t, "expecting , for more params or > to end param list");
720 return false;
721 }
722 t = t.nextToken;
723 decl.genParams = parms;
724
725 return true;
726 }
727
728 /**
729 * @brief Catalog a script-defined type.
730 * Its short name (eg, 'Node') gets put in the next outer level (eg, 'List')'s inner type definition table.
731 * Its long name (eg, 'List.Node') gets put in the global type definition table.
732 */
733 public Token CatalogSDTypeDecl (TokenDeclSDType decl)
734 {
735 string longName = decl.longName.val;
736 TokenDeclSDType dupDecl;
737 if (!tokenScript.sdSrcTypesTryGetValue (longName, out dupDecl)) {
738 tokenScript.sdSrcTypesAdd (longName, decl);
739 if (decl.outerSDType != null) {
740 decl.outerSDType.innerSDTypes.Add (decl.shortName.val, decl);
741 }
742 return null;
743 }
744
745 if (!dupDecl.isPartial || !decl.isPartial) {
746 ErrorMsg (decl, "duplicate definition of type " + longName);
747 ErrorMsg (dupDecl, "previous definition here");
748 return null;
749 }
750
751 if (!GenericParametersMatch (decl, dupDecl)) {
752 ErrorMsg (decl, "all partial class generic parameters must match");
753 }
754
755 /*
756 * Have new declaration be the cataloged one because body is going to get
757 * snipped out of old declaration and pasted into new declaration.
758 */
759 tokenScript.sdSrcTypesRep (longName, decl);
760 if (decl.outerSDType != null) {
761 decl.outerSDType.innerSDTypes[decl.shortName.val] = decl;
762 }
763
764 /*
765 * Find old partial definition's opening brace.
766 */
767 Token dupBrcOpen;
768 for (dupBrcOpen = dupDecl; !(dupBrcOpen is TokenKwBrcOpen); dupBrcOpen = dupBrcOpen.nextToken) {
769 if (dupBrcOpen == dupDecl.endToken) {
770 ErrorMsg (dupDecl, "missing {");
771 return null;
772 }
773 }
774
775 /*
776 * Find new partial definition's opening brace.
777 */
778 Token brcOpen;
779 for (brcOpen = decl; !(brcOpen is TokenKwBrcOpen); brcOpen = brcOpen.nextToken) {
780 if (brcOpen is TokenEnd) {
781 ErrorMsg (decl, "missing {");
782 return null;
783 }
784 }
785 Token body = brcOpen.nextToken;
786
787 /*
788 * Stick old partial definition's extends/implementeds list just
789 * in front of new partial definition's extends/implementeds list.
790 *
791 * class oldextimp { oldbody } ...
792 * dupDecl dupBrcOpen dupDecl.endToken
793 *
794 * class newextimp { newbody } ...
795 * decl brcOpen body decl.endToken
796 *
797 * becomes
798 *
799 * class ...
800 * dupDecl
801 * dupDecl.endToken
802 *
803 * class oldextimp newextimp { oldbody newbody } ...
804 * decl brcOpen body decl.endToken
805 */
806 if (dupBrcOpen != dupDecl.nextToken) {
807 dupBrcOpen.prevToken.nextToken = decl.nextToken;
808 dupDecl.nextToken.prevToken = decl;
809 decl.nextToken.prevToken = dupBrcOpen.prevToken;
810 decl.nextToken = dupDecl.nextToken;
811 }
812
813 /*
814 * Stick old partial definition's body just
815 * in front of new partial definition's body.
816 */
817 if (dupBrcOpen.nextToken != dupDecl.endToken) {
818 dupBrcOpen.nextToken.prevToken = brcOpen;
819 dupDecl.endToken.prevToken.nextToken = body;
820 body.prevToken = dupDecl.endToken.prevToken;
821 brcOpen.nextToken = dupBrcOpen.nextToken;
822 }
823
824 /*
825 * Null out old definition's extends/implementeds list and body
826 * by having the declaration token be the only thing left.
827 */
828 dupDecl.nextToken = dupDecl.endToken.nextToken;
829 dupDecl.nextToken.prevToken = dupDecl;
830 dupDecl.endToken = dupDecl;
831
832 return body;
833 }
834
835 /**
836 * @brief Determine whether or not the generic parameters of two class declarations match exactly.
837 */
838 private static bool GenericParametersMatch (TokenDeclSDType c1, TokenDeclSDType c2)
839 {
840 if ((c1.genParams == null) && (c2.genParams == null)) return true;
841 if ((c1.genParams == null) || (c2.genParams == null)) return false;
842 Dictionary<string, int> gp1 = c1.genParams;
843 Dictionary<string, int> gp2 = c2.genParams;
844 if (gp1.Count != gp2.Count) return false;
845 foreach (KeyValuePair<string, int> kvp1 in gp1) {
846 int v2;
847 if (!gp2.TryGetValue (kvp1.Key, out v2)) return false;
848 if (v2 != kvp1.Value) return false;
849 }
850 return true;
851 }
852
853 /**
854 * @brief Replace all TokenName tokens that refer to the script-defined types with
855 * corresponding TokenTypeSDType{Class,Delegate,GenParam,Interface} tokens.
856 * Also handle generic references, ie, recognize that 'List<integer>' is an
857 * instantiation of 'List<>' and instantiate the generic.
858 */
859 private const uint REPEAT_NOTYPE = 1;
860 private const uint REPEAT_INSTGEN = 2;
861 private const uint REPEAT_SUBST = 4;
862
863 private void ParseSDTypePreScanPassTwo (Token tokenBegin)
864 {
865 List<Token> noTypes = new List<Token> ();
866 TokenDeclSDType outerSDType;
867 uint repeat;
868
869 do {
870 repeat = 0;
871 outerSDType = null;
872 noTypes.Clear ();
873
874 for (Token t = tokenBegin; !((t = t.nextToken) is TokenEnd);) {
875
876 /*
877 * Maybe it's time to pop out of an outer class definition.
878 */
879 if ((outerSDType != null) && (outerSDType.endToken == t)) {
880 outerSDType = outerSDType.outerSDType;
881 continue;
882 }
883
884 /*
885 * Skip completely over any script-defined generic prototypes.
886 * We only need to process their instantiations which are non-
887 * generic versions of the generics.
888 */
889 if ((t is TokenDeclSDType) && (((TokenDeclSDType)t).genParams != null)) {
890 t = ((TokenDeclSDType)t).endToken;
891 continue;
892 }
893
894 /*
895 * Check for beginning of non-generic script-defined type definitions.
896 * They can have nested definitions in their innerSDTypes[] that match
897 * name tokens, so add them to the stack.
898 *
899 * But just ignore any preliminary partial definitions as they have had
900 * their entire contents spliced out and spliced into a subsequent partial
901 * definition. So if we originally had:
902 * partial class Abc { public intenger one; }
903 * partial class Abc { public intenger two; }
904 * We now have:
905 * partial_class_Abc <== if we are here, just ignore the partial_class_Abc token
906 * partial_class_Abc { public intenger one; public intenger two; }
907 */
908 if (t is TokenDeclSDType) {
909 if (((TokenDeclSDType)t).endToken != t) {
910 outerSDType = (TokenDeclSDType)t;
911 }
912 continue;
913 }
914
915 /*
916 * For names not preceded by a '.', scan the script-defined type definition
917 * stack for that name. Splice the name out and replace with equivalent token.
918 */
919 if ((t is TokenName) && !(t.prevToken is TokenKwDot)) {
920 t = TrySpliceTypeRef (t, outerSDType, ref repeat, noTypes);
921 }
922
923 /*
924 * This handles types such as integer[,][], List<string>[], etc.
925 * They are an instantiation of an internally generated type of the same name, brackets and all.
926 * Note that to malloc an array, use something like 'new float[,][](3,5)', not 'new float[3,5][]'.
927 *
928 * Note that we must not get confused by $idxprop property declarations such as:
929 * float [string kee] { get { ... } }
930 * ... and try to convert 'float' '[' to an array type.
931 */
932 if ((t is TokenType) && (t.nextToken is TokenKwBrkOpen)) {
933 if ((t.nextToken.nextToken is TokenKwBrkClose) ||
934 (t.nextToken.nextToken is TokenKwComma)) {
935 t = InstantiateJaggedArray (t, tokenBegin, ref repeat);
936 }
937 }
938 }
939
940 /*
941 * If we instantiated a generic, loop back to process its contents
942 * just as if the source code had the instantiated code to begin with.
943 * Also repeat if we found a non-type inside the <> of a generic reference
944 * provided we have made at least one name->type substitution.
945 */
946 } while (((repeat & REPEAT_INSTGEN) != 0) ||
947 ((repeat & (REPEAT_NOTYPE | REPEAT_SUBST)) == (REPEAT_NOTYPE | REPEAT_SUBST)));
948
949 /*
950 * These are places where we required a type be present,
951 * eg, a generic type argument or the body of a typedef.
952 */
953 foreach (Token t in noTypes) {
954 ErrorMsg (t, "looking for type");
955 }
956 }
957
958 /**
959 * @brief Try to convert the source token string to a type reference
960 * and splice the type reference into the source token string
961 * replacing the original token(s).
962 * @param t = points to the initial TokenName token
963 * @param outerSDType = null: this is a top-level code reference
964 * else: this code is within outerSDType
965 * @returns pointer to last token parsed
966 * possibly with spliced-in type token
967 * repeat = possibly set true if need to do another pass
968 */
969 private Token TrySpliceTypeRef (Token t, TokenDeclSDType outerSDType, ref uint repeat, List<Token> noTypes)
970 {
971 Token start = t;
972 string tnamestr = ((TokenName)t).val;
973
974 /*
975 * Look for the name as a type declared by outerSDType or anything
976 * even farther out than that. If not found, simply return
977 * without updating t, meaning that t isn't the name of a type.
978 */
979 TokenDeclSDType decl = null;
980 while (outerSDType != null) {
981 if (outerSDType.innerSDTypes.TryGetValue (tnamestr, out decl)) break;
982 outerSDType = outerSDType.outerSDType;
983 }
984 if ((outerSDType == null) && !tokenScript.sdSrcTypesTryGetValue (tnamestr, out decl)) return t;
985
986 TokenDeclSDType instdecl;
987 while (true) {
988
989 /*
990 * If it is a generic type, it must be followed by instantiation arguments.
991 */
992 instdecl = decl;
993 if (decl.genParams != null) {
994 t = t.nextToken;
995 if (!(t is TokenKwCmpLT)) {
996 ErrorMsg (t, "expecting < for generic argument list");
997 return t;
998 }
999 tnamestr += "<";
1000 int nArgs = decl.genParams.Count;
1001 TokenType[] genArgs = new TokenType[nArgs];
1002 for (int i = 0; i < nArgs;) {
1003 t = t.nextToken;
1004 if (!(t is TokenType)) {
1005 repeat |= REPEAT_NOTYPE;
1006 noTypes.Add (t);
1007 return t.prevToken; // make sure name gets processed
1008 // so substitution can occur on it
1009 }
1010 TokenType ga = (TokenType)t;
1011 genArgs[i] = ga;
1012 tnamestr += ga.ToString ();
1013 t = t.nextToken;
1014 if (++ i < nArgs) {
1015 if (!(t is TokenKwComma)) {
1016 ErrorMsg (t, "expecting , for more generic arguments");
1017 return t;
1018 }
1019 tnamestr += ",";
1020 }
1021 }
1022 if (t is TokenKwRSh) { // idiot >>
1023 Token u = new TokenKwCmpGT (t);
1024 Token v = new TokenKwCmpGT (t);
1025 v.posn ++;
1026 u.prevToken = t.prevToken;
1027 u.nextToken = v;
1028 v.nextToken = t.nextToken;
1029 v.prevToken = u;
1030 u.prevToken.nextToken = u;
1031 v.nextToken.prevToken = v;
1032 t = u;
1033 }
1034 if (!(t is TokenKwCmpGT)) {
1035 ErrorMsg (t, "expecting > at end of generic argument list");
1036 return t;
1037 }
1038 tnamestr += ">";
1039 if (outerSDType != null) {
1040 outerSDType.innerSDTypes.TryGetValue (tnamestr, out instdecl);
1041 } else {
1042 tokenScript.sdSrcTypesTryGetValue (tnamestr, out instdecl);
1043 }
1044
1045 /*
1046 * Couldn't find 'List<string>' but found 'List' and we have genArgs = 'string'.
1047 * Instantiate the generic to create 'List<string>'. This splices the definition
1048 * of 'List<string>' into the source token stream just as if it had been there all
1049 * along. We have to then repeat the scan to process the instance's contents.
1050 */
1051 if (instdecl == null) {
1052 instdecl = decl.InstantiateGeneric (tnamestr, genArgs, this);
1053 CatalogSDTypeDecl (instdecl);
1054 repeat |= REPEAT_INSTGEN;
1055 }
1056 }
1057
1058 /*
1059 * Maybe caller wants a subtype by putting a '.' following all that.
1060 */
1061 if (!(t.nextToken is TokenKwDot)) break;
1062 if (!(t.nextToken.nextToken is TokenName)) break;
1063 tnamestr = ((TokenName)t.nextToken.nextToken).val;
1064 if (!instdecl.innerSDTypes.TryGetValue (tnamestr, out decl)) break;
1065 t = t.nextToken.nextToken;
1066 outerSDType = instdecl;
1067 }
1068
1069 /*
1070 * Create a reference in the source to the definition
1071 * that encapsulates the long dotted type name given in
1072 * the source, and replace the long dotted type name in
1073 * the source with the reference token, eg, replace
1074 * 'Dictionary' '<' 'string' ',' 'integer' '>' '.' 'ValueList'
1075 * with 'Dictionary<string,integer>.ValueList'.
1076 */
1077 TokenType refer = instdecl.MakeRefToken (start);
1078 if (refer == null) {
1079 // typedef body is not yet a type
1080 noTypes.Add (start);
1081 repeat |= REPEAT_NOTYPE;
1082 return start;
1083 }
1084 refer.prevToken = start.prevToken; // start points right at the first TokenName
1085 refer.nextToken = t.nextToken; // t points at the last TokenName or TokenKwCmpGT
1086 refer.prevToken.nextToken = refer;
1087 refer.nextToken.prevToken = refer;
1088 repeat |= REPEAT_SUBST;
1089
1090 return refer;
1091 }
1092
1093 /**
1094 * @brief We are known to have <type>'[' so make an equivalent array type.
1095 * @param t = points to the TokenType
1096 * @param tokenBegin = where we can safely splice in new array class definitions
1097 * @param repeat = set REPEAT_INSTGEN if new type created
1098 * @returns pointer to last token parsed
1099 * possibly with spliced-in type token
1100 * repeat = possibly set true if need to do another pass
1101 */
1102 private Token InstantiateJaggedArray (Token t, Token tokenBegin, ref uint repeat)
1103 {
1104 Token start = t;
1105 TokenType ofType = (TokenType)t;
1106
1107 Stack<int> ranks = new Stack<int> ();
1108
1109 /*
1110 * When script specifies 'float[,][]' it means a two-dimensional matrix
1111 * that points to one-dimensional vectors of floats. So we would push
1112 * a 2 then a 1 in this parsing code...
1113 */
1114 do {
1115 t = t.nextToken; // point at '['
1116 int rank = 0;
1117 do {
1118 rank ++; // count '[' and ','s
1119 t = t.nextToken; // point at ',' or ']'
1120 } while (t is TokenKwComma);
1121 if (!(t is TokenKwBrkClose)) {
1122 ErrorMsg (t, "expecting only [ , or ] for array type specification");
1123 return t;
1124 }
1125 ranks.Push (rank);
1126 } while (t.nextToken is TokenKwBrkOpen);
1127
1128 /*
1129 * Now we build the types in reverse order. For the example above we will:
1130 * first, create a type that is a one-dimensional vector of floats, float[]
1131 * second, create a type that is a two-dimensional matrix of that.
1132 * This keeps declaration and referencing similar, eg,
1133 * float[,][] jag = new float[,][] (3,4);
1134 * jag[i,j][k] ... is used to access the elements
1135 */
1136 do {
1137 int rank = ranks.Pop ();
1138 TokenDeclSDType decl = InstantiateFixedArray (rank, ofType, tokenBegin, ref repeat);
1139 ofType = decl.MakeRefToken (ofType);
1140 } while (ranks.Count > 0);
1141
1142 /*
1143 * Finally splice in the resultant array type to replace the original tokens.
1144 */
1145 ofType.prevToken = start.prevToken;
1146 ofType.nextToken = t.nextToken;
1147 ofType.prevToken.nextToken = ofType;
1148 ofType.nextToken.prevToken = ofType;
1149
1150 /*
1151 * Resume parsing just after the spliced-in array type token.
1152 */
1153 return ofType;
1154 }
1155
1156 /**
1157 * @brief Instantiate a script-defined class type to handle fixed-dimension arrays.
1158 * @param rank = number of dimensions for the array
1159 * @param ofType = type of each element of the array
1160 * @returns script-defined class declaration created to handle the array
1161 */
1162 private TokenDeclSDType InstantiateFixedArray (int rank, TokenType ofType, Token tokenBegin, ref uint repeat)
1163 {
1164 /*
1165 * Create the array type's name.
1166 * If starting with a non-array type, just append the rank to it, eg, float + rank=1 -> float[]
1167 * If starting with an array type, slip this rank in front of existing array, eg, float[] + rank=2 -> float[,][].
1168 * This makes it consistent with what the script-writer sees for both a type specification and when
1169 * referencing elements in a jagged array.
1170 */
1171 string name = ofType.ToString ();
1172 StringBuilder sb = new StringBuilder (name);
1173 int ix = name.IndexOf ('[');
1174 if (ix < 0) ix = name.Length;
1175 sb.Insert (ix ++, '[');
1176 for (int i = 0; ++ i < rank;) {
1177 sb.Insert (ix ++, ',');
1178 }
1179 sb.Insert (ix, ']');
1180 name = sb.ToString ();
1181
1182 TokenDeclSDType fa;
1183 if (!tokenScript.sdSrcTypesTryGetValue (name, out fa)) {
1184 char suffix = 'O';
1185 if (ofType is TokenTypeChar) suffix = 'C';
1186 if (ofType is TokenTypeFloat) suffix = 'F';
1187 if (ofType is TokenTypeInt) suffix = 'I';
1188
1189 /*
1190 * Don't already have one, create a new skeleton struct.
1191 * Splice in a definition for the class at beginning of source file.
1192 *
1193 * class <arraytypename> {
1194 */
1195 fa = new TokenDeclSDTypeClass (new TokenName (tokenScript, name), false);
1196 CatalogSDTypeDecl (fa);
1197 repeat |= REPEAT_INSTGEN;
1198 ((TokenDeclSDTypeClass)fa).arrayOfType = ofType;
1199 ((TokenDeclSDTypeClass)fa).arrayOfRank = rank;
1200
1201 Token t = SpliceAfter (tokenBegin, fa);
1202 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1203
1204 /*
1205 * public integer len0;
1206 * public integer len1;
1207 * ...
1208 * public object obj;
1209 */
1210 for (int i = 0; i < rank; i ++) {
1211 t = SpliceAfter (t, new TokenKwPublic (t));
1212 t = SpliceAfter (t, new TokenTypeInt (t));
1213 t = SpliceAfter (t, new TokenName (t, "len" + i));
1214 t = SpliceAfter (t, new TokenKwSemi (t));
1215 }
1216
1217 t = SpliceAfter (t, new TokenKwPublic (t));
1218 t = SpliceAfter (t, new TokenTypeObject (t));
1219 t = SpliceAfter (t, new TokenName (t, "obj"));
1220 t = SpliceAfter (t, new TokenKwSemi (t));
1221
1222 /*
1223 * public constructor (integer len0, integer len1, ...) {
1224 * this.len0 = len0;
1225 * this.len1 = len1;
1226 * ...
1227 * this.obj = xmrFixedArrayAlloc<suffix> (len0 * len1 * ...);
1228 * }
1229 */
1230 t = SpliceAfter (t, new TokenKwPublic (t));
1231 t = SpliceAfter (t, new TokenKwConstructor (t));
1232 t = SpliceAfter (t, new TokenKwParOpen (t));
1233 for (int i = 0; i < rank; i ++) {
1234 if (i > 0) t = SpliceAfter (t, new TokenKwComma (t));
1235 t = SpliceAfter (t, new TokenTypeInt (t));
1236 t = SpliceAfter (t, new TokenName (t, "len" + i));
1237 }
1238 t = SpliceAfter (t, new TokenKwParClose (t));
1239 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1240
1241 for (int i = 0; i < rank; i ++) {
1242 t = SpliceAfter (t, new TokenKwThis (t));
1243 t = SpliceAfter (t, new TokenKwDot (t));
1244 t = SpliceAfter (t, new TokenName (t, "len" + i));
1245 t = SpliceAfter (t, new TokenKwAssign (t));
1246 t = SpliceAfter (t, new TokenName (t, "len" + i));
1247 t = SpliceAfter (t, new TokenKwSemi (t));
1248 }
1249
1250 t = SpliceAfter (t, new TokenKwThis (t));
1251 t = SpliceAfter (t, new TokenKwDot (t));
1252 t = SpliceAfter (t, new TokenName (t, "obj"));
1253 t = SpliceAfter (t, new TokenKwAssign (t));
1254 t = SpliceAfter (t, new TokenName (t, "xmrFixedArrayAlloc" + suffix));
1255 t = SpliceAfter (t, new TokenKwParOpen (t));
1256 for (int i = 0; i < rank; i ++) {
1257 if (i > 0) t = SpliceAfter (t, new TokenKwMul (t));
1258 t = SpliceAfter (t, new TokenName (t, "len" + i));
1259 }
1260 t = SpliceAfter (t, new TokenKwParClose (t));
1261 t = SpliceAfter (t, new TokenKwSemi (t));
1262 t = SpliceAfter (t, new TokenKwBrcClose (t));
1263
1264 /*
1265 * public integer Length { get {
1266 * return this.len0 * this.len1 * ... ;
1267 * } }
1268 */
1269 t = SpliceAfter (t, new TokenKwPublic (t));
1270 t = SpliceAfter (t, new TokenTypeInt (t));
1271 t = SpliceAfter (t, new TokenName (t, "Length"));
1272 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1273 t = SpliceAfter (t, new TokenKwGet (t));
1274 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1275
1276 t = SpliceAfter (t, new TokenKwRet (t));
1277 for (int i = 0; i < rank; i ++) {
1278 if (i > 0) t = SpliceAfter (t, new TokenKwMul (t));
1279 t = SpliceAfter (t, new TokenKwThis (t));
1280 t = SpliceAfter (t, new TokenKwDot (t));
1281 t = SpliceAfter (t, new TokenName (t, "len" + i));
1282 }
1283 t = SpliceAfter (t, new TokenKwSemi (t));
1284
1285 t = SpliceAfter (t, new TokenKwBrcClose (t));
1286 t = SpliceAfter (t, new TokenKwBrcClose (t));
1287
1288 /*
1289 * public integer Length (integer dim) {
1290 * switch (dim) {
1291 * case 0: return this.len0;
1292 * case 1: return this.len1;
1293 * ...
1294 * }
1295 * return 0;
1296 * }
1297 */
1298 t = SpliceAfter (t, new TokenKwPublic (t));
1299 t = SpliceAfter (t, new TokenTypeInt (t));
1300 t = SpliceAfter (t, new TokenName (t, "Length"));
1301 t = SpliceAfter (t, new TokenKwParOpen (t));
1302 t = SpliceAfter (t, new TokenTypeInt (t));
1303 t = SpliceAfter (t, new TokenName (t, "dim"));
1304 t = SpliceAfter (t, new TokenKwParClose (t));
1305 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1306
1307 t = SpliceAfter (t, new TokenKwSwitch (t));
1308 t = SpliceAfter (t, new TokenKwParOpen (t));
1309 t = SpliceAfter (t, new TokenName (t, "dim"));
1310 t = SpliceAfter (t, new TokenKwParClose (t));
1311 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1312
1313 for (int i = 0; i < rank; i ++) {
1314 t = SpliceAfter (t, new TokenKwCase (t));
1315 t = SpliceAfter (t, new TokenInt (t, i));
1316 t = SpliceAfter (t, new TokenKwColon (t));
1317 t = SpliceAfter (t, new TokenKwRet (t));
1318 t = SpliceAfter (t, new TokenKwThis (t));
1319 t = SpliceAfter (t, new TokenKwDot (t));
1320 t = SpliceAfter (t, new TokenName (t, "len" + i));
1321 t = SpliceAfter (t, new TokenKwSemi (t));
1322 }
1323 t = SpliceAfter (t, new TokenKwBrcClose (t));
1324
1325 t = SpliceAfter (t, new TokenKwRet (t));
1326 t = SpliceAfter (t, new TokenInt (t, 0));
1327 t = SpliceAfter (t, new TokenKwSemi (t));
1328 t = SpliceAfter (t, new TokenKwBrcClose (t));
1329
1330 /*
1331 * public integer Index (integer idx0, integet idx1, ...) {
1332 * integer idx = idx0;
1333 * idx *= this.len1; idx += idx1;
1334 * idx *= this.len2; idx += idx2;
1335 * ...
1336 * return idx;
1337 * }
1338 */
1339 t = SpliceAfter (t, new TokenKwPublic (t));
1340 t = SpliceAfter (t, new TokenTypeInt (t));
1341 t = SpliceAfter (t, new TokenName (t, "Index"));
1342 t = SpliceAfter (t, new TokenKwParOpen (t));
1343 for (int i = 0; i < rank; i ++) {
1344 if (i > 0) t = SpliceAfter (t, new TokenKwComma (t));
1345 t = SpliceAfter (t, new TokenTypeInt (t));
1346 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1347 }
1348 t = SpliceAfter (t, new TokenKwParClose (t));
1349 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1350
1351 t = SpliceAfter (t, new TokenTypeInt (t));
1352 t = SpliceAfter (t, new TokenName (t, "idx"));
1353 t = SpliceAfter (t, new TokenKwAssign (t));
1354 t = SpliceAfter (t, new TokenName (t, "idx0"));
1355 t = SpliceAfter (t, new TokenKwSemi (t));
1356
1357 for (int i = 1; i < rank; i ++) {
1358 t = SpliceAfter (t, new TokenName (t, "idx"));
1359 t = SpliceAfter (t, new TokenKwAsnMul (t));
1360 t = SpliceAfter (t, new TokenKwThis (t));
1361 t = SpliceAfter (t, new TokenKwDot (t));
1362 t = SpliceAfter (t, new TokenName (t, "len" + i));
1363 t = SpliceAfter (t, new TokenKwSemi (t));
1364 t = SpliceAfter (t, new TokenName (t, "idx"));
1365 t = SpliceAfter (t, new TokenKwAsnAdd (t));
1366 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1367 t = SpliceAfter (t, new TokenKwSemi (t));
1368 }
1369
1370 t = SpliceAfter (t, new TokenKwRet (t));
1371 t = SpliceAfter (t, new TokenName (t, "idx"));
1372 t = SpliceAfter (t, new TokenKwSemi (t));
1373 t = SpliceAfter (t, new TokenKwBrcClose (t));
1374
1375 /*
1376 * public <oftype> Get (integer idx0, integet idx1, ...) {
1377 * integer idx = idx0;
1378 * idx *= this.len1; idx += idx1;
1379 * idx *= this.len2; idx += idx2;
1380 * ...
1381 * return (<oftype>) xmrFixedArrayGet<suffix> (this.obj, idx);
1382 * }
1383 */
1384 t = SpliceAfter (t, new TokenKwPublic (t));
1385 t = SpliceAfter (t, ofType.CopyToken (t));
1386 t = SpliceAfter (t, new TokenName (t, "Get"));
1387 t = SpliceAfter (t, new TokenKwParOpen (t));
1388 for (int i = 0; i < rank; i ++) {
1389 if (i > 0) t = SpliceAfter (t, new TokenKwComma (t));
1390 t = SpliceAfter (t, new TokenTypeInt (t));
1391 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1392 }
1393 t = SpliceAfter (t, new TokenKwParClose (t));
1394 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1395
1396 t = SpliceAfter (t, new TokenTypeInt (t));
1397 t = SpliceAfter (t, new TokenName (t, "idx"));
1398 t = SpliceAfter (t, new TokenKwAssign (t));
1399 t = SpliceAfter (t, new TokenName (t, "idx0"));
1400 t = SpliceAfter (t, new TokenKwSemi (t));
1401
1402 for (int i = 1; i < rank; i ++) {
1403 t = SpliceAfter (t, new TokenName (t, "idx"));
1404 t = SpliceAfter (t, new TokenKwAsnMul (t));
1405 t = SpliceAfter (t, new TokenKwThis (t));
1406 t = SpliceAfter (t, new TokenKwDot (t));
1407 t = SpliceAfter (t, new TokenName (t, "len" + i));
1408 t = SpliceAfter (t, new TokenKwSemi (t));
1409 t = SpliceAfter (t, new TokenName (t, "idx"));
1410 t = SpliceAfter (t, new TokenKwAsnAdd (t));
1411 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1412 t = SpliceAfter (t, new TokenKwSemi (t));
1413 }
1414
1415 t = SpliceAfter (t, new TokenKwRet (t));
1416 if (suffix == 'O') {
1417 t = SpliceAfter (t, new TokenKwParOpen (t));
1418 t = SpliceAfter (t, ofType.CopyToken (t));
1419 t = SpliceAfter (t, new TokenKwParClose (t));
1420 }
1421 t = SpliceAfter (t, new TokenName (t, "xmrFixedArrayGet" + suffix));
1422 t = SpliceAfter (t, new TokenKwParOpen (t));
1423 t = SpliceAfter (t, new TokenKwThis (t));
1424 t = SpliceAfter (t, new TokenKwDot (t));
1425 t = SpliceAfter (t, new TokenName (t, "obj"));
1426 t = SpliceAfter (t, new TokenKwComma (t));
1427 t = SpliceAfter (t, new TokenName (t, "idx"));
1428 t = SpliceAfter (t, new TokenKwParClose (t));
1429 t = SpliceAfter (t, new TokenKwSemi (t));
1430 t = SpliceAfter (t, new TokenKwBrcClose (t));
1431
1432 /*
1433 * public void Set (integer idx0, integer idx1, ..., <oftype> val) {
1434 * integer idx = idx0;
1435 * idx *= this.len1; idx += idx1;
1436 * idx *= this.len2; idx += idx2;
1437 * ...
1438 * xmrFixedArraySet<suffix> (this.obj, idx, val);
1439 * }
1440 */
1441 t = SpliceAfter (t, new TokenKwPublic (t));
1442 t = SpliceAfter (t, new TokenTypeVoid (t));
1443 t = SpliceAfter (t, new TokenName (t, "Set"));
1444 t = SpliceAfter (t, new TokenKwParOpen (t));
1445 for (int i = 0; i < rank; i ++) {
1446 t = SpliceAfter (t, new TokenTypeInt (t));
1447 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1448 t = SpliceAfter (t, new TokenKwComma (t));
1449 }
1450 t = SpliceAfter (t, ofType.CopyToken (t));
1451 t = SpliceAfter (t, new TokenName (t, "val"));
1452 t = SpliceAfter (t, new TokenKwParClose (t));
1453 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1454
1455 t = SpliceAfter (t, new TokenTypeInt (t));
1456 t = SpliceAfter (t, new TokenName (t, "idx"));
1457 t = SpliceAfter (t, new TokenKwAssign (t));
1458 t = SpliceAfter (t, new TokenName (t, "idx0"));
1459 t = SpliceAfter (t, new TokenKwSemi (t));
1460 for (int i = 1; i < rank; i ++) {
1461 t = SpliceAfter (t, new TokenName (t, "idx"));
1462 t = SpliceAfter (t, new TokenKwAsnMul (t));
1463 t = SpliceAfter (t, new TokenKwThis (t));
1464 t = SpliceAfter (t, new TokenKwDot (t));
1465 t = SpliceAfter (t, new TokenName (t, "len" + i));
1466 t = SpliceAfter (t, new TokenKwSemi (t));
1467 t = SpliceAfter (t, new TokenName (t, "idx"));
1468 t = SpliceAfter (t, new TokenKwAsnAdd (t));
1469 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1470 t = SpliceAfter (t, new TokenKwSemi (t));
1471 }
1472
1473 t = SpliceAfter (t, new TokenName (t, "xmrFixedArraySet" + suffix));
1474 t = SpliceAfter (t, new TokenKwParOpen (t));
1475 t = SpliceAfter (t, new TokenKwThis (t));
1476 t = SpliceAfter (t, new TokenKwDot (t));
1477 t = SpliceAfter (t, new TokenName (t, "obj"));
1478 t = SpliceAfter (t, new TokenKwComma (t));
1479 t = SpliceAfter (t, new TokenName (t, "idx"));
1480 t = SpliceAfter (t, new TokenKwComma (t));
1481 t = SpliceAfter (t, new TokenName (t, "val"));
1482 t = SpliceAfter (t, new TokenKwParClose (t));
1483 t = SpliceAfter (t, new TokenKwSemi (t));
1484
1485 t = SpliceAfter (t, new TokenKwBrcClose (t));
1486 t = SpliceAfter (t, new TokenKwBrcClose (t));
1487 }
1488 return fa;
1489 }
1490 private Token SpliceAfter (Token before, Token after)
1491 {
1492 after.nextToken = before.nextToken;
1493 after.prevToken = before;
1494 before.nextToken = after;
1495 after.nextToken.prevToken = after;
1496 return after;
1497 }
1498
1499 /**
1500 * @brief Parse script-defined type declarations.
1501 * @param token = points to possible script-defined type keyword
1502 * @param outerSDType = null: top-level type
1503 * else: sub-type of this type
1504 * @param flags = access level (SDT_{PRIVATE,PROTECTED,PUBLIC})
1505 * @returns true: something defined; else: not a sd type def
1506 */
1507 private bool ParseDeclSDTypes (ref Token token, TokenDeclSDType outerSDType, uint flags)
1508 {
1509 if (!(token is TokenDeclSDType)) return false;
1510
1511 TokenDeclSDType decl = (TokenDeclSDType)token;
1512
1513 /*
1514 * If declaration of generic type, skip it.
1515 * The instantiations get parsed (ie, when we know the concrete types)
1516 * below because they appear as non-generic types.
1517 */
1518 if (decl.genParams != null) {
1519 token = decl.endToken.nextToken;
1520 return true;
1521 }
1522
1523 /*
1524 * Also skip over any typedefs. They were all processed in
1525 * ParseSDTypePreScanPassTwo().
1526 */
1527 if (decl is TokenDeclSDTypeTypedef) {
1528 token = decl.endToken.nextToken;
1529 return true;
1530 }
1531
1532 /*
1533 * Non-generic types get parsed inline because we know all their types.
1534 */
1535 if (decl is TokenDeclSDTypeClass) {
1536 ParseDeclClass (ref token, outerSDType, flags);
1537 return true;
1538 }
1539 if (decl is TokenDeclSDTypeDelegate) {
1540 ParseDeclDelegate (ref token, outerSDType, flags);
1541 return true;
1542 }
1543 if (decl is TokenDeclSDTypeInterface) {
1544 ParseDeclInterface (ref token, outerSDType, flags);
1545 return true;
1546 }
1547
1548 throw new Exception ("unhandled token " + token.GetType ().ToString ());
1549 }
1550
1551 /**
1552 * @brief Parse a class declaration.
1553 * @param token = points to TokenDeclSDTypeClass token
1554 * points just past closing '}' on return
1555 * @param outerSDType = null: this is a top-level class
1556 * else: this class is being defined inside this type
1557 * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
1558 */
1559 private void ParseDeclClass (ref Token token, TokenDeclSDType outerSDType, uint flags)
1560 {
1561 bool haveExplicitConstructor = false;
1562 Token u = token;
1563 TokenDeclSDTypeClass tokdeclcl;
1564
1565 tokdeclcl = (TokenDeclSDTypeClass)u;
1566 tokdeclcl.outerSDType = outerSDType;
1567 tokdeclcl.accessLevel = flags;
1568 u = u.nextToken;
1569
1570 // maybe it is a partial class that had its body snipped out
1571 // by a later partial class declaration of the same class
1572 if (tokdeclcl.endToken == tokdeclcl) {
1573 token = u;
1574 return;
1575 }
1576
1577 // make this class the currently compiled class
1578 // used for retrieving stuff like 'this' possibly
1579 // in field initialization code
1580 TokenDeclSDType saveCurSDType = currentDeclSDType;
1581 currentDeclSDType = tokdeclcl;
1582
1583 // next can be ':' followed by list of implemented
1584 // interfaces and one extended class
1585 if (u is TokenKwColon) {
1586 u = u.nextToken;
1587 while (true) {
1588 if (u is TokenTypeSDTypeClass) {
1589 TokenDeclSDTypeClass c = ((TokenTypeSDTypeClass)u).decl;
1590 if (tokdeclcl.extends == null) {
1591 tokdeclcl.extends = c;
1592 } else if (tokdeclcl.extends != c) {
1593 ErrorMsg (u, "can extend from only one class");
1594 }
1595 } else if (u is TokenTypeSDTypeInterface) {
1596 TokenDeclSDTypeInterface i = ((TokenTypeSDTypeInterface)u).decl;
1597 i.AddToClassDecl (tokdeclcl);
1598 } else {
1599 ErrorMsg (u, "expecting class or interface name");
1600 if (u is TokenKwBrcOpen) break;
1601 }
1602 u = u.nextToken;
1603
1604 // allow : in case it is spliced from multiple partial class definitions
1605 if (!(u is TokenKwComma) && !(u is TokenKwColon)) break;
1606 u = u.nextToken;
1607 }
1608 }
1609
1610 // next must be '{' to open class declaration body
1611 if (!(u is TokenKwBrcOpen)) {
1612 ErrorMsg (u, "expecting { to open class declaration body");
1613 token = SkipPastSemi (token);
1614 goto ret;
1615 }
1616 token = u.nextToken;
1617
1618 // push a var frame to put all the class members in
1619 tokdeclcl.members.thisClass = tokdeclcl;
1620 tokenScript.PushVarFrame (tokdeclcl.members);
1621
1622 /*
1623 * Create a function $instfieldnit to hold all explicit
1624 * instance field initializations.
1625 */
1626 TokenDeclVar ifiFunc = new TokenDeclVar (tokdeclcl, null, tokenScript);
1627 ifiFunc.name = new TokenName (ifiFunc, "$instfieldinit");
1628 ifiFunc.retType = new TokenTypeVoid (ifiFunc);
1629 ifiFunc.argDecl = new TokenArgDecl (ifiFunc);
1630 ifiFunc.sdtClass = tokdeclcl;
1631 ifiFunc.sdtFlags = SDT_PUBLIC | SDT_NEW;
1632 TokenStmtBlock ifiBody = new TokenStmtBlock (ifiFunc);
1633 ifiBody.function = ifiFunc;
1634 ifiFunc.body = ifiBody;
1635 tokdeclcl.instFieldInit = ifiFunc;
1636 tokenScript.AddVarEntry (ifiFunc);
1637
1638 /*
1639 * Create a function $staticfieldnit to hold all explicit
1640 * static field initializations.
1641 */
1642 TokenDeclVar sfiFunc = new TokenDeclVar (tokdeclcl, null, tokenScript);
1643 sfiFunc.name = new TokenName (sfiFunc, "$staticfieldinit");
1644 sfiFunc.retType = new TokenTypeVoid (sfiFunc);
1645 sfiFunc.argDecl = new TokenArgDecl (sfiFunc);
1646 sfiFunc.sdtClass = tokdeclcl;
1647 sfiFunc.sdtFlags = SDT_PUBLIC | SDT_STATIC | SDT_NEW;
1648 TokenStmtBlock sfiBody = new TokenStmtBlock (sfiFunc);
1649 sfiBody.function = sfiFunc;
1650 sfiFunc.body = sfiBody;
1651 tokdeclcl.staticFieldInit = sfiFunc;
1652 tokenScript.AddVarEntry (sfiFunc);
1653
1654 // process declaration statements until '}'
1655 while (!(token is TokenKwBrcClose)) {
1656 if (token is TokenKwSemi) {
1657 token = token.nextToken;
1658 continue;
1659 }
1660
1661 /*
1662 * Check for all qualifiers.
1663 * typedef has an implied 'public' qualifier.
1664 */
1665 flags = SDT_PUBLIC;
1666 if (!(token is TokenDeclSDTypeTypedef)) {
1667 flags = ParseQualifierFlags (ref token);
1668 }
1669
1670 /*
1671 * Parse nested script-defined type definitions.
1672 */
1673 if (ParseDeclSDTypes (ref token, tokdeclcl, flags)) continue;
1674
1675 /*
1676 * constant <name> = <rval> ;
1677 */
1678 if (token is TokenKwConst) {
1679 if ((flags & (SDT_ABSTRACT | SDT_NEW | SDT_OVERRIDE | SDT_VIRTUAL)) != 0) {
1680 ErrorMsg (token, "cannot have abstract, new, override or virtual field");
1681 }
1682 TokenDeclVar var = ParseDeclVar (ref token, null);
1683 if (var != null) {
1684 var.sdtClass = tokdeclcl;
1685 var.sdtFlags = flags | SDT_STATIC;
1686 }
1687 continue;
1688 }
1689
1690 /*
1691 * <type> <name> ;
1692 * <type> <name> = <rval> ;
1693 */
1694 if ((token is TokenType) &&
1695 (token.nextToken is TokenName) &&
1696 ((token.nextToken.nextToken is TokenKwSemi) ||
1697 (token.nextToken.nextToken is TokenKwAssign))) {
1698 if ((flags & (SDT_ABSTRACT | SDT_FINAL | SDT_NEW | SDT_OVERRIDE | SDT_VIRTUAL)) != 0) {
1699 ErrorMsg (token, "cannot have abstract, final, new, override or virtual field");
1700 }
1701 TokenDeclVar var = ParseDeclVar (ref token, ifiFunc);
1702 if (var != null) {
1703 var.sdtClass = tokdeclcl;
1704 var.sdtFlags = flags;
1705 if ((flags & SDT_STATIC) != 0) {
1706 // <type>.<name> = <init>;
1707 TokenLValSField left = new TokenLValSField (var.init);
1708 left.baseType = tokdeclcl.MakeRefToken (var);
1709 left.fieldName = var.name;
1710 DoVarInit (sfiFunc, left, var.init);
1711 } else if (var.init != null) {
1712 // this.<name> = <init>;
1713 TokenLValIField left = new TokenLValIField (var.init);
1714 left.baseRVal = new TokenRValThis (var.init, tokdeclcl);
1715 left.fieldName = var.name;
1716 DoVarInit (ifiFunc, left, var.init);
1717 }
1718 }
1719 continue;
1720 }
1721
1722 /*
1723 * <type> <name> [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
1724 * <type> '[' ... ']' [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
1725 */
1726 bool prop = (token is TokenType) &&
1727 (token.nextToken is TokenName) &&
1728 (token.nextToken.nextToken is TokenKwBrcOpen ||
1729 token.nextToken.nextToken is TokenKwColon);
1730 prop |= (token is TokenType) && (token.nextToken is TokenKwBrkOpen);
1731 if (prop) {
1732 TokenDeclVar var = ParseProperty (ref token, (flags & SDT_ABSTRACT) != 0, true);
1733 if (var != null) {
1734 var.sdtClass = tokdeclcl;
1735 var.sdtFlags = flags;
1736 if (var.getProp != null) {
1737 var.getProp.sdtClass = tokdeclcl;
1738 var.getProp.sdtFlags = flags;
1739 }
1740 if (var.setProp != null) {
1741 var.setProp.sdtClass = tokdeclcl;
1742 var.setProp.sdtFlags = flags;
1743 }
1744 }
1745 continue;
1746 }
1747
1748 /*
1749 * 'constructor' '(' arglist ')' [ ':' [ 'base' ] '(' baseconstructorcall ')' ] '{' body '}'
1750 */
1751 if (token is TokenKwConstructor) {
1752 ParseSDTClassCtorDecl (ref token, flags, tokdeclcl);
1753 haveExplicitConstructor = true;
1754 continue;
1755 }
1756
1757 /*
1758 * <type> <name> <funcargs> <funcbody>
1759 * method with explicit return type
1760 */
1761 if (token is TokenType) {
1762 ParseSDTClassMethodDecl (ref token, flags, tokdeclcl);
1763 continue;
1764 }
1765
1766 /*
1767 * <name> <funcargs> <funcbody>
1768 * method returning void
1769 */
1770 if ((token is TokenName) || ((token is TokenKw) && ((TokenKw)token).sdtClassOp)) {
1771 ParseSDTClassMethodDecl (ref token, flags, tokdeclcl);
1772 continue;
1773 }
1774
1775 /*
1776 * That's all we support in a class declaration.
1777 */
1778 ErrorMsg (token, "expecting field or method declaration");
1779 token = SkipPastSemi (token);
1780 }
1781
1782 /*
1783 * If script didn't specify any constructor, create a default no-argument one.
1784 */
1785 if (!haveExplicitConstructor) {
1786 TokenDeclVar tokenDeclFunc = new TokenDeclVar (token, null, tokenScript);
1787 tokenDeclFunc.name = new TokenName (token, "$ctor");
1788 tokenDeclFunc.retType = new TokenTypeVoid (token);
1789 tokenDeclFunc.argDecl = new TokenArgDecl (token);
1790 tokenDeclFunc.sdtClass = tokdeclcl;
1791 tokenDeclFunc.sdtFlags = SDT_PUBLIC | SDT_NEW;
1792 tokenDeclFunc.body = new TokenStmtBlock (token);
1793 tokenDeclFunc.body.function = tokenDeclFunc;
1794
1795 if (tokdeclcl.extends != null) {
1796 SetUpDefaultBaseCtorCall (tokenDeclFunc);
1797 } else {
1798 // default constructor that doesn't do anything is trivial
1799 tokenDeclFunc.triviality = Triviality.trivial;
1800 }
1801
1802 tokenScript.AddVarEntry (tokenDeclFunc);
1803 }
1804
1805 /*
1806 * Skip over the closing brace and pop corresponding var frame.
1807 */
1808 token = token.nextToken;
1809 tokenScript.PopVarFrame ();
1810 ret:
1811 currentDeclSDType = saveCurSDType;
1812 }
1813
1814 /**
1815 * @brief Parse out abstract/override/private/protected/public/static/virtual keywords.
1816 * @param token = first token to evaluate
1817 * @returns flags found; token = unprocessed token
1818 */
1819 private Dictionary<uint, Token> foundFlags = new Dictionary<uint, Token> ();
1820 private uint ParseQualifierFlags (ref Token token)
1821 {
1822 foundFlags.Clear ();
1823 while (true) {
1824 if (token is TokenKwPrivate) {
1825 token = AddQualifierFlag (token, SDT_PRIVATE, SDT_PROTECTED | SDT_PUBLIC);
1826 continue;
1827 }
1828 if (token is TokenKwProtected) {
1829 token = AddQualifierFlag (token, SDT_PROTECTED, SDT_PRIVATE | SDT_PUBLIC);
1830 continue;
1831 }
1832 if (token is TokenKwPublic) {
1833 token = AddQualifierFlag (token, SDT_PUBLIC, SDT_PRIVATE | SDT_PROTECTED);
1834 continue;
1835 }
1836
1837 if (token is TokenKwAbstract) {
1838 token = AddQualifierFlag (token, SDT_ABSTRACT, SDT_FINAL | SDT_STATIC | SDT_VIRTUAL);
1839 continue;
1840 }
1841 if (token is TokenKwFinal) {
1842 token = AddQualifierFlag (token, SDT_FINAL, SDT_ABSTRACT | SDT_VIRTUAL);
1843 continue;
1844 }
1845 if (token is TokenKwNew) {
1846 token = AddQualifierFlag (token, SDT_NEW, SDT_OVERRIDE);
1847 continue;
1848 }
1849 if (token is TokenKwOverride) {
1850 token = AddQualifierFlag (token, SDT_OVERRIDE, SDT_NEW | SDT_STATIC);
1851 continue;
1852 }
1853 if (token is TokenKwStatic) {
1854 token = AddQualifierFlag (token, SDT_STATIC, SDT_ABSTRACT | SDT_OVERRIDE | SDT_VIRTUAL);
1855 continue;
1856 }
1857 if (token is TokenKwVirtual) {
1858 token = AddQualifierFlag (token, SDT_VIRTUAL, SDT_ABSTRACT | SDT_STATIC);
1859 continue;
1860 }
1861 break;
1862 }
1863
1864 uint flags = 0;
1865 foreach (uint flag in foundFlags.Keys) flags |= flag;
1866 if ((flags & (SDT_PRIVATE | SDT_PROTECTED | SDT_PUBLIC)) == 0) {
1867 ErrorMsg (token, "must specify exactly one of private, protected or public");
1868 }
1869 return flags;
1870 }
1871 private Token AddQualifierFlag (Token token, uint add, uint confs)
1872 {
1873 while (confs != 0) {
1874 uint conf = (uint)(confs & -confs);
1875 Token confToken;
1876 if (foundFlags.TryGetValue (conf, out confToken)) {
1877 ErrorMsg (token, "conflicts with " + confToken.ToString ());
1878 }
1879 confs -= conf;
1880 }
1881 foundFlags[add] = token;
1882 return token.nextToken;
1883 }
1884
1885 /**
1886 * @brief Parse a property declaration.
1887 * @param token = points to the property type token on entry
1888 * points just past the closing brace on return
1889 * @param abs = true: property is abstract
1890 * false: property is concrete
1891 * @param imp = allow implemented interface specs
1892 * @returns null: parse failure
1893 * else: property
1894 *
1895 * <type> <name> [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
1896 * <type> '[' ... ']' [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
1897 */
1898 private TokenDeclVar ParseProperty (ref Token token, bool abs, bool imp)
1899 {
1900 /*
1901 * Parse out the property's type and name.
1902 * <type> <name>
1903 */
1904 TokenType type = (TokenType)token;
1905 TokenName name;
1906 TokenArgDecl args;
1907 Token argTokens = null;
1908 token = token.nextToken;
1909 if (token is TokenKwBrkOpen) {
1910 argTokens = token;
1911 name = new TokenName (token, "$idxprop");
1912 args = ParseFuncArgs (ref token, typeof (TokenKwBrkClose));
1913 } else {
1914 name = (TokenName)token;
1915 token = token.nextToken;
1916 args = new TokenArgDecl (token);
1917 }
1918
1919 /*
1920 * Maybe it claims to implement some interface properties.
1921 * [ ':' <ifacetype>[.<propname>] ',' ... ]
1922 */
1923 TokenIntfImpl implements = null;
1924 if (token is TokenKwColon) {
1925 implements = ParseImplements (ref token, name);
1926 if (implements == null) return null;
1927 if (!imp) {
1928 ErrorMsg (token, "cannot implement interface property");
1929 }
1930 }
1931
1932 /*
1933 * Should have an opening brace.
1934 */
1935 if (!(token is TokenKwBrcOpen)) {
1936 ErrorMsg (token, "expect { to open property definition");
1937 token = SkipPastSemi (token);
1938 return null;
1939 }
1940 token = token.nextToken;
1941
1942 /*
1943 * Parse out the getter and/or setter.
1944 * 'get' { <body> | ';' }
1945 * 'set' { <body> | ';' }
1946 */
1947 TokenDeclVar getFunc = null;
1948 TokenDeclVar setFunc = null;
1949 while (!(token is TokenKwBrcClose)) {
1950
1951 /*
1952 * Maybe create a getter function.
1953 */
1954 if (token is TokenKwGet) {
1955 getFunc = new TokenDeclVar (token, null, tokenScript);
1956 getFunc.name = new TokenName (token, name.val + "$get");
1957 getFunc.retType = type;
1958 getFunc.argDecl = args;
1959 getFunc.implements = MakePropertyImplements (implements, "$get");
1960
1961 token = token.nextToken;
1962 if (!ParseFunctionBody (ref token, getFunc, abs)) {
1963 getFunc = null;
1964 } else if (!tokenScript.AddVarEntry (getFunc)) {
1965 ErrorMsg (getFunc, "duplicate getter");
1966 }
1967 continue;
1968 }
1969
1970 /*
1971 * Maybe create a setter function.
1972 */
1973 if (token is TokenKwSet) {
1974 TokenArgDecl argDecl = args;
1975 if (getFunc != null) {
1976 argDecl = (argTokens == null) ? new TokenArgDecl (token) :
1977 ParseFuncArgs (ref argTokens, typeof (TokenKwBrkClose));
1978 }
1979 argDecl.AddArg (type, new TokenName (token, "value"));
1980
1981 setFunc = new TokenDeclVar (token, null, tokenScript);
1982 setFunc.name = new TokenName (token, name.val + "$set");
1983 setFunc.retType = new TokenTypeVoid (token);
1984 setFunc.argDecl = argDecl;
1985 setFunc.implements = MakePropertyImplements (implements, "$set");
1986
1987 token = token.nextToken;
1988 if (!ParseFunctionBody (ref token, setFunc, abs)) {
1989 setFunc = null;
1990 } else if (!tokenScript.AddVarEntry (setFunc)) {
1991 ErrorMsg (setFunc, "duplicate setter");
1992 }
1993 continue;
1994 }
1995
1996 ErrorMsg (token, "expecting get or set");
1997 token = SkipPastSemi (token);
1998 return null;
1999 }
2000 token = token.nextToken;
2001
2002 if ((getFunc == null) && (setFunc == null)) {
2003 ErrorMsg (name, "must specify at least one of get, set");
2004 return null;
2005 }
2006
2007 /*
2008 * Set up a variable for the property.
2009 */
2010 TokenDeclVar tokenDeclVar = new TokenDeclVar (name, null, tokenScript);
2011 tokenDeclVar.type = type;
2012 tokenDeclVar.name = name;
2013 tokenDeclVar.getProp = getFunc;
2014 tokenDeclVar.setProp = setFunc;
2015
2016 /*
2017 * Can't be same name already in block.
2018 */
2019 if (!tokenScript.AddVarEntry (tokenDeclVar)) {
2020 ErrorMsg (tokenDeclVar, "duplicate member " + name.val);
2021 return null;
2022 }
2023 return tokenDeclVar;
2024 }
2025
2026 /**
2027 * @brief Given a list of implemented interface methods, create a similar list with suffix added to all the names
2028 * @param implements = original list of implemented interface methods
2029 * @param suffix = string to be added to end of implemented interface method names
2030 * @returns list similar to implements with suffix added to end of implemented interface method names
2031 */
2032 private TokenIntfImpl MakePropertyImplements (TokenIntfImpl implements, string suffix)
2033 {
2034 TokenIntfImpl gsimpls = null;
2035 for (TokenIntfImpl impl = implements; impl != null; impl = (TokenIntfImpl)impl.nextToken) {
2036 TokenIntfImpl gsimpl = new TokenIntfImpl (impl.intfType,
2037 new TokenName (impl.methName, impl.methName.val + suffix));
2038 gsimpl.nextToken = gsimpls;
2039 gsimpls = gsimpl;
2040 }
2041 return gsimpls;
2042 }
2043
2044 /**
2045 * @brief Parse a constructor definition for a script-defined type class.
2046 * @param token = points to 'constructor' keyword
2047 * @param flags = abstract/override/static/virtual flags
2048 * @param tokdeclcl = which script-defined type class this method is in
2049 * @returns with method parsed and cataloged (or error message(s) printed)
2050 */
2051 private void ParseSDTClassCtorDecl (ref Token token, uint flags, TokenDeclSDTypeClass tokdeclcl)
2052 {
2053 if ((flags & (SDT_ABSTRACT | SDT_OVERRIDE | SDT_STATIC | SDT_VIRTUAL)) != 0) {
2054 ErrorMsg (token, "cannot have abstract, override, static or virtual constructor");
2055 }
2056
2057 TokenDeclVar tokenDeclFunc = new TokenDeclVar (token, null, tokenScript);
2058 tokenDeclFunc.name = new TokenName (tokenDeclFunc, "$ctor");
2059 tokenDeclFunc.retType = new TokenTypeVoid (token);
2060 tokenDeclFunc.sdtClass = tokdeclcl;
2061 tokenDeclFunc.sdtFlags = flags | SDT_NEW;
2062
2063 token = token.nextToken;
2064 if (!(token is TokenKwParOpen)) {
2065 ErrorMsg (token, "expecting ( for constructor argument list");
2066 token = SkipPastSemi (token);
2067 return;
2068 }
2069 tokenDeclFunc.argDecl = ParseFuncArgs (ref token, typeof (TokenKwParClose));
2070 if (tokenDeclFunc.argDecl == null) return;
2071
2072 TokenDeclVar saveDeclFunc = currentDeclFunc;
2073 currentDeclFunc = tokenDeclFunc;
2074 tokenScript.PushVarFrame (tokenDeclFunc.argDecl.varDict);
2075 try {
2076 /*
2077 * Set up reference to base constructor.
2078 */
2079 TokenLValBaseField baseCtor = new TokenLValBaseField (token,
2080 new TokenName (token, "$ctor"),
2081 tokdeclcl);
2082
2083 /*
2084 * Parse any base constructor call as if it were the first statement of the
2085 * constructor itself.
2086 */
2087 if (token is TokenKwColon) {
2088 token = token.nextToken;
2089 if (token is TokenKwBase) {
2090 token = token.nextToken;
2091 }
2092 if (!(token is TokenKwParOpen)) {
2093 ErrorMsg (token, "expecting ( for base constructor call arguments");
2094 token = SkipPastSemi (token);
2095 return;
2096 }
2097 TokenRValCall rvc = ParseRValCall (ref token, baseCtor);
2098 if (rvc == null) return;
2099 if (tokdeclcl.extends != null) {
2100 tokenDeclFunc.baseCtorCall = rvc;
2101 tokenDeclFunc.unknownTrivialityCalls.AddLast (rvc);
2102 } else {
2103 ErrorMsg (rvc, "base constructor call cannot be specified if not extending anything");
2104 }
2105 } else if (tokdeclcl.extends != null) {
2106
2107 /*
2108 * Caller didn't specify a constructor but we are extending, so we will
2109 * call the extended class's default constructor.
2110 */
2111 SetUpDefaultBaseCtorCall (tokenDeclFunc);
2112 }
2113
2114 /*
2115 * Parse the constructor body.
2116 */
2117 tokenDeclFunc.body = ParseStmtBlock (ref token);
2118 if (tokenDeclFunc.body == null) return;
2119 if (tokenDeclFunc.argDecl == null) return;
2120 } finally {
2121 tokenScript.PopVarFrame ();
2122 currentDeclFunc = saveDeclFunc;
2123 }
2124
2125 /*
2126 * Add to list of methods defined by this class.
2127 * It has the name "$ctor(argsig)".
2128 */
2129 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
2130 ErrorMsg (tokenDeclFunc, "duplicate constructor definition");
2131 }
2132 }
2133
2134 /**
2135 * @brief Set up a call from a constructor to its default base constructor.
2136 */
2137 private void SetUpDefaultBaseCtorCall (TokenDeclVar thisCtor)
2138 {
2139 TokenLValBaseField baseCtor = new TokenLValBaseField (thisCtor,
2140 new TokenName (thisCtor, "$ctor"),
2141 (TokenDeclSDTypeClass)thisCtor.sdtClass);
2142 TokenRValCall rvc = new TokenRValCall (thisCtor);
2143 rvc.meth = baseCtor;
2144 thisCtor.baseCtorCall = rvc;
2145 thisCtor.unknownTrivialityCalls.AddLast (rvc);
2146 }
2147
2148 /**
2149 * @brief Parse a method definition for a script-defined type class.
2150 * @param token = points to return type (or method name for implicit return type of void)
2151 * @param flags = abstract/override/static/virtual flags
2152 * @param tokdeclcl = which script-defined type class this method is in
2153 * @returns with method parsed and cataloged (or error message(s) printed)
2154 */
2155 private void ParseSDTClassMethodDecl (ref Token token, uint flags, TokenDeclSDTypeClass tokdeclcl)
2156 {
2157 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token,
2158 (flags & SDT_ABSTRACT) != 0,
2159 (flags & SDT_STATIC) == 0,
2160 (flags & SDT_STATIC) == 0);
2161 if (tokenDeclFunc != null) {
2162 tokenDeclFunc.sdtClass = tokdeclcl;
2163 tokenDeclFunc.sdtFlags = flags;
2164 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
2165 string funcNameSig = tokenDeclFunc.funcNameSig.val;
2166 ErrorMsg (tokenDeclFunc.funcNameSig, "duplicate method name " + funcNameSig);
2167 }
2168 }
2169 }
2170
2171 /**
2172 * @brief Parse a delegate declaration statement.
2173 * @param token = points to TokenDeclSDTypeDelegate token on entry
2174 * points just past ';' on return
2175 * @param outerSDType = null: this is a top-level delegate
2176 * else: this delegate is being defined inside this type
2177 * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
2178 */
2179 private void ParseDeclDelegate (ref Token token, TokenDeclSDType outerSDType, uint flags)
2180 {
2181 Token u = token;
2182 TokenDeclSDTypeDelegate tokdecldel;
2183 TokenType retType;
2184
2185 tokdecldel = (TokenDeclSDTypeDelegate)u;
2186 tokdecldel.outerSDType = outerSDType;
2187 tokdecldel.accessLevel = flags;
2188
2189 // first thing following that should be return type
2190 // but we will fill in 'void' if it is missing
2191 u = u.nextToken;
2192 if (u is TokenType) {
2193 retType = (TokenType)u;
2194 u = u.nextToken;
2195 } else {
2196 retType = new TokenTypeVoid (u);
2197 }
2198
2199 // get list of argument types until we see a ')'
2200 List<TokenType> args = new List<TokenType> ();
2201 bool first = true;
2202 do {
2203 if (first) {
2204
2205 // first time should have '(' ')' or '(' <type>
2206 if (!(u is TokenKwParOpen)) {
2207 ErrorMsg (u, "expecting ( after delegate name");
2208 token = SkipPastSemi (token);
2209 return;
2210 }
2211 first = false;
2212 u = u.nextToken;
2213 if (u is TokenKwParClose) break;
2214 } else {
2215
2216 // other times should have ',' <type>
2217 if (!(u is TokenKwComma)) {
2218 ErrorMsg (u, "expecting , separating arg types");
2219 token = SkipPastSemi (token);
2220 return;
2221 }
2222 u = u.nextToken;
2223 }
2224 if (!(u is TokenType)) {
2225 ErrorMsg (u, "expecting argument type");
2226 token = SkipPastSemi (token);
2227 return;
2228 }
2229 args.Add ((TokenType)u);
2230 u = u.nextToken;
2231
2232 // they can put in a dummy name that we toss out
2233 if (u is TokenName) u = u.nextToken;
2234
2235 // scanning ends on a ')'
2236 } while (!(u is TokenKwParClose));
2237
2238 // fill in the return type and argment type array
2239 tokdecldel.SetRetArgTypes (retType, args.ToArray ());
2240
2241 // and finally must have ';' to finish the delegate declaration statement
2242 u = u.nextToken;
2243 if (!(u is TokenKwSemi)) {
2244 ErrorMsg (u, "expecting ; after ) in delegate");
2245 token = SkipPastSemi (token);
2246 return;
2247 }
2248 token = u.nextToken;
2249 }
2250
2251 /**
2252 * @brief Parse an interface declaration.
2253 * @param token = points to TokenDeclSDTypeInterface token on entry
2254 * points just past closing '}' on return
2255 * @param outerSDType = null: this is a top-level interface
2256 * else: this interface is being defined inside this type
2257 * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
2258 */
2259 private void ParseDeclInterface (ref Token token, TokenDeclSDType outerSDType, uint flags)
2260 {
2261 Token u = token;
2262 TokenDeclSDTypeInterface tokdeclin;
2263
2264 tokdeclin = (TokenDeclSDTypeInterface)u;
2265 tokdeclin.outerSDType = outerSDType;
2266 tokdeclin.accessLevel = flags;
2267 u = u.nextToken;
2268
2269 // next can be ':' followed by list of implemented interfaces
2270 if (u is TokenKwColon) {
2271 u = u.nextToken;
2272 while (true) {
2273 if (u is TokenTypeSDTypeInterface) {
2274 TokenDeclSDTypeInterface i = ((TokenTypeSDTypeInterface)u).decl;
2275 if (!tokdeclin.implements.Contains (i)) {
2276 tokdeclin.implements.Add (i);
2277 }
2278 } else {
2279 ErrorMsg (u, "expecting interface name");
2280 if (u is TokenKwBrcOpen) break;
2281 }
2282 u = u.nextToken;
2283 if (!(u is TokenKwComma)) break;
2284 u = u.nextToken;
2285 }
2286 }
2287
2288 // next must be '{' to open interface declaration body
2289 if (!(u is TokenKwBrcOpen)) {
2290 ErrorMsg (u, "expecting { to open interface declaration body");
2291 token = SkipPastSemi (token);
2292 return;
2293 }
2294 token = u.nextToken;
2295
2296 // start a var definition frame to collect the interface members
2297 tokenScript.PushVarFrame (false);
2298 tokdeclin.methsNProps = tokenScript.variablesStack;
2299
2300 // process declaration statements until '}'
2301 while (!(token is TokenKwBrcClose)) {
2302 if (token is TokenKwSemi) {
2303 token = token.nextToken;
2304 continue;
2305 }
2306
2307 /*
2308 * Parse nested script-defined type definitions.
2309 */
2310 if (ParseDeclSDTypes (ref token, tokdeclin, SDT_PUBLIC)) continue;
2311
2312 /*
2313 * <type> <name> <funcargs> ;
2314 * abstract method with explicit return type
2315 */
2316 if ((token is TokenType) &&
2317 (token.nextToken is TokenName) &&
2318 (token.nextToken.nextToken is TokenKwParOpen)) {
2319 Token name = token.nextToken;
2320 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, true, false, false);
2321 if (tokenDeclFunc == null) continue;
2322 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
2323 ErrorMsg (name, "duplicate method name");
2324 continue;
2325 }
2326 continue;
2327 }
2328
2329 /*
2330 * <name> <funcargs> ;
2331 * abstract method returning void
2332 */
2333 if ((token is TokenName) &&
2334 (token.nextToken is TokenKwParOpen)) {
2335 Token name = token;
2336 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, true, false, false);
2337 if (tokenDeclFunc == null) continue;
2338 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
2339 ErrorMsg (name, "duplicate method name");
2340 }
2341 continue;
2342 }
2343
2344 /*
2345 * <type> <name> { [ get ; ] [ set ; ] }
2346 * <type> '[' ... ']' { [ get ; ] [ set ; ] }
2347 * abstract property
2348 */
2349 bool prop = (token is TokenType) &&
2350 (token.nextToken is TokenName) &&
2351 (token.nextToken.nextToken is TokenKwBrcOpen ||
2352 token.nextToken.nextToken is TokenKwColon);
2353 prop |= (token is TokenType) && (token.nextToken is TokenKwBrkOpen);
2354 if (prop) {
2355 ParseProperty (ref token, true, false);
2356 continue;
2357 }
2358
2359 /*
2360 * That's all we support in an interface declaration.
2361 */
2362 ErrorMsg (token, "expecting method or property prototype");
2363 token = SkipPastSemi (token);
2364 }
2365
2366 /*
2367 * Skip over the closing brace and pop the corresponding var frame.
2368 */
2369 token = token.nextToken;
2370 tokenScript.PopVarFrame ();
2371 }
2372
2373 /**
2374 * @brief parse state body (including all its event handlers)
2375 * @param token = points to TokenKwBrcOpen
2376 * @returns null: state body parse error
2377 * else: token representing state
2378 * token = points past close brace
2379 */
2380 private TokenStateBody ParseStateBody (ref Token token)
2381 {
2382 TokenStateBody tokenStateBody = new TokenStateBody (token);
2383
2384 if (!(token is TokenKwBrcOpen)) {
2385 ErrorMsg (token, "expecting { at beg of state");
2386 token = SkipPastSemi (token);
2387 return null;
2388 }
2389
2390 token = token.nextToken;
2391 while (!(token is TokenKwBrcClose)) {
2392 if (token is TokenEnd) {
2393 ErrorMsg (tokenStateBody, "eof parsing state body");
2394 return null;
2395 }
2396 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, false, false, false);
2397 if (tokenDeclFunc == null) return null;
2398 if (!(tokenDeclFunc.retType is TokenTypeVoid)) {
2399 ErrorMsg (tokenDeclFunc.retType, "event handlers don't have return types");
2400 return null;
2401 }
2402 tokenDeclFunc.nextToken = tokenStateBody.eventFuncs;
2403 tokenStateBody.eventFuncs = tokenDeclFunc;
2404 }
2405 token = token.nextToken;
2406 return tokenStateBody;
2407 }
2408
2409 /**
2410 * @brief Parse a function declaration, including its arg list and body
2411 * @param token = points to function return type token (or function name token if return type void)
2412 * @param abs = false: concrete function; true: abstract declaration
2413 * @param imp = allow implemented interface specs
2414 * @param ops = accept operators (==, +, etc) for function name
2415 * @returns null: error parsing function definition
2416 * else: function declaration
2417 * token = advanced just past function, ie, just past the closing brace
2418 */
2419 private TokenDeclVar ParseDeclFunc (ref Token token, bool abs, bool imp, bool ops)
2420 {
2421 TokenType retType;
2422 if (token is TokenType) {
2423 retType = (TokenType)token;
2424 token = token.nextToken;
2425 } else {
2426 retType = new TokenTypeVoid (token);
2427 }
2428
2429 TokenName simpleName;
2430 if ((token is TokenKw) && ((TokenKw)token).sdtClassOp) {
2431 if (!ops) ErrorMsg (token, "operator functions disallowed in static contexts");
2432 simpleName = new TokenName (token, "$op" + token.ToString ());
2433 } else if (!(token is TokenName)) {
2434 ErrorMsg (token, "expecting function name");
2435 token = SkipPastSemi (token);
2436 return null;
2437 } else {
2438 simpleName = (TokenName)token;
2439 }
2440 token = token.nextToken;
2441
2442 return ParseDeclFunc (ref token, abs, imp, retType, simpleName);
2443 }
2444
2445 /**
2446 * @brief Parse a function declaration, including its arg list and body
2447 * This version enters with token pointing to the '(' at beginning of arg list
2448 * @param token = points to the '(' of the arg list
2449 * @param abs = false: concrete function; true: abstract declaration
2450 * @param imp = allow implemented interface specs
2451 * @param retType = return type (TokenTypeVoid if void, never null)
2452 * @param simpleName = function name without any signature
2453 * @returns null: error parsing remainder of function definition
2454 * else: function declaration
2455 * token = advanced just past function, ie, just past the closing brace
2456 */
2457 private TokenDeclVar ParseDeclFunc (ref Token token, bool abs, bool imp, TokenType retType, TokenName simpleName)
2458 {
2459 TokenDeclVar tokenDeclFunc = new TokenDeclVar (simpleName, null, tokenScript);
2460 tokenDeclFunc.name = simpleName;
2461 tokenDeclFunc.retType = retType;
2462 tokenDeclFunc.argDecl = ParseFuncArgs (ref token, typeof (TokenKwParClose));
2463 if (tokenDeclFunc.argDecl == null) return null;
2464
2465 if (token is TokenKwColon) {
2466 tokenDeclFunc.implements = ParseImplements (ref token, simpleName);
2467 if (tokenDeclFunc.implements == null) return null;
2468 if (!imp) {
2469 ErrorMsg (tokenDeclFunc.implements, "cannot implement interface method");
2470 tokenDeclFunc.implements = null;
2471 }
2472 }
2473
2474 if (!ParseFunctionBody (ref token, tokenDeclFunc, abs)) return null;
2475 if (tokenDeclFunc.argDecl == null) return null;
2476 return tokenDeclFunc;
2477 }
2478
2479 /**
2480 * @brief Parse interface implementation list.
2481 * @param token = points to ':' on entry
2482 * points just past list on return
2483 * @param simpleName = simple name (no arg signature) of method/property that
2484 * is implementing the interface method/property
2485 * @returns list of implemented interface methods/properties
2486 */
2487 private TokenIntfImpl ParseImplements (ref Token token, TokenName simpleName)
2488 {
2489 TokenIntfImpl implements = null;
2490 do {
2491 token = token.nextToken;
2492 if (!(token is TokenTypeSDTypeInterface)) {
2493 ErrorMsg (token, "expecting interface type");
2494 token = SkipPastSemi (token);
2495 return null;
2496 }
2497 TokenTypeSDTypeInterface intfType = (TokenTypeSDTypeInterface)token;
2498 token = token.nextToken;
2499 TokenName methName = simpleName;
2500 if ((token is TokenKwDot) && (token.nextToken is TokenName)) {
2501 methName = (TokenName)token.nextToken;
2502 token = token.nextToken.nextToken;
2503 }
2504 TokenIntfImpl intfImpl = new TokenIntfImpl (intfType, methName);
2505 intfImpl.nextToken = implements;
2506 implements = intfImpl;
2507 } while (token is TokenKwComma);
2508 return implements;
2509 }
2510
2511
2512 /**
2513 * @brief Parse function declaration's body
2514 * @param token = points to body, ie, ';' or '{'
2515 * @param tokenDeclFunc = function being declared
2516 * @param abs = false: concrete function; true: abstract declaration
2517 * @returns whether or not the function definition parsed correctly
2518 */
2519 private bool ParseFunctionBody (ref Token token, TokenDeclVar tokenDeclFunc, bool abs)
2520 {
2521 if (token is TokenKwSemi) {
2522 if (!abs) {
2523 ErrorMsg (token, "concrete function must have body");
2524 token = SkipPastSemi (token);
2525 return false;
2526 }
2527 token = token.nextToken;
2528 return true;
2529 }
2530
2531 /*
2532 * Declare this function as being the one currently being processed
2533 * for anything that cares. We also start a variable frame that
2534 * includes all the declared parameters.
2535 */
2536 TokenDeclVar saveDeclFunc = currentDeclFunc;
2537 currentDeclFunc = tokenDeclFunc;
2538 tokenScript.PushVarFrame (tokenDeclFunc.argDecl.varDict);
2539
2540 /*
2541 * Now parse the function statement block.
2542 */
2543 tokenDeclFunc.body = ParseStmtBlock (ref token);
2544
2545 /*
2546 * Pop the var frame that contains the arguments.
2547 */
2548 tokenScript.PopVarFrame ();
2549 currentDeclFunc = saveDeclFunc;
2550
2551 /*
2552 * Check final errors.
2553 */
2554 if (tokenDeclFunc.body == null) return false;
2555 if (abs) {
2556 ErrorMsg (tokenDeclFunc.body, "abstract function must not have body");
2557 tokenDeclFunc.body = null;
2558 return false;
2559 }
2560 return true;
2561 }
2562
2563
2564 /**
2565 * @brief Parse statement
2566 * @param token = first token of statement
2567 * @returns null: parse error
2568 * else: token representing whole statement
2569 * token = points past statement
2570 */
2571 private TokenStmt ParseStmt (ref Token token)
2572 {
2573 /*
2574 * Statements that begin with a specific keyword.
2575 */
2576 if (token is TokenKwAt) return ParseStmtLabel (ref token);
2577 if (token is TokenKwBrcOpen) return ParseStmtBlock (ref token);
2578 if (token is TokenKwBreak) return ParseStmtBreak (ref token);
2579 if (token is TokenKwCont) return ParseStmtCont (ref token);
2580 if (token is TokenKwDo) return ParseStmtDo (ref token);
2581 if (token is TokenKwFor) return ParseStmtFor (ref token);
2582 if (token is TokenKwForEach) return ParseStmtForEach (ref token);
2583 if (token is TokenKwIf) return ParseStmtIf (ref token);
2584 if (token is TokenKwJump) return ParseStmtJump (ref token);
2585 if (token is TokenKwRet) return ParseStmtRet (ref token);
2586 if (token is TokenKwSemi) return ParseStmtNull (ref token);
2587 if (token is TokenKwState) return ParseStmtState (ref token);
2588 if (token is TokenKwSwitch) return ParseStmtSwitch (ref token);
2589 if (token is TokenKwThrow) return ParseStmtThrow (ref token);
2590 if (token is TokenKwTry) return ParseStmtTry (ref token);
2591 if (token is TokenKwWhile) return ParseStmtWhile (ref token);
2592
2593 /*
2594 * Try to parse anything else as an expression, possibly calling
2595 * something and/or writing to a variable.
2596 */
2597 TokenRVal tokenRVal = ParseRVal (ref token, semiOnly);
2598 if (tokenRVal != null) {
2599 TokenStmtRVal tokenStmtRVal = new TokenStmtRVal (tokenRVal);
2600 tokenStmtRVal.rVal = tokenRVal;
2601 return tokenStmtRVal;
2602 }
2603
2604 /*
2605 * Who knows what it is...
2606 */
2607 ErrorMsg (token, "unknown statement");
2608 token = SkipPastSemi (token);
2609 return null;
2610 }
2611
2612 /**
2613 * @brief parse a statement block, ie, group of statements between braces
2614 * @param token = points to { token
2615 * @returns null: error parsing
2616 * else: statements bundled in this token
2617 * token = advanced just past the } token
2618 */
2619 private TokenStmtBlock ParseStmtBlock (ref Token token)
2620 {
2621 if (!(token is TokenKwBrcOpen)) {
2622 ErrorMsg (token, "statement block body must begin with a {");
2623 token = SkipPastSemi (token);
2624 return null;
2625 }
2626 TokenStmtBlock tokenStmtBlock = new TokenStmtBlock (token);
2627 tokenStmtBlock.function = currentDeclFunc;
2628 tokenStmtBlock.outerStmtBlock = currentStmtBlock;
2629 currentStmtBlock = tokenStmtBlock;
2630 VarDict outerVariablesStack = tokenScript.variablesStack;
2631 try {
2632 Token prevStmt = null;
2633 token = token.nextToken;
2634 while (!(token is TokenKwBrcClose)) {
2635 if (token is TokenEnd) {
2636 ErrorMsg (tokenStmtBlock, "missing }");
2637 return null;
2638 }
2639 Token thisStmt;
2640 if (((token is TokenType) && (token.nextToken is TokenName)) ||
2641 (token is TokenKwConst)) {
2642 thisStmt = ParseDeclVar (ref token, null);
2643 } else {
2644 thisStmt = ParseStmt (ref token);
2645 }
2646 if (thisStmt == null) return null;
2647 if (prevStmt == null) tokenStmtBlock.statements = thisStmt;
2648 else prevStmt.nextToken = thisStmt;
2649 prevStmt = thisStmt;
2650 }
2651 token = token.nextToken;
2652 } finally {
2653 tokenScript.variablesStack = outerVariablesStack;
2654 currentStmtBlock = tokenStmtBlock.outerStmtBlock;
2655 }
2656 return tokenStmtBlock;
2657 }
2658
2659 /**
2660 * @brief parse a 'break' statement
2661 * @param token = points to break keyword token
2662 * @returns null: error parsing
2663 * else: statements bundled in this token
2664 * token = advanced just past the ; token
2665 */
2666 private TokenStmtBreak ParseStmtBreak (ref Token token)
2667 {
2668 TokenStmtBreak tokenStmtBreak = new TokenStmtBreak (token);
2669 token = token.nextToken;
2670 if (!(token is TokenKwSemi)) {
2671 ErrorMsg (token, "expecting ;");
2672 token = SkipPastSemi (token);
2673 return null;
2674 }
2675 token = token.nextToken;
2676 return tokenStmtBreak;
2677 }
2678
2679 /**
2680 * @brief parse a 'continue' statement
2681 * @param token = points to continue keyword token
2682 * @returns null: error parsing
2683 * else: statements bundled in this token
2684 * token = advanced just past the ; token
2685 */
2686 private TokenStmtCont ParseStmtCont (ref Token token)
2687 {
2688 TokenStmtCont tokenStmtCont = new TokenStmtCont (token);
2689 token = token.nextToken;
2690 if (!(token is TokenKwSemi)) {
2691 ErrorMsg (token, "expecting ;");
2692 token = SkipPastSemi (token);
2693 return null;
2694 }
2695 token = token.nextToken;
2696 return tokenStmtCont;
2697 }
2698
2699 /**
2700 * @brief parse a 'do' statement
2701 * @params token = points to 'do' keyword token
2702 * @returns null: parse error
2703 * else: pointer to token encapsulating the do statement, including body
2704 * token = advanced just past the body statement
2705 */
2706 private TokenStmtDo ParseStmtDo (ref Token token)
2707 {
2708 currentDeclFunc.triviality = Triviality.complex;
2709 TokenStmtDo tokenStmtDo = new TokenStmtDo (token);
2710 token = token.nextToken;
2711 tokenStmtDo.bodyStmt = ParseStmt (ref token);
2712 if (tokenStmtDo.bodyStmt == null) return null;
2713 if (!(token is TokenKwWhile)) {
2714 ErrorMsg (token, "expecting while clause");
2715 return null;
2716 }
2717 token = token.nextToken;
2718 tokenStmtDo.testRVal = ParseRValParen (ref token);
2719 if (tokenStmtDo.testRVal == null) return null;
2720 if (!(token is TokenKwSemi)) {
2721 ErrorMsg (token, "while clause must terminate on semicolon");
2722 token = SkipPastSemi (token);
2723 return null;
2724 }
2725 token = token.nextToken;
2726 return tokenStmtDo;
2727 }
2728
2729 /**
2730 * @brief parse a for statement
2731 * @param token = points to 'for' keyword token
2732 * @returns null: parse error
2733 * else: pointer to encapsulated for statement token
2734 * token = advanced just past for body statement
2735 */
2736 private TokenStmt ParseStmtFor (ref Token token)
2737 {
2738 currentDeclFunc.triviality = Triviality.complex;
2739
2740 /*
2741 * Create encapsulating token and skip past 'for ('
2742 */
2743 TokenStmtFor tokenStmtFor = new TokenStmtFor (token);
2744 token = token.nextToken;
2745 if (!(token is TokenKwParOpen)) {
2746 ErrorMsg (token, "for must be followed by (");
2747 return null;
2748 }
2749 token = token.nextToken;
2750
2751 /*
2752 * If a plain for, ie, not declaring a variable, it's straightforward.
2753 */
2754 if (!(token is TokenType)) {
2755 tokenStmtFor.initStmt = ParseStmt (ref token);
2756 if (tokenStmtFor.initStmt == null) return null;
2757 return ParseStmtFor2 (tokenStmtFor, ref token) ? tokenStmtFor : null;
2758 }
2759
2760 /*
2761 * Initialization declares a variable, so encapsulate it in a block so
2762 * variable has scope only in the for statement, including its body.
2763 */
2764 TokenStmtBlock forStmtBlock = new TokenStmtBlock (tokenStmtFor);
2765 forStmtBlock.outerStmtBlock = currentStmtBlock;
2766 forStmtBlock.function = currentDeclFunc;
2767 currentStmtBlock = forStmtBlock;
2768 tokenScript.PushVarFrame (true);
2769
2770 TokenDeclVar tokenDeclVar = ParseDeclVar (ref token, null);
2771 if (tokenDeclVar == null) {
2772 tokenScript.PopVarFrame ();
2773 currentStmtBlock = forStmtBlock.outerStmtBlock;
2774 return null;
2775 }
2776
2777 forStmtBlock.statements = tokenDeclVar;
2778 tokenDeclVar.nextToken = tokenStmtFor;
2779
2780 bool ok = ParseStmtFor2 (tokenStmtFor, ref token);
2781 tokenScript.PopVarFrame ();
2782 currentStmtBlock = forStmtBlock.outerStmtBlock;
2783 return ok ? forStmtBlock : null;
2784 }
2785
2786 /**
2787 * @brief parse rest of 'for' statement starting with the test expression.
2788 * @param tokenStmtFor = token encapsulating the for statement
2789 * @param token = points to test expression
2790 * @returns false: parse error
2791 * true: successful
2792 * token = points just past body statement
2793 */
2794 private bool ParseStmtFor2 (TokenStmtFor tokenStmtFor, ref Token token)
2795 {
2796 if (token is TokenKwSemi) {
2797 token = token.nextToken;
2798 } else {
2799 tokenStmtFor.testRVal = ParseRVal (ref token, semiOnly);
2800 if (tokenStmtFor.testRVal == null) return false;
2801 }
2802 if (token is TokenKwParClose) {
2803 token = token.nextToken;
2804 } else {
2805 tokenStmtFor.incrRVal = ParseRVal (ref token, parCloseOnly);
2806 if (tokenStmtFor.incrRVal == null) return false;
2807 }
2808 tokenStmtFor.bodyStmt = ParseStmt (ref token);
2809 return tokenStmtFor.bodyStmt != null;
2810 }
2811
2812 /**
2813 * @brief parse a foreach statement
2814 * @param token = points to 'foreach' keyword token
2815 * @returns null: parse error
2816 * else: pointer to encapsulated foreach statement token
2817 * token = advanced just past for body statement
2818 */
2819 private TokenStmt ParseStmtForEach (ref Token token)
2820 {
2821 currentDeclFunc.triviality = Triviality.complex;
2822
2823 /*
2824 * Create encapsulating token and skip past 'foreach ('
2825 */
2826 TokenStmtForEach tokenStmtForEach = new TokenStmtForEach (token);
2827 token = token.nextToken;
2828 if (!(token is TokenKwParOpen)) {
2829 ErrorMsg (token, "foreach must be followed by (");
2830 return null;
2831 }
2832 token = token.nextToken;
2833
2834 if (token is TokenName) {
2835 tokenStmtForEach.keyLVal = new TokenLValName ((TokenName)token, tokenScript.variablesStack);
2836 token = token.nextToken;
2837 }
2838 if (!(token is TokenKwComma)) {
2839 ErrorMsg (token, "expecting comma");
2840 token = SkipPastSemi (token);
2841 return null;
2842 }
2843 token = token.nextToken;
2844 if (token is TokenName) {
2845 tokenStmtForEach.valLVal = new TokenLValName ((TokenName)token, tokenScript.variablesStack);
2846 token = token.nextToken;
2847 }
2848 if (!(token is TokenKwIn)) {
2849 ErrorMsg (token, "expecting 'in'");
2850 token = SkipPastSemi (token);
2851 return null;
2852 }
2853 token = token.nextToken;
2854 tokenStmtForEach.arrayRVal = GetOperand (ref token);
2855 if (tokenStmtForEach.arrayRVal == null) return null;
2856 if (!(token is TokenKwParClose)) {
2857 ErrorMsg (token, "expecting )");
2858 token = SkipPastSemi (token);
2859 return null;
2860 }
2861 token = token.nextToken;
2862 tokenStmtForEach.bodyStmt = ParseStmt (ref token);
2863 if (tokenStmtForEach.bodyStmt == null) return null;
2864 return tokenStmtForEach;
2865 }
2866
2867 private TokenStmtIf ParseStmtIf (ref Token token)
2868 {
2869 TokenStmtIf tokenStmtIf = new TokenStmtIf (token);
2870 token = token.nextToken;
2871 tokenStmtIf.testRVal = ParseRValParen (ref token);
2872 if (tokenStmtIf.testRVal == null) return null;
2873 tokenStmtIf.trueStmt = ParseStmt (ref token);
2874 if (tokenStmtIf.trueStmt == null) return null;
2875 if (token is TokenKwElse) {
2876 token = token.nextToken;
2877 tokenStmtIf.elseStmt = ParseStmt (ref token);
2878 if (tokenStmtIf.elseStmt == null) return null;
2879 }
2880 return tokenStmtIf;
2881 }
2882
2883 private TokenStmtJump ParseStmtJump (ref Token token)
2884 {
2885 /*
2886 * Create jump statement token to encapsulate the whole statement.
2887 */
2888 TokenStmtJump tokenStmtJump = new TokenStmtJump (token);
2889 token = token.nextToken;
2890 if (!(token is TokenName) || !(token.nextToken is TokenKwSemi)) {
2891 ErrorMsg (token, "expecting label;");
2892 token = SkipPastSemi (token);
2893 return null;
2894 }
2895 tokenStmtJump.label = (TokenName)token;
2896 token = token.nextToken.nextToken;
2897
2898 /*
2899 * If label is already defined, it means this is a backward (looping)
2900 * jump, so remember the label has backward jump references.
2901 * We also then assume the function is complex, ie, it has a loop.
2902 */
2903 if (currentDeclFunc.labels.ContainsKey (tokenStmtJump.label.val)) {
2904 currentDeclFunc.labels[tokenStmtJump.label.val].hasBkwdRefs = true;
2905 currentDeclFunc.triviality = Triviality.complex;
2906 }
2907
2908 return tokenStmtJump;
2909 }
2910
2911 /**
2912 * @brief parse a jump target label statement
2913 * @param token = points to the '@' token
2914 * @returns null: error parsing
2915 * else: the label
2916 * token = advanced just past the ;
2917 */
2918 private TokenStmtLabel ParseStmtLabel (ref Token token)
2919 {
2920 if (!(token.nextToken is TokenName) ||
2921 !(token.nextToken.nextToken is TokenKwSemi)) {
2922 ErrorMsg (token, "invalid label");
2923 token = SkipPastSemi (token);
2924 return null;
2925 }
2926 TokenStmtLabel stmtLabel = new TokenStmtLabel (token);
2927 stmtLabel.name = (TokenName)token.nextToken;
2928 stmtLabel.block = currentStmtBlock;
2929 if (currentDeclFunc.labels.ContainsKey (stmtLabel.name.val)) {
2930 ErrorMsg (token.nextToken, "duplicate label");
2931 ErrorMsg (currentDeclFunc.labels[stmtLabel.name.val], "previously defined here");
2932 token = SkipPastSemi (token);
2933 return null;
2934 }
2935 currentDeclFunc.labels.Add (stmtLabel.name.val, stmtLabel);
2936 token = token.nextToken.nextToken.nextToken;
2937 return stmtLabel;
2938 }
2939
2940 private TokenStmtNull ParseStmtNull (ref Token token)
2941 {
2942 TokenStmtNull tokenStmtNull = new TokenStmtNull (token);
2943 token = token.nextToken;
2944 return tokenStmtNull;
2945 }
2946
2947 private TokenStmtRet ParseStmtRet (ref Token token)
2948 {
2949 TokenStmtRet tokenStmtRet = new TokenStmtRet (token);
2950 token = token.nextToken;
2951 if (token is TokenKwSemi) {
2952 token = token.nextToken;
2953 } else {
2954 tokenStmtRet.rVal = ParseRVal (ref token, semiOnly);
2955 if (tokenStmtRet.rVal == null) return null;
2956 }
2957 return tokenStmtRet;
2958 }
2959
2960 private TokenStmtSwitch ParseStmtSwitch (ref Token token)
2961 {
2962 TokenStmtSwitch tokenStmtSwitch = new TokenStmtSwitch (token);
2963 token = token.nextToken;
2964 tokenStmtSwitch.testRVal = ParseRValParen (ref token);
2965 if (tokenStmtSwitch.testRVal == null) return null;
2966 if (!(token is TokenKwBrcOpen)) {
2967 ErrorMsg (token, "expecting open brace");
2968 token = SkipPastSemi (token);
2969 return null;
2970 }
2971 token = token.nextToken;
2972 TokenSwitchCase tokenSwitchCase = null;
2973 bool haveComplained = false;
2974 while (!(token is TokenKwBrcClose)) {
2975 if (token is TokenKwCase) {
2976 tokenSwitchCase = new TokenSwitchCase (token);
2977 if (tokenStmtSwitch.lastCase == null) {
2978 tokenStmtSwitch.cases = tokenSwitchCase;
2979 } else {
2980 tokenStmtSwitch.lastCase.nextCase = tokenSwitchCase;
2981 }
2982 tokenStmtSwitch.lastCase = tokenSwitchCase;
2983
2984 token = token.nextToken;
2985 tokenSwitchCase.rVal1 = ParseRVal (ref token, colonOrDotDotDot);
2986 if (tokenSwitchCase.rVal1 == null) return null;
2987 if (token is TokenKwDotDotDot) {
2988 token = token.nextToken;
2989 tokenSwitchCase.rVal2 = ParseRVal (ref token, colonOnly);
2990 if (tokenSwitchCase.rVal2 == null) return null;
2991 } else {
2992 if (!(token is TokenKwColon)) {
2993 ErrorMsg (token, "expecting : or ...");
2994 token = SkipPastSemi (token);
2995 return null;
2996 }
2997 token = token.nextToken;
2998 }
2999 } else if (token is TokenKwDefault) {
3000 tokenSwitchCase = new TokenSwitchCase (token);
3001 if (tokenStmtSwitch.lastCase == null) {
3002 tokenStmtSwitch.cases = tokenSwitchCase;
3003 } else {
3004 tokenStmtSwitch.lastCase.nextCase = tokenSwitchCase;
3005 }
3006 tokenStmtSwitch.lastCase = tokenSwitchCase;
3007
3008 token = token.nextToken;
3009 if (!(token is TokenKwColon)) {
3010 ErrorMsg (token, "expecting :");
3011 token = SkipPastSemi (token);
3012 return null;
3013 }
3014 token = token.nextToken;
3015 } else if (tokenSwitchCase != null) {
3016 TokenStmt bodyStmt = ParseStmt (ref token);
3017 if (bodyStmt == null) return null;
3018 if (tokenSwitchCase.lastStmt == null) {
3019 tokenSwitchCase.stmts = bodyStmt;
3020 } else {
3021 tokenSwitchCase.lastStmt.nextToken = bodyStmt;
3022 }
3023 tokenSwitchCase.lastStmt = bodyStmt;
3024 bodyStmt.nextToken = null;
3025 } else if (!haveComplained) {
3026 ErrorMsg (token, "expecting case or default label");
3027 token = SkipPastSemi (token);
3028 haveComplained = true;
3029 }
3030 }
3031 token = token.nextToken;
3032 return tokenStmtSwitch;
3033 }
3034
3035 private TokenStmtState ParseStmtState (ref Token token)
3036 {
3037 TokenStmtState tokenStmtState = new TokenStmtState (token);
3038 token = token.nextToken;
3039 if ((!(token is TokenName) && !(token is TokenKwDefault)) || !(token.nextToken is TokenKwSemi)) {
3040 ErrorMsg (token, "expecting state;");
3041 token = SkipPastSemi (token);
3042 return null;
3043 }
3044 if (token is TokenName) {
3045 tokenStmtState.state = (TokenName)token;
3046 }
3047 token = token.nextToken.nextToken;
3048 return tokenStmtState;
3049 }
3050
3051 private TokenStmtThrow ParseStmtThrow (ref Token token)
3052 {
3053 TokenStmtThrow tokenStmtThrow = new TokenStmtThrow (token);
3054 token = token.nextToken;
3055 if (token is TokenKwSemi) {
3056 token = token.nextToken;
3057 } else {
3058 tokenStmtThrow.rVal = ParseRVal (ref token, semiOnly);
3059 if (tokenStmtThrow.rVal == null) return null;
3060 }
3061 return tokenStmtThrow;
3062 }
3063
3064 /**
3065 * @brief Parse a try { ... } catch { ... } finally { ... } statement block
3066 * @param token = point to 'try' keyword on entry
3067 * points past last '}' processed on return
3068 * @returns encapsulated try/catch/finally or null if parsing error
3069 */
3070 private TokenStmtTry ParseStmtTry (ref Token token)
3071 {
3072 /*
3073 * Parse out the 'try { ... }' part
3074 */
3075 Token tryKw = token;
3076 token = token.nextToken;
3077 TokenStmt body = ParseStmtBlock (ref token);
3078
3079 while (true) {
3080 TokenStmtTry tokenStmtTry;
3081 if (token is TokenKwCatch) {
3082 if (!(token.nextToken is TokenKwParOpen) ||
3083 !(token.nextToken.nextToken is TokenType) ||
3084 !(token.nextToken.nextToken.nextToken is TokenName) ||
3085 !(token.nextToken.nextToken.nextToken.nextToken is TokenKwParClose)) {
3086 ErrorMsg (token, "catch must be followed by ( <type> <varname> ) { <statement>... }");
3087 return null;
3088 }
3089 token = token.nextToken.nextToken; // skip over 'catch' '('
3090 TokenDeclVar tag = new TokenDeclVar (token.nextToken, currentDeclFunc, tokenScript);
3091 tag.type = (TokenType)token;
3092 token = token.nextToken; // skip over <type>
3093 tag.name = (TokenName)token;
3094 token = token.nextToken.nextToken; // skip over <varname> ')'
3095
3096 if ((!(tag.type is TokenTypeExc)) && (!(tag.type is TokenTypeStr))) {
3097 ErrorMsg (tag.type, "must be type 'exception' or 'string'");
3098 }
3099
3100 tokenStmtTry = new TokenStmtTry (tryKw);
3101 tokenStmtTry.tryStmt = WrapTryCatFinInBlock (body);
3102 tokenStmtTry.catchVar = tag;
3103 tokenScript.PushVarFrame (false);
3104 tokenScript.AddVarEntry (tag);
3105 tokenStmtTry.catchStmt = ParseStmtBlock (ref token);
3106 tokenScript.PopVarFrame ();
3107 if (tokenStmtTry.catchStmt == null) return null;
3108 tokenStmtTry.tryStmt.isTry = true;
3109 tokenStmtTry.tryStmt.tryStmt = tokenStmtTry;
3110 tokenStmtTry.catchStmt.isCatch = true;
3111 tokenStmtTry.catchStmt.tryStmt = tokenStmtTry;
3112 }
3113 else if (token is TokenKwFinally) {
3114 token = token.nextToken;
3115
3116 tokenStmtTry = new TokenStmtTry (tryKw);
3117 tokenStmtTry.tryStmt = WrapTryCatFinInBlock (body);
3118 tokenStmtTry.finallyStmt = ParseStmtBlock (ref token);
3119 if (tokenStmtTry.finallyStmt == null) return null;
3120 tokenStmtTry.tryStmt.isTry = true;
3121 tokenStmtTry.tryStmt.tryStmt = tokenStmtTry;
3122 tokenStmtTry.finallyStmt.isFinally = true;
3123 tokenStmtTry.finallyStmt.tryStmt = tokenStmtTry;
3124 }
3125 else break;
3126
3127 body = tokenStmtTry;
3128 }
3129
3130 if (!(body is TokenStmtTry)) {
3131 ErrorMsg (body, "try must have a matching catch and/or finally");
3132 return null;
3133 }
3134 return (TokenStmtTry)body;
3135 }
3136
3137 /**
3138 * @brief Wrap a possible try/catch/finally statement block in a block statement.
3139 *
3140 * Given body = try { } catch (string s) { }
3141 *
3142 * we return { try { } catch (string s) { } }
3143 *
3144 * @param body = a TokenStmtTry or a TokenStmtBlock
3145 * @returns a TokenStmtBlock
3146 */
3147 private TokenStmtBlock WrapTryCatFinInBlock (TokenStmt body)
3148 {
3149 if (body is TokenStmtBlock) return (TokenStmtBlock)body;
3150
3151 TokenStmtTry innerTry = (TokenStmtTry)body;
3152
3153 TokenStmtBlock wrapper = new TokenStmtBlock (body);
3154 wrapper.statements = innerTry;
3155 wrapper.outerStmtBlock = currentStmtBlock;
3156 wrapper.function = currentDeclFunc;
3157
3158 innerTry.tryStmt.outerStmtBlock = wrapper;
3159 if (innerTry.catchStmt != null) innerTry.catchStmt.outerStmtBlock = wrapper;
3160 if (innerTry.finallyStmt != null) innerTry.finallyStmt.outerStmtBlock = wrapper;
3161
3162 return wrapper;
3163 }
3164
3165 private TokenStmtWhile ParseStmtWhile (ref Token token)
3166 {
3167 currentDeclFunc.triviality = Triviality.complex;
3168 TokenStmtWhile tokenStmtWhile = new TokenStmtWhile (token);
3169 token = token.nextToken;
3170 tokenStmtWhile.testRVal = ParseRValParen (ref token);
3171 if (tokenStmtWhile.testRVal == null) return null;
3172 tokenStmtWhile.bodyStmt = ParseStmt (ref token);
3173 if (tokenStmtWhile.bodyStmt == null) return null;
3174 return tokenStmtWhile;
3175 }
3176
3177 /**
3178 * @brief parse a variable declaration statement, including init value if any.
3179 * @param token = points to type or 'constant' token
3180 * @param initFunc = null: parsing a local var declaration
3181 * put initialization code in .init
3182 * else: parsing a global var or field var declaration
3183 * put initialization code in initFunc.body
3184 * @returns null: parsing error
3185 * else: variable declaration encapulating token
3186 * token = advanced just past semi-colon
3187 * variables = modified to include the new variable
3188 */
3189 private TokenDeclVar ParseDeclVar (ref Token token, TokenDeclVar initFunc)
3190 {
3191 TokenDeclVar tokenDeclVar = new TokenDeclVar (token.nextToken, currentDeclFunc, tokenScript);
3192
3193 /*
3194 * Handle constant declaration.
3195 * It ends up in the declared variables list for the statement block just like
3196 * any other variable, except it has .constant = true.
3197 * The code generator will test that the initialization expression is constant.
3198 *
3199 * constant <name> = <value> ;
3200 */
3201 if (token is TokenKwConst) {
3202 token = token.nextToken;
3203 if (!(token is TokenName)) {
3204 ErrorMsg (token, "expecting constant name");
3205 token = SkipPastSemi (token);
3206 return null;
3207 }
3208 tokenDeclVar.name = (TokenName)token;
3209 token = token.nextToken;
3210 if (!(token is TokenKwAssign)) {
3211 ErrorMsg (token, "expecting =");
3212 token = SkipPastSemi (token);
3213 return null;
3214 }
3215 token = token.nextToken;
3216 TokenRVal rVal = ParseRVal (ref token, semiOnly);
3217 if (rVal == null) return null;
3218 tokenDeclVar.init = rVal;
3219 tokenDeclVar.constant = true;
3220 }
3221
3222 /*
3223 * Otherwise, normal variable declaration with optional initialization value.
3224 */
3225 else {
3226 /*
3227 * Build basic encapsulating token with type and name.
3228 */
3229 tokenDeclVar.type = (TokenType)token;
3230 token = token.nextToken;
3231 if (!(token is TokenName)) {
3232 ErrorMsg (token, "expecting variable name");
3233 token = SkipPastSemi (token);
3234 return null;
3235 }
3236 tokenDeclVar.name = (TokenName)token;
3237 token = token.nextToken;
3238
3239 /*
3240 * If just a ;, there is no explicit initialization value.
3241 * Otherwise, look for an =RVal; expression that has init value.
3242 */
3243 if (token is TokenKwSemi) {
3244 token = token.nextToken;
3245 if (initFunc != null) {
3246 tokenDeclVar.init = TokenRValInitDef.Construct (tokenDeclVar);
3247 }
3248 } else if (token is TokenKwAssign) {
3249 token = token.nextToken;
3250 if (initFunc != null) {
3251 currentDeclFunc = initFunc;
3252 tokenDeclVar.init = ParseRVal (ref token, semiOnly);
3253 currentDeclFunc = null;
3254 } else {
3255 tokenDeclVar.init = ParseRVal (ref token, semiOnly);
3256 }
3257 if (tokenDeclVar.init == null) return null;
3258 } else {
3259 ErrorMsg (token, "expecting = or ;");
3260 token = SkipPastSemi (token);
3261 return null;
3262 }
3263 }
3264
3265 /*
3266 * If doing local vars, each var goes in its own var frame,
3267 * to make sure no code before this point can reference it.
3268 */
3269 if (currentStmtBlock != null) {
3270 tokenScript.PushVarFrame (true);
3271 }
3272
3273 /*
3274 * Can't be same name already in block.
3275 */
3276 if (!tokenScript.AddVarEntry (tokenDeclVar)) {
3277 ErrorMsg (tokenDeclVar, "duplicate variable " + tokenDeclVar.name.val);
3278 return null;
3279 }
3280 return tokenDeclVar;
3281 }
3282
3283 /**
3284 * @brief Add variable initialization to $globalvarinit, $staticfieldinit or $instfieldinit function.
3285 * @param initFunc = $globalvarinit, $staticfieldinit or $instfieldinit function
3286 * @param left = variable being initialized
3287 * @param init = null: initialize to default value
3288 * else: initialize to this value
3289 */
3290 private void DoVarInit (TokenDeclVar initFunc, TokenLVal left, TokenRVal init)
3291 {
3292 /*
3293 * Make a statement that assigns the initialization value to the variable.
3294 */
3295 TokenStmt stmt;
3296 if (init == null) {
3297 TokenStmtVarIniDef tsvid = new TokenStmtVarIniDef (left);
3298 tsvid.var = left;
3299 stmt = tsvid;
3300 } else {
3301 TokenKw op = new TokenKwAssign (left);
3302 TokenStmtRVal tsrv = new TokenStmtRVal (init);
3303 tsrv.rVal = new TokenRValOpBin (left, op, init);
3304 stmt = tsrv;
3305 }
3306
3307 /*
3308 * Add statement to end of initialization function.
3309 * Be sure to execute them in same order as in source
3310 * as some doofus scripts depend on it.
3311 */
3312 Token lastStmt = initFunc.body.statements;
3313 if (lastStmt == null) {
3314 initFunc.body.statements = stmt;
3315 } else {
3316 Token nextStmt;
3317 while ((nextStmt = lastStmt.nextToken) != null) {
3318 lastStmt = nextStmt;
3319 }
3320 lastStmt.nextToken = stmt;
3321 }
3322 }
3323
3324 /**
3325 * @brief parse function declaration argument list
3326 * @param token = points to TokenKwParOpen
3327 * @returns null: parse error
3328 * else: points to token with types and names
3329 * token = updated past the TokenKw{Brk,Par}Close
3330 */
3331 private TokenArgDecl ParseFuncArgs (ref Token token, Type end)
3332 {
3333 TokenArgDecl tokenArgDecl = new TokenArgDecl (token);
3334
3335 bool first = true;
3336 do {
3337 token = token.nextToken;
3338 if ((token.GetType () == end) && first) break;
3339 if (!(token is TokenType)) {
3340 ErrorMsg (token, "expecting arg type");
3341 token = SkipPastSemi (token);
3342 return null;
3343 }
3344 TokenType type = (TokenType)token;
3345 token = token.nextToken;
3346 if (!(token is TokenName)) {
3347 ErrorMsg (token, "expecting arg name");
3348 token = SkipPastSemi (token);
3349 return null;
3350 }
3351 TokenName name = (TokenName)token;
3352 token = token.nextToken;
3353
3354 if (!tokenArgDecl.AddArg (type, name)) {
3355 ErrorMsg (name, "duplicate arg name");
3356 }
3357 first = false;
3358 } while (token is TokenKwComma);
3359
3360 if (token.GetType () != end) {
3361 ErrorMsg (token, "expecting comma or close bracket/paren");
3362 token = SkipPastSemi (token);
3363 return null;
3364 }
3365 token = token.nextToken;
3366
3367 return tokenArgDecl;
3368 }
3369
3370 /**
3371 * @brief parse right-hand value expression
3372 * this is where arithmetic-like expressions are processed
3373 * @param token = points to first token expression
3374 * @param termTokenType = expression termination token type
3375 * @returns null: not an RVal
3376 * else: single token representing whole expression
3377 * token = if termTokenType.Length == 1, points just past terminating token
3378 * else, points right at terminating token
3379 */
3380 public TokenRVal ParseRVal (ref Token token, Type[] termTokenTypes)
3381 {
3382 /*
3383 * Start with pushing the first operand on operand stack.
3384 */
3385 BinOp binOps = null;
3386 TokenRVal operands = GetOperand (ref token);
3387 if (operands == null) return null;
3388
3389 /*
3390 * Keep scanning until we hit the termination token.
3391 */
3392 while (true) {
3393 Type tokType = token.GetType ();
3394 for (int i = termTokenTypes.Length; -- i >= 0;) {
3395 if (tokType == termTokenTypes[i]) goto done;
3396 }
3397
3398 /*
3399 * Special form:
3400 * <operand> is <typeexp>
3401 */
3402 if (token is TokenKwIs) {
3403 TokenRValIsType tokenRValIsType = new TokenRValIsType (token);
3404 token = token.nextToken;
3405
3406 /*
3407 * Parse the <typeexp>.
3408 */
3409 tokenRValIsType.typeExp = ParseTypeExp (ref token);
3410 if (tokenRValIsType.typeExp == null) return null;
3411
3412 /*
3413 * Replace top operand with result of <operand> is <typeexp>
3414 */
3415 tokenRValIsType.rValExp = operands;
3416 tokenRValIsType.nextToken = operands.nextToken;
3417 operands = tokenRValIsType;
3418
3419 /*
3420 * token points just past <typeexp> so see if it is another operator.
3421 */
3422 continue;
3423 }
3424
3425 /*
3426 * Peek at next operator.
3427 */
3428 BinOp binOp = GetOperator (ref token);
3429 if (binOp == null) return null;
3430
3431 /*
3432 * If there are stacked operators of higher or same precedence than new one,
3433 * perform their computation then push result back on operand stack.
3434 *
3435 * higher or same = left-to-right application of operators
3436 * eg, a - b - c becomes (a - b) - c
3437 *
3438 * higher precedence = right-to-left application of operators
3439 * eg, a - b - c becomes a - (b - c)
3440 *
3441 * Now of course, there is some ugliness necessary:
3442 * we want: a - b - c => (a - b) - c so we do 'higher or same'
3443 * but we want: a += b = c => a += (b = c) so we do 'higher only'
3444 *
3445 * binOps is the first operator (or null if only one)
3446 * binOp is the second operator (or first if only one)
3447 */
3448 while (binOps != null) {
3449 if (binOps.preced < binOp.preced) break; // 1st operator lower than 2nd, so leave 1st on stack to do later
3450 if (binOps.preced > binOp.preced) goto do1st; // 1st op higher than 2nd, so we always do 1st op first
3451 if (binOps.preced == ASNPR) break; // equal preced, if assignment type, leave 1st on stack to do later
3452 // if non-asn type, do 1st op first (ie left-to-right)
3453 do1st:
3454 TokenRVal result = PerformBinOp ((TokenRVal)operands.prevToken, binOps, (TokenRVal)operands);
3455 result.prevToken = operands.prevToken.prevToken;
3456 operands = result;
3457 binOps = binOps.pop;
3458 }
3459
3460 /*
3461 * Handle conditional expression as a special form:
3462 * <condexp> ? <trueexp> : <falseexp>
3463 */
3464 if (binOp.token is TokenKwQMark) {
3465 TokenRValCondExpr condExpr = new TokenRValCondExpr (binOp.token);
3466 condExpr.condExpr = operands;
3467 condExpr.trueExpr = ParseRVal (ref token, new Type[] { typeof (TokenKwColon) });
3468 condExpr.falseExpr = ParseRVal (ref token, termTokenTypes);
3469 condExpr.prevToken = operands.prevToken;
3470 operands = condExpr;
3471 termTokenTypes = new Type[0];
3472 goto done;
3473 }
3474
3475 /*
3476 * Push new operator on its stack.
3477 */
3478 binOp.pop = binOps;
3479 binOps = binOp;
3480
3481 /*
3482 * Push next operand on its stack.
3483 */
3484 TokenRVal operand = GetOperand (ref token);
3485 if (operand == null) return null;
3486 operand.prevToken = operands;
3487 operands = operand;
3488 }
3489 done:
3490
3491 /*
3492 * At end of expression, perform any stacked computations.
3493 */
3494 while (binOps != null) {
3495 TokenRVal result = PerformBinOp ((TokenRVal)operands.prevToken, binOps, (TokenRVal)operands);
3496 result.prevToken = operands.prevToken.prevToken;
3497 operands = result;
3498 binOps = binOps.pop;
3499 }
3500
3501 /*
3502 * There should be exactly one remaining operand on the stack which is our final result.
3503 */
3504 if (operands.prevToken != null) throw new Exception ("too many operands");
3505
3506 /*
3507 * If only one terminator type possible, advance past the terminator.
3508 */
3509 if (termTokenTypes.Length == 1) token = token.nextToken;
3510
3511 return operands;
3512 }
3513
3514 private TokenTypeExp ParseTypeExp (ref Token token)
3515 {
3516 TokenTypeExp leftOperand = GetTypeExp (ref token);
3517 if (leftOperand == null) return null;
3518
3519 while ((token is TokenKwAnd) || (token is TokenKwOr)) {
3520 Token typeBinOp = token;
3521 token = token.nextToken;
3522 TokenTypeExp rightOperand = GetTypeExp (ref token);
3523 if (rightOperand == null) return null;
3524 TokenTypeExpBinOp typeExpBinOp = new TokenTypeExpBinOp (typeBinOp);
3525 typeExpBinOp.leftOp = leftOperand;
3526 typeExpBinOp.binOp = typeBinOp;
3527 typeExpBinOp.rightOp = rightOperand;
3528 leftOperand = typeExpBinOp;
3529 }
3530 return leftOperand;
3531 }
3532
3533 private TokenTypeExp GetTypeExp (ref Token token)
3534 {
3535 if (token is TokenKwTilde) {
3536 TokenTypeExpNot typeExpNot = new TokenTypeExpNot (token);
3537 token = token.nextToken;
3538 typeExpNot.typeExp = GetTypeExp (ref token);
3539 if (typeExpNot.typeExp == null) return null;
3540 return typeExpNot;
3541 }
3542 if (token is TokenKwParOpen) {
3543 TokenTypeExpPar typeExpPar = new TokenTypeExpPar (token);
3544 token = token.nextToken;
3545 typeExpPar.typeExp = GetTypeExp (ref token);
3546 if (typeExpPar.typeExp == null) return null;
3547 if (!(token is TokenKwParClose)) {
3548 ErrorMsg (token, "expected close parenthesis");
3549 token = SkipPastSemi (token);
3550 return null;
3551 }
3552 return typeExpPar;
3553 }
3554 if (token is TokenKwUndef) {
3555 TokenTypeExpUndef typeExpUndef = new TokenTypeExpUndef (token);
3556 token = token.nextToken;
3557 return typeExpUndef;
3558 }
3559 if (token is TokenType) {
3560 TokenTypeExpType typeExpType = new TokenTypeExpType (token);
3561 typeExpType.typeToken = (TokenType)token;
3562 token = token.nextToken;
3563 return typeExpType;
3564 }
3565 ErrorMsg (token, "expected type");
3566 token = SkipPastSemi (token);
3567 return null;
3568 }
3569
3570 /**
3571 * @brief get a right-hand operand expression token
3572 * @param token = first token of operand to parse
3573 * @returns null: invalid operand
3574 * else: token that bundles or wraps the operand
3575 * token = points to token following last operand token
3576 */
3577 private TokenRVal GetOperand (ref Token token)
3578 {
3579 /*
3580 * Prefix unary operators (eg ++, --) requiring an L-value.
3581 */
3582 if ((token is TokenKwIncr) || (token is TokenKwDecr)) {
3583 TokenRValAsnPre asnPre = new TokenRValAsnPre (token);
3584 asnPre.prefix = token;
3585 token = token.nextToken;
3586 TokenRVal op = GetOperand (ref token);
3587 if (op == null) return null;
3588 if (!(op is TokenLVal)) {
3589 ErrorMsg (op, "can pre{in,de}crement only an L-value");
3590 return null;
3591 }
3592 asnPre.lVal = (TokenLVal)op;
3593 return asnPre;
3594 }
3595
3596 /*
3597 * Get the bulk of the operand, ie, without any of the below suffixes.
3598 */
3599 TokenRVal operand = GetOperandNoMods (ref token);
3600 if (operand == null) return null;
3601 modifiers:
3602
3603 /*
3604 * If followed by '++' or '--', it is post-{in,de}cremented.
3605 */
3606 if ((token is TokenKwIncr) || (token is TokenKwDecr)) {
3607 TokenRValAsnPost asnPost = new TokenRValAsnPost (token);
3608 asnPost.postfix = token;
3609 token = token.nextToken;
3610 if (!(operand is TokenLVal)) {
3611 ErrorMsg (operand, "can post{in,de}crement only an L-value");
3612 return null;
3613 }
3614 asnPost.lVal = (TokenLVal)operand;
3615 return asnPost;
3616 }
3617
3618 /*
3619 * If followed by a '.', it is an instance field or instance method reference.
3620 */
3621 if (token is TokenKwDot) {
3622 token = token.nextToken;
3623 if (!(token is TokenName)) {
3624 ErrorMsg (token, ". must be followed by field/method name");
3625 return null;
3626 }
3627 TokenLValIField field = new TokenLValIField (token);
3628 field.baseRVal = operand;
3629 field.fieldName = (TokenName)token;
3630 operand = field;
3631 token = token.nextToken;
3632 goto modifiers;
3633 }
3634
3635 /*
3636 * If followed by a '[', it is an array subscript.
3637 */
3638 if (token is TokenKwBrkOpen) {
3639 TokenLValArEle tokenLValArEle = new TokenLValArEle (token);
3640 token = token.nextToken;
3641
3642 /*
3643 * Parse subscript(s) expression.
3644 */
3645 tokenLValArEle.subRVal = ParseRVal (ref token, brkCloseOnly);
3646 if (tokenLValArEle.subRVal == null) {
3647 ErrorMsg (tokenLValArEle, "invalid subscript");
3648 return null;
3649 }
3650
3651 /*
3652 * See if comma-separated list of values.
3653 */
3654 TokenRVal subscriptRVals;
3655 int numSubscripts = SplitCommaRVals (tokenLValArEle.subRVal, out subscriptRVals);
3656 if (numSubscripts > 1) {
3657
3658 /*
3659 * If so, put the values in an LSL_List object.
3660 */
3661 TokenRValList rValList = new TokenRValList (tokenLValArEle);
3662 rValList.rVal = subscriptRVals;
3663 rValList.nItems = numSubscripts;
3664 tokenLValArEle.subRVal = rValList;
3665 }
3666
3667 /*
3668 * Either way, save array variable name
3669 * and substitute whole reference for L-value
3670 */
3671 tokenLValArEle.baseRVal = operand;
3672 operand = tokenLValArEle;
3673 goto modifiers;
3674 }
3675
3676 /*
3677 * If followed by a '(', it is a function/method call.
3678 */
3679 if (token is TokenKwParOpen) {
3680 operand = ParseRValCall (ref token, operand);
3681 goto modifiers;
3682 }
3683
3684 /*
3685 * If 'new' arraytipe '{', it is an array initializer.
3686 */
3687 if ((token is TokenKwBrcOpen) && (operand is TokenLValSField) &&
3688 (((TokenLValSField)operand).fieldName.val == "$new") &&
3689 ((TokenLValSField)operand).baseType.ToString ().EndsWith ("]")) {
3690 operand = ParseRValNewArIni (ref token, (TokenLValSField)operand);
3691 if (operand != null) goto modifiers;
3692 }
3693
3694 return operand;
3695 }
3696
3697 /**
3698 * @brief same as GetOperand() except doesn't check for any modifiers
3699 */
3700 private TokenRVal GetOperandNoMods (ref Token token)
3701 {
3702 /*
3703 * Simple unary operators.
3704 */
3705 if ((token is TokenKwSub) ||
3706 (token is TokenKwTilde) ||
3707 (token is TokenKwExclam)) {
3708 Token uop = token;
3709 token = token.nextToken;
3710 TokenRVal rVal = GetOperand (ref token);
3711 if (rVal == null) return null;
3712 return PerformUnOp (uop, rVal);
3713 }
3714
3715 /*
3716 * Type casting.
3717 */
3718 if ((token is TokenKwParOpen) &&
3719 (token.nextToken is TokenType) &&
3720 (token.nextToken.nextToken is TokenKwParClose)) {
3721 TokenType type = (TokenType)token.nextToken;
3722 token = token.nextToken.nextToken.nextToken;
3723 TokenRVal rVal = GetOperand (ref token);
3724 if (rVal == null) return null;
3725 return new TokenRValCast (type, rVal);
3726 }
3727
3728 /*
3729 * Parenthesized expression.
3730 */
3731 if (token is TokenKwParOpen) {
3732 return ParseRValParen (ref token);
3733 }
3734
3735 /*
3736 * Constants.
3737 */
3738 if (token is TokenChar) {
3739 TokenRValConst rValConst = new TokenRValConst (token, ((TokenChar)token).val);
3740 token = token.nextToken;
3741 return rValConst;
3742 }
3743 if (token is TokenFloat) {
3744 TokenRValConst rValConst = new TokenRValConst (token, ((TokenFloat)token).val);
3745 token = token.nextToken;
3746 return rValConst;
3747 }
3748 if (token is TokenInt) {
3749 TokenRValConst rValConst = new TokenRValConst (token, ((TokenInt)token).val);
3750 token = token.nextToken;
3751 return rValConst;
3752 }
3753 if (token is TokenStr) {
3754 TokenRValConst rValConst = new TokenRValConst (token, ((TokenStr)token).val);
3755 token = token.nextToken;
3756 return rValConst;
3757 }
3758 if (token is TokenKwUndef) {
3759 TokenRValUndef rValUndef = new TokenRValUndef ((TokenKwUndef)token);
3760 token = token.nextToken;
3761 return rValUndef;
3762 }
3763
3764 /*
3765 * '<'value,...'>', ie, rotation or vector
3766 */
3767 if (token is TokenKwCmpLT) {
3768 Token openBkt = token;
3769 token = token.nextToken;
3770 TokenRVal rValAll = ParseRVal (ref token, cmpGTOnly);
3771 if (rValAll == null) return null;
3772 TokenRVal rVals;
3773 int nVals = SplitCommaRVals (rValAll, out rVals);
3774 switch (nVals) {
3775 case 3: {
3776 TokenRValVec rValVec = new TokenRValVec (openBkt);
3777 rValVec.xRVal = rVals;
3778 rValVec.yRVal = (TokenRVal)rVals.nextToken;
3779 rValVec.zRVal = (TokenRVal)rVals.nextToken.nextToken;
3780 return rValVec;
3781 }
3782 case 4: {
3783 TokenRValRot rValRot = new TokenRValRot (openBkt);
3784 rValRot.xRVal = rVals;
3785 rValRot.yRVal = (TokenRVal)rVals.nextToken;
3786 rValRot.zRVal = (TokenRVal)rVals.nextToken.nextToken;
3787 rValRot.wRVal = (TokenRVal)rVals.nextToken.nextToken.nextToken;
3788 return rValRot;
3789 }
3790 default: {
3791 ErrorMsg (openBkt, "bad rotation/vector");
3792 token = SkipPastSemi (token);
3793 return null;
3794 }
3795 }
3796 }
3797
3798 /*
3799 * '['value,...']', ie, list
3800 */
3801 if (token is TokenKwBrkOpen) {
3802 TokenRValList rValList = new TokenRValList (token);
3803 token = token.nextToken;
3804 if (token is TokenKwBrkClose) {
3805 token = token.nextToken; // empty list
3806 } else {
3807 TokenRVal rValAll = ParseRVal (ref token, brkCloseOnly);
3808 if (rValAll == null) return null;
3809 rValList.nItems = SplitCommaRVals (rValAll, out rValList.rVal);
3810 }
3811 return rValList;
3812 }
3813
3814 /*
3815 * Maybe we have <type>.<name> referencing a static field or method of some type.
3816 */
3817 if ((token is TokenType) && (token.nextToken is TokenKwDot) && (token.nextToken.nextToken is TokenName)) {
3818 TokenLValSField field = new TokenLValSField (token.nextToken.nextToken);
3819 field.baseType = (TokenType)token;
3820 field.fieldName = (TokenName)token.nextToken.nextToken;
3821 token = token.nextToken.nextToken.nextToken;
3822 return field;
3823 }
3824
3825 /*
3826 * Maybe we have 'this' referring to the object of the instance method.
3827 */
3828 if (token is TokenKwThis) {
3829 if ((currentDeclSDType == null) || !(currentDeclSDType is TokenDeclSDTypeClass)) {
3830 ErrorMsg (token, "using 'this' outside class definition");
3831 token = SkipPastSemi (token);
3832 return null;
3833 }
3834 TokenRValThis zhis = new TokenRValThis (token, (TokenDeclSDTypeClass)currentDeclSDType);
3835 token = token.nextToken;
3836 return zhis;
3837 }
3838
3839 /*
3840 * Maybe we have 'base' referring to a field/method of the extended class.
3841 */
3842 if (token is TokenKwBase) {
3843 if ((currentDeclFunc == null) || (currentDeclFunc.sdtClass == null) || !(currentDeclFunc.sdtClass is TokenDeclSDTypeClass)) {
3844 ErrorMsg (token, "using 'base' outside method");
3845 token = SkipPastSemi (token);
3846 return null;
3847 }
3848 if (!(token.nextToken is TokenKwDot) || !(token.nextToken.nextToken is TokenName)) {
3849 ErrorMsg (token, "base must be followed by . then field or method name");
3850 TokenRValThis zhis = new TokenRValThis (token, (TokenDeclSDTypeClass)currentDeclFunc.sdtClass);
3851 token = token.nextToken;
3852 return zhis;
3853 }
3854 TokenLValBaseField baseField = new TokenLValBaseField (token,
3855 (TokenName)token.nextToken.nextToken,
3856 (TokenDeclSDTypeClass)currentDeclFunc.sdtClass);
3857 token = token.nextToken.nextToken.nextToken;
3858 return baseField;
3859 }
3860
3861 /*
3862 * Maybe we have 'new <script-defined-type>' saying to create an object instance.
3863 * This ends up generating a call to static function <script-defined-type>.$new(...)
3864 * whose CIL code is generated by GenerateNewobjBody().
3865 */
3866 if (token is TokenKwNew) {
3867 if (!(token.nextToken is TokenType)) {
3868 ErrorMsg (token.nextToken, "new must be followed by type");
3869 token = SkipPastSemi (token);
3870 return null;
3871 }
3872 TokenLValSField field = new TokenLValSField (token.nextToken.nextToken);
3873 field.baseType = (TokenType)token.nextToken;
3874 field.fieldName = new TokenName (token, "$new");
3875 token = token.nextToken.nextToken;
3876 return field;
3877 }
3878
3879 /*
3880 * All we got left is <name>, eg, arg, function, global or local variable reference
3881 */
3882 if (token is TokenName) {
3883 TokenLValName name = new TokenLValName ((TokenName)token, tokenScript.variablesStack);
3884 token = token.nextToken;
3885 return name;
3886 }
3887
3888 /*
3889 * Who knows what it is supposed to be?
3890 */
3891 ErrorMsg (token, "invalid operand token");
3892 token = SkipPastSemi (token);
3893 return null;
3894 }
3895
3896 /**
3897 * @brief Parse a call expression
3898 * @param token = points to arg list '('
3899 * @param meth = points to method name being called
3900 * @returns call expression value
3901 * token = points just past arg list ')'
3902 */
3903 private TokenRValCall ParseRValCall (ref Token token, TokenRVal meth)
3904 {
3905 /*
3906 * Set up basic function call struct with function name.
3907 */
3908 TokenRValCall rValCall = new TokenRValCall (token);
3909 rValCall.meth = meth;
3910
3911 /*
3912 * Parse the call parameters, if any.
3913 */
3914 token = token.nextToken;
3915 if (token is TokenKwParClose) {
3916 token = token.nextToken;
3917 } else {
3918 rValCall.args = ParseRVal (ref token, parCloseOnly);
3919 if (rValCall.args == null) return null;
3920 rValCall.nArgs = SplitCommaRVals (rValCall.args, out rValCall.args);
3921 }
3922
3923 currentDeclFunc.unknownTrivialityCalls.AddLast (rValCall);
3924
3925 return rValCall;
3926 }
3927
3928 /**
3929 * @brief decode binary operator token
3930 * @param token = points to token to decode
3931 * @returns null: invalid operator token
3932 * else: operator token and precedence
3933 */
3934 private BinOp GetOperator (ref Token token)
3935 {
3936 BinOp binOp = new BinOp ();
3937 if (precedence.TryGetValue (token.GetType (), out binOp.preced)) {
3938 binOp.token = (TokenKw)token;
3939 token = token.nextToken;
3940 return binOp;
3941 }
3942
3943 if ((token is TokenKwSemi) || (token is TokenKwBrcOpen) || (token is TokenKwBrcClose)) {
3944 ErrorMsg (token, "premature expression end");
3945 } else {
3946 ErrorMsg (token, "invalid operator");
3947 }
3948 token = SkipPastSemi (token);
3949 return null;
3950 }
3951
3952 private class BinOp {
3953 public BinOp pop;
3954 public TokenKw token;
3955 public int preced;
3956 }
3957
3958 /**
3959 * @brief Return an R-value expression token that will be used to
3960 * generate code to perform the operation at runtime.
3961 * @param left = left-hand operand
3962 * @param binOp = operator
3963 * @param right = right-hand operand
3964 * @returns resultant expression
3965 */
3966 private TokenRVal PerformBinOp (TokenRVal left, BinOp binOp, TokenRVal right)
3967 {
3968 return new TokenRValOpBin (left, binOp.token, right);
3969 }
3970
3971 /**
3972 * @brief Return an R-value expression token that will be used to
3973 * generate code to perform the operation at runtime.
3974 * @param unOp = operator
3975 * @param right = right-hand operand
3976 * @returns resultant constant or expression
3977 */
3978 private TokenRVal PerformUnOp (Token unOp, TokenRVal right)
3979 {
3980 return new TokenRValOpUn ((TokenKw)unOp, right);
3981 }
3982
3983 /**
3984 * @brief Parse an array initialization expression.
3985 * @param token = points to '{' on entry
3986 * @param newCall = encapsulates a '$new' call
3987 * @return resultant operand encapsulating '$new' call and initializers
3988 * token = points just past terminating '}'
3989 * ...or null if parse error
3990 */
3991 private TokenRVal ParseRValNewArIni (ref Token token, TokenLValSField newCall)
3992 {
3993 Stack<TokenList> stack = new Stack<TokenList> ();
3994 TokenRValNewArIni arini = new TokenRValNewArIni (token);
3995 arini.arrayType = newCall.baseType;
3996 TokenList values = null;
3997 while (true) {
3998
3999 // open brace means start a (sub-)list
4000 if (token is TokenKwBrcOpen) {
4001 stack.Push (values);
4002 values = new TokenList (token);
4003 token = token.nextToken;
4004 continue;
4005 }
4006
4007 // close brace means end of (sub-)list
4008 // if final '}' all done parsing
4009 if (token is TokenKwBrcClose) {
4010 token = token.nextToken; // skip over the '}'
4011 TokenList innerds = values; // save the list just closed
4012 arini.valueList = innerds; // it's the top list if it's the last closed
4013 values = stack.Pop (); // pop to next outer list
4014 if (values == null) return arini; // final '}', we are done
4015 values.tl.Add (innerds); // put the inner list on end of outer list
4016 if (token is TokenKwComma) { // should have a ',' or '}' next
4017 token = token.nextToken; // skip over the ','
4018 } else if (!(token is TokenKwBrcClose)) {
4019 ErrorMsg (token, "expecting , or } after sublist");
4020 }
4021 continue;
4022 }
4023
4024 // this is a comma that doesn't have a value expression before it
4025 // so we take it to mean skip initializing element (leave it zeroes/null etc)
4026 if (token is TokenKwComma) {
4027 values.tl.Add (token);
4028 token = token.nextToken;
4029 continue;
4030 }
4031
4032 // parse value expression and skip terminating ',' if any
4033 TokenRVal append = ParseRVal (ref token, commaOrBrcClose);
4034 if (append == null) return null;
4035 values.tl.Add (append);
4036 if (token is TokenKwComma) {
4037 token = token.nextToken;
4038 }
4039 }
4040 }
4041
4042 /**
4043 * @brief parse out a parenthesized expression.
4044 * @param token = points to open parenthesis
4045 * @returns null: invalid expression
4046 * else: parenthesized expression token or constant token
4047 * token = points past the close parenthesis
4048 */
4049 private TokenRValParen ParseRValParen (ref Token token)
4050 {
4051 if (!(token is TokenKwParOpen)) {
4052 ErrorMsg (token, "expecting (");
4053 token = SkipPastSemi (token);
4054 return null;
4055 }
4056 TokenRValParen tokenRValParen = new TokenRValParen (token);
4057 token = token.nextToken;
4058 tokenRValParen.rVal = ParseRVal (ref token, parCloseOnly);
4059 if (tokenRValParen.rVal == null) return null;
4060 return tokenRValParen;
4061 }
4062
4063 /**
4064 * @brief Split a comma'd RVal into separate expressions
4065 * @param rValAll = expression containing commas
4066 * @returns number of comma separated values
4067 * rVals = values in a null-terminated list linked by rVals.nextToken
4068 */
4069 private int SplitCommaRVals (TokenRVal rValAll, out TokenRVal rVals)
4070 {
4071 if (!(rValAll is TokenRValOpBin) || !(((TokenRValOpBin)rValAll).opcode is TokenKwComma)) {
4072 rVals = rValAll;
4073 if (rVals.nextToken != null) throw new Exception ("expected null");
4074 return 1;
4075 }
4076 TokenRValOpBin opBin = (TokenRValOpBin)rValAll;
4077 TokenRVal rValLeft, rValRight;
4078 int leftCount = SplitCommaRVals (opBin.rValLeft, out rValLeft);
4079 int rightCount = SplitCommaRVals (opBin.rValRight, out rValRight);
4080 rVals = rValLeft;
4081 while (rValLeft.nextToken != null) rValLeft = (TokenRVal)rValLeft.nextToken;
4082 rValLeft.nextToken = rValRight;
4083 return leftCount + rightCount;
4084 }
4085
4086 /**
4087 * @brief output error message and remember that there is an error.
4088 * @param token = what token is associated with the error
4089 * @param message = error message string
4090 */
4091 private void ErrorMsg (Token token, string message)
4092 {
4093 if (!errors || (token.file != lastErrorFile) || (token.line > lastErrorLine)) {
4094 errors = true;
4095 lastErrorFile = token.file;
4096 lastErrorLine = token.line;
4097 token.ErrorMsg (message);
4098 }
4099 }
4100
4101 /**
4102 * @brief Skip past the next semicolon (or matched braces)
4103 * @param token = points to token to skip over
4104 * @returns token just after the semicolon or close brace
4105 */
4106 private Token SkipPastSemi (Token token)
4107 {
4108 int braceLevel = 0;
4109
4110 while (!(token is TokenEnd)) {
4111 if ((token is TokenKwSemi) && (braceLevel == 0)) {
4112 return token.nextToken;
4113 }
4114 if (token is TokenKwBrcOpen) {
4115 braceLevel ++;
4116 }
4117 if ((token is TokenKwBrcClose) && (-- braceLevel <= 0)) {
4118 return token.nextToken;
4119 }
4120 token = token.nextToken;
4121 }
4122 return token;
4123 }
4124 }
4125
4126 /**
4127 * @brief Script-defined type declarations
4128 */
4129 public abstract class TokenDeclSDType : Token {
4130 protected const byte CLASS = 0;
4131 protected const byte DELEGATE = 1;
4132 protected const byte INTERFACE = 2;
4133 protected const byte TYPEDEF = 3;
4134
4135 // stuff that gets cloned/copied/transformed when instantiating a generic
4136 // see InstantiateGeneric() below
4137 public TokenDeclSDType outerSDType; // null if top-level
4138 // else points to defining script-defined type
4139 public Dictionary<string, TokenDeclSDType> innerSDTypes = new Dictionary<string, TokenDeclSDType> ();
4140 // indexed by shortName
4141 public Token begToken; // token that begins the definition (might be this or something like 'public')
4142 public Token endToken; // the '}' or ';' that ends the definition
4143
4144 // generic instantiation assumes none of the rest needs to be cloned (well except for the shortName)
4145 public int sdTypeIndex = -1; // index in scriptObjCode.sdObjTypesIndx[] array
4146 public TokenDeclSDTypeClass extends; // only non-null for TokenDeclSDTypeClass's
4147 public uint accessLevel; // SDT_PRIVATE, SDT_PROTECTED or SDT_PUBLIC
4148 // ... all top-level types are SDT_PUBLIC
4149 public VarDict members = new VarDict (false); // declared fields, methods, properties if any
4150
4151 public Dictionary<string, int> genParams; // list of parameters for generic prototypes
4152 // null for non-generic prototypes
4153 // eg, for 'Dictionary<K,V>'
4154 // ...genParams gives K->0; V->1
4155
4156 public bool isPartial; // was declared with 'partial' keyword
4157 // classes only, all others always false
4158
4159 /*
4160 * Name of the type.
4161 * shortName = doesn't include outer class type names
4162 * eg, 'Engine' for non-generic
4163 * 'Dictionary<,>' for generic prototype
4164 * 'Dictionary<string,integer>' for generic instantiation
4165 * longName = includes all outer class type names if any
4166 */
4167 private TokenName _shortName;
4168 private TokenName _longName;
4169
4170 public TokenName shortName {
4171 get {
4172 return _shortName;
4173 }
4174 set {
4175 _shortName = value;
4176 _longName = null;
4177 }
4178 }
4179
4180 public TokenName longName {
4181 get {
4182 if (_longName == null) {
4183 _longName = _shortName;
4184 if (outerSDType != null) {
4185 _longName = new TokenName (_shortName, outerSDType.longName.val + "." + _shortName.val);
4186 }
4187 }
4188 return _longName;
4189 }
4190 }
4191
4192 /*
4193 * Dictionary used when reading from object file that holds all script-defined types.
4194 * Not complete though until all types have been read from the object file.
4195 */
4196 private Dictionary<string, TokenDeclSDType> sdTypes;
4197
4198 public TokenDeclSDType (Token t) : base (t) { }
4199 protected abstract TokenDeclSDType MakeBlank (TokenName shortName);
4200 public abstract TokenType MakeRefToken (Token t);
4201 public abstract Type GetSysType ();
4202 public abstract void WriteToFile (BinaryWriter objFileWriter);
4203 public abstract void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter);
4204
4205 /**
4206 * @brief Given that this is a generic prototype, apply the supplied genArgs
4207 * to create an equivalent instantiated non-generic. This basically
4208 * makes a copy replacing all the parameter types with the actual
4209 * argument types.
4210 * @param this = the prototype to be instantiated, eg, 'Dictionary<string,integer>.Converter'
4211 * @param name = short name with arguments, eg, 'Converter<float>'.
4212 * @param genArgs = argument types of just this level, eg, 'float'.
4213 * @returns clone of this but with arguments applied and spliced in source token stream
4214 */
4215 public TokenDeclSDType InstantiateGeneric (string name, TokenType[] genArgs, ScriptReduce reduce)
4216 {
4217 /*
4218 * Malloc the struct and give it a name.
4219 */
4220 TokenDeclSDType instdecl = this.MakeBlank (new TokenName (this, name));
4221
4222 /*
4223 * If the original had an outer type, then so does the new one.
4224 * The outer type will never be a generic prototype, eg, if this
4225 * is 'ValueList' it will always be inside 'Dictionary<string,integer>'
4226 * not 'Dictionary' at this point.
4227 */
4228 if ((this.outerSDType != null) && (this.outerSDType.genParams != null)) throw new Exception ();
4229 instdecl.outerSDType = this.outerSDType;
4230
4231 /*
4232 * The generic prototype may have stuff like 'public' just before it and we need to copy that too.
4233 */
4234 Token prefix;
4235 for (prefix = this; (prefix = prefix.prevToken) != null;) {
4236 if (!(prefix is TokenKwPublic) && !(prefix is TokenKwProtected) && !(prefix is TokenKwPrivate)) break;
4237 }
4238 this.begToken = prefix.nextToken;
4239
4240 /*
4241 * Splice in a copy of the prefix tokens, just before the beginning token of prototype (this.begToken).
4242 */
4243 while ((prefix = prefix.nextToken) != this) {
4244 SpliceSourceToken (prefix.CopyToken (prefix));
4245 }
4246
4247 /*
4248 * Splice instantiation (instdecl) in just before the beginning token of prototype (this.begToken).
4249 */
4250 SpliceSourceToken (instdecl);
4251
4252 /*
4253 * Now for the fun part... Copy the rest of the prototype body to the
4254 * instantiated body, replacing all generic parameter type tokens with
4255 * the corresponding generic argument types. Note that the parameters
4256 * are numbered starting with the outermost so we need the full genArgs
4257 * array. Eg if we are doing 'Converter<V=float>' from
4258 * 'Dictionary<T=string,U=integer>.Converter<V=float>', any V's are
4259 * numbered [2]. Any [0]s or [1]s should be gone by now but it doesn't
4260 * matter.
4261 */
4262 int index;
4263 Token it, pt;
4264 TokenDeclSDType innerProto = this;
4265 TokenDeclSDType innerInst = instdecl;
4266 for (pt = this; (pt = pt.nextToken) != this.endToken;) {
4267
4268 /*
4269 * Coming across a sub-type's declaration involves a deep copy of the
4270 * declaration token. Fortunately we are early on in parsing, so there
4271 * really isn't much to copy:
4272 * 1) short name is the same, eg, doing List of Dictionary<string,integer>.List is same short name as Dictionary<T,U>.List
4273 * if generic, eg doing Converter<W> of Dictionary<T,U>.Converter<W>, we have to manually copy the W as well.
4274 * 2) outerSDType is transformed from Dictionary<T,U> to Dictionary<string,integer>.
4275 * 3) innerSDTypes is rebuilt when/if we find classes that are inner to this one.
4276 */
4277 if (pt is TokenDeclSDType) {
4278
4279 /*
4280 * Make a new TokenDeclSDType{Class,Delegate,Interface}.
4281 */
4282 TokenDeclSDType ptSDType = (TokenDeclSDType)pt;
4283 TokenDeclSDType itSDType = ptSDType.MakeBlank (new TokenName (ptSDType.shortName, ptSDType.shortName.val));
4284
4285 /*
4286 * Set up the transformed outerSDType.
4287 * Eg, if we are creating Enumerator of Dictionary<string,integer>.Enumerator,
4288 * innerProto = Dictionary<T,U> and innerInst = Dictionary<string,integer>.
4289 */
4290 itSDType.outerSDType = innerInst;
4291
4292 /*
4293 * This clone is an inner type of its next outer level.
4294 */
4295 reduce.CatalogSDTypeDecl (itSDType);
4296
4297 /*
4298 * We need to manually copy any generic parameters of the class declaration being cloned.
4299 * eg, if we are cloning Converter<W>, this is where the W gets copied.
4300 * Since it is an immutable array of strings, just copy the array pointer, if any.
4301 */
4302 itSDType.genParams = ptSDType.genParams;
4303
4304 /*
4305 * We are now processing tokens for this cloned type declaration.
4306 */
4307 innerProto = ptSDType;
4308 innerInst = itSDType;
4309
4310 /*
4311 * Splice this clone token in.
4312 */
4313 it = itSDType;
4314 }
4315
4316 /*
4317 * Check for an generic parameter to substitute out.
4318 */
4319 else if ((pt is TokenName) && this.genParams.TryGetValue (((TokenName)pt).val, out index)) {
4320 it = genArgs[index].CopyToken (pt);
4321 }
4322
4323 /*
4324 * Everything else is a simple copy.
4325 */
4326 else it = pt.CopyToken (pt);
4327
4328 /*
4329 * Whatever we came up with, splice it into the source token stream.
4330 */
4331 SpliceSourceToken (it);
4332
4333 /*
4334 * Maybe we just finished copying an inner type definition.
4335 * If so, remember where it ends and pop it from the stack.
4336 */
4337 if (innerProto.endToken == pt) {
4338 innerInst.endToken = it;
4339 innerProto = innerProto.outerSDType;
4340 innerInst = innerInst.outerSDType;
4341 }
4342 }
4343
4344 /*
4345 * Clone and insert the terminator, either '}' or ';'
4346 */
4347 it = pt.CopyToken (pt);
4348 SpliceSourceToken (it);
4349 instdecl.endToken = it;
4350
4351 return instdecl;
4352 }
4353
4354 /**
4355 * @brief Splice a source token in just before the type's beginning keyword.
4356 */
4357 private void SpliceSourceToken (Token it)
4358 {
4359 it.nextToken = this.begToken;
4360 (it.prevToken = this.begToken.prevToken).nextToken = it;
4361 this.begToken.prevToken = it;
4362 }
4363
4364 /**
4365 * @brief Read one of these in from the object file.
4366 * @param sdTypes = dictionary of script-defined types, not yet complete
4367 * @param name = script-visible name of this type
4368 * @param objFileReader = reads from the object file
4369 * @param asmFileWriter = writes to the disassembly file (might be null)
4370 */
4371 public static TokenDeclSDType ReadFromFile (Dictionary<string, TokenDeclSDType> sdTypes, string name,
4372 BinaryReader objFileReader, TextWriter asmFileWriter)
4373 {
4374 string file = objFileReader.ReadString ();
4375 int line = objFileReader.ReadInt32 ();
4376 int posn = objFileReader.ReadInt32 ();
4377 byte code = objFileReader.ReadByte ();
4378 TokenName n = new TokenName (null, file, line, posn, name);
4379 TokenDeclSDType sdt;
4380 switch (code) {
4381 case CLASS: {
4382 sdt = new TokenDeclSDTypeClass (n, false);
4383 break;
4384 }
4385 case DELEGATE: {
4386 sdt = new TokenDeclSDTypeDelegate (n);
4387 break;
4388 }
4389 case INTERFACE: {
4390 sdt = new TokenDeclSDTypeInterface (n);
4391 break;
4392 }
4393 case TYPEDEF: {
4394 sdt = new TokenDeclSDTypeTypedef (n);
4395 break;
4396 }
4397 default: throw new Exception ();
4398 }
4399 sdt.sdTypes = sdTypes;
4400 sdt.sdTypeIndex = objFileReader.ReadInt32 ();
4401 sdt.ReadFromFile (objFileReader, asmFileWriter);
4402 return sdt;
4403 }
4404
4405 /**
4406 * @brief Convert a typename string to a type token
4407 * @param name = script-visible name of token to create,
4408 * either a script-defined type or an LSL-defined type
4409 * @returns type token
4410 */
4411 protected TokenType MakeTypeToken (string name)
4412 {
4413 TokenDeclSDType sdtdecl;
4414 if (sdTypes.TryGetValue (name, out sdtdecl)) return sdtdecl.MakeRefToken (this);
4415 return TokenType.FromLSLType (this, name);
4416 }
4417
4418 // debugging - returns, eg, 'Dictionary<T,U>.Enumerator.Node'
4419 public override void DebString (StringBuilder sb)
4420 {
4421 // get long name broken down into segments from outermost to this
4422 Stack<TokenDeclSDType> declStack = new Stack<TokenDeclSDType> ();
4423 for (TokenDeclSDType decl = this; decl != null; decl = decl.outerSDType) {
4424 declStack.Push (decl);
4425 }
4426
4427 // output each segment's name followed by our args for it
4428 // starting with outermost and ending with this
4429 while (declStack.Count > 0) {
4430 TokenDeclSDType decl = declStack.Pop ();
4431 sb.Append (decl.shortName.val);
4432 if (decl.genParams != null) {
4433 sb.Append ('<');
4434 string[] parms = new string[decl.genParams.Count];
4435 foreach (KeyValuePair<string, int> kvp in decl.genParams) {
4436 parms[kvp.Value] = kvp.Key;
4437 }
4438 for (int j = 0; j < parms.Length;) {
4439 sb.Append (parms[j]);
4440 if (++ j < parms.Length) sb.Append (',');
4441 }
4442 sb.Append ('>');
4443 }
4444 if (declStack.Count > 0) sb.Append ('.');
4445 }
4446 }
4447 }
4448
4449 public class TokenDeclSDTypeClass : TokenDeclSDType {
4450 public List<TokenDeclSDTypeInterface> implements = new List<TokenDeclSDTypeInterface> ();
4451 public TokenDeclVar instFieldInit; // $instfieldinit function to do instance field initializations
4452 public TokenDeclVar staticFieldInit; // $staticfieldinit function to do static field initializations
4453
4454 public Dictionary<string, int> intfIndices = new Dictionary<string, int> (); // longname => this.iFaces index
4455 public TokenDeclSDTypeInterface[] iFaces; // array of implemented interfaces
4456 // low-end entries copied from rootward classes
4457 public TokenDeclVar[][] iImplFunc; // iImplFunc[i][j]:
4458 // low-end [i] entries copied from rootward classes
4459 // i = interface number from this.intfIndices[name]
4460 // j = method of interface from iface.methods[name].vTableIndex
4461
4462 public TokenType arrayOfType; // if array, it's an array of this type, else null
4463 public int arrayOfRank; // if array, it has this number of dimensions, else zero
4464
4465 public bool slotsAssigned; // set true when slots have been assigned...
4466 public XMRInstArSizes instSizes = new XMRInstArSizes ();
4467 // number of instance fields of various types
4468 public int numVirtFuncs; // number of virtual functions
4469 public int numInterfaces; // number of implemented interfaces
4470
4471 private string extendsStr;
4472 private string arrayOfTypeStr;
4473 private List<StackedMethod> stackedMethods;
4474 private List<StackedIFace> stackedIFaces;
4475
4476 public DynamicMethod[] vDynMeths; // virtual method entrypoints
4477 public Type[] vMethTypes; // virtual method delegate types
4478 public DynamicMethod[][] iDynMeths; // interface method entrypoints
4479 public Type[][] iMethTypes; // interface method types
4480 // low-end [i] entries copied from rootward classes
4481 // i = interface number from this.intfIndices[name]
4482 // j = method of interface from iface.methods[name].vTableIndex
4483
4484 public TokenDeclSDTypeClass (TokenName shortName, bool isPartial) : base (shortName)
4485 {
4486 this.shortName = shortName;
4487 this.isPartial = isPartial;
4488 }
4489
4490 protected override TokenDeclSDType MakeBlank (TokenName shortName)
4491 {
4492 return new TokenDeclSDTypeClass (shortName, false);
4493 }
4494
4495 public override TokenType MakeRefToken (Token t)
4496 {
4497 return new TokenTypeSDTypeClass (t, this);
4498 }
4499
4500 public override Type GetSysType ()
4501 {
4502 return typeof (XMRSDTypeClObj);
4503 }
4504
4505 /**
4506 * @brief See if the class implements the interface.
4507 * Do a recursive (deep) check in all rootward classes.
4508 */
4509 public bool CanCastToIntf (TokenDeclSDTypeInterface intf)
4510 {
4511 if (this.implements.Contains (intf)) return true;
4512 if (this.extends == null) return false;
4513 return this.extends.CanCastToIntf (intf);
4514 }
4515
4516 /**
4517 * @brief Write enough out so we can reconstruct with ReadFromFile.
4518 */
4519 public override void WriteToFile (BinaryWriter objFileWriter)
4520 {
4521 objFileWriter.Write (this.file);
4522 objFileWriter.Write (this.line);
4523 objFileWriter.Write (this.posn);
4524 objFileWriter.Write ((byte)CLASS);
4525 objFileWriter.Write (this.sdTypeIndex);
4526
4527 this.instSizes.WriteToFile (objFileWriter);
4528 objFileWriter.Write (numVirtFuncs);
4529
4530 if (extends == null) {
4531 objFileWriter.Write ("");
4532 } else {
4533 objFileWriter.Write (extends.longName.val);
4534 }
4535
4536 objFileWriter.Write (arrayOfRank);
4537 if (arrayOfRank > 0) objFileWriter.Write (arrayOfType.ToString ());
4538
4539 foreach (TokenDeclVar meth in members) {
4540 if ((meth.retType != null) && (meth.vTableIndex >= 0)) {
4541 objFileWriter.Write (meth.vTableIndex);
4542 objFileWriter.Write (meth.GetObjCodeName ());
4543 objFileWriter.Write (meth.GetDelType ().decl.GetWholeSig ());
4544 }
4545 }
4546 objFileWriter.Write (-1);
4547
4548 int numIFaces = iImplFunc.Length;
4549 objFileWriter.Write (numIFaces);
4550 for (int i = 0; i < numIFaces; i ++) {
4551 objFileWriter.Write (iFaces[i].longName.val);
4552 TokenDeclVar[] meths = iImplFunc[i];
4553 int numMeths = 0;
4554 if (meths != null) numMeths = meths.Length;
4555 objFileWriter.Write (numMeths);
4556 for (int j = 0; j < numMeths; j ++) {
4557 TokenDeclVar meth = meths[j];
4558 objFileWriter.Write (meth.vTableIndex);
4559 objFileWriter.Write (meth.GetObjCodeName ());
4560 objFileWriter.Write (meth.GetDelType ().decl.GetWholeSig ());
4561 }
4562 }
4563 }
4564
4565 /**
4566 * @brief Reconstruct from the file.
4567 */
4568 public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
4569 {
4570 instSizes.ReadFromFile (objFileReader);
4571 numVirtFuncs = objFileReader.ReadInt32 ();
4572
4573 extendsStr = objFileReader.ReadString ();
4574 arrayOfRank = objFileReader.ReadInt32 ();
4575 if (arrayOfRank > 0) arrayOfTypeStr = objFileReader.ReadString ();
4576
4577 if (asmFileWriter != null) {
4578 instSizes.WriteAsmFile (asmFileWriter, extendsStr + "." + shortName.val + ".numInst");
4579 }
4580
4581 stackedMethods = new List<StackedMethod> ();
4582 int vTableIndex;
4583 while ((vTableIndex = objFileReader.ReadInt32 ()) >= 0) {
4584 StackedMethod sm;
4585 sm.methVTI = vTableIndex;
4586 sm.methName = objFileReader.ReadString ();
4587 sm.methSig = objFileReader.ReadString ();
4588 stackedMethods.Add (sm);
4589 }
4590
4591 int numIFaces = objFileReader.ReadInt32 ();
4592 if (numIFaces > 0) {
4593 iDynMeths = new DynamicMethod[numIFaces][];
4594 iMethTypes = new Type[numIFaces][];
4595 stackedIFaces = new List<StackedIFace> ();
4596 for (int i = 0; i < numIFaces; i ++) {
4597 string iFaceName = objFileReader.ReadString ();
4598 intfIndices[iFaceName] = i;
4599 int numMeths = objFileReader.ReadInt32 ();
4600 iDynMeths[i] = new DynamicMethod[numMeths];
4601 iMethTypes[i] = new Type[numMeths];
4602 for (int j = 0; j < numMeths; j ++) {
4603 StackedIFace si;
4604 si.iFaceIndex = i;
4605 si.methIndex = j;
4606 si.vTableIndex = objFileReader.ReadInt32 ();
4607 si.methName = objFileReader.ReadString ();
4608 si.methSig = objFileReader.ReadString ();
4609 stackedIFaces.Add (si);
4610 }
4611 }
4612 }
4613 }
4614
4615 private struct StackedMethod {
4616 public int methVTI;
4617 public string methName;
4618 public string methSig;
4619 }
4620
4621 private struct StackedIFace {
4622 public int iFaceIndex; // which implemented interface
4623 public int methIndex; // which method of that interface
4624 public int vTableIndex; // <0: implemented by non-virtual; else: implemented by virtual
4625 public string methName; // object code name of implementing method (GetObjCodeName)
4626 public string methSig; // method signature incl return type (GetWholeSig)
4627 }
4628
4629 /**
4630 * @brief Called after all dynamic method code has been generated to fill in vDynMeths and vMethTypes
4631 * Also fills in iDynMeths, iMethTypes.
4632 */
4633 public void FillVTables (ScriptObjCode scriptObjCode)
4634 {
4635 if (extendsStr != null) {
4636 if (extendsStr != "") {
4637 extends = (TokenDeclSDTypeClass)scriptObjCode.sdObjTypesName[extendsStr];
4638 extends.FillVTables (scriptObjCode);
4639 }
4640 extendsStr = null;
4641 }
4642 if (arrayOfTypeStr != null) {
4643 arrayOfType = MakeTypeToken (arrayOfTypeStr);
4644 arrayOfTypeStr = null;
4645 }
4646
4647 if ((numVirtFuncs > 0) && (stackedMethods != null)) {
4648
4649 /*
4650 * Allocate arrays big enough for mine plus type we are extending.
4651 */
4652 vDynMeths = new DynamicMethod[numVirtFuncs];
4653 vMethTypes = new Type[numVirtFuncs];
4654
4655 /*
4656 * Fill in low parts from type we are extending.
4657 */
4658 if (extends != null) {
4659 int n = extends.numVirtFuncs;
4660 for (int i = 0; i < n; i ++) {
4661 vDynMeths[i] = extends.vDynMeths[i];
4662 vMethTypes[i] = extends.vMethTypes[i];
4663 }
4664 }
4665
4666 /*
4667 * Fill in high parts with my own methods.
4668 * Might also overwrite lower ones with 'override' methods.
4669 */
4670 foreach (StackedMethod sm in stackedMethods) {
4671 int i = sm.methVTI;
4672 string methName = sm.methName;
4673 DynamicMethod dm;
4674 if (scriptObjCode.dynamicMethods.TryGetValue (methName, out dm)) {
4675 // method is not abstract
4676 vDynMeths[i] = dm;
4677 vMethTypes[i] = GetDynamicMethodDelegateType (dm, sm.methSig);
4678 }
4679 }
4680 stackedMethods = null;
4681 }
4682
4683 if (stackedIFaces != null) {
4684 foreach (StackedIFace si in stackedIFaces) {
4685 int i = si.iFaceIndex;
4686 int j = si.methIndex;
4687 int vti = si.vTableIndex;
4688 string methName = si.methName;
4689 DynamicMethod dm = scriptObjCode.dynamicMethods[methName];
4690 iDynMeths[i][j] = (vti < 0) ? dm : vDynMeths[vti];
4691 iMethTypes[i][j] = GetDynamicMethodDelegateType (dm, si.methSig);
4692 }
4693 stackedIFaces = null;
4694 }
4695 }
4696
4697 private Type GetDynamicMethodDelegateType (DynamicMethod dm, string methSig)
4698 {
4699 Type retType = dm.ReturnType;
4700 ParameterInfo[] pi = dm.GetParameters ();
4701 Type[] argTypes = new Type[pi.Length];
4702 for (int j = 0; j < pi.Length; j ++) {
4703 argTypes[j] = pi[j].ParameterType;
4704 }
4705 return DelegateCommon.GetType (retType, argTypes, methSig);
4706 }
4707
4708 public override void DebString (StringBuilder sb)
4709 {
4710 /*
4711 * Don't output if array of some type.
4712 * They will be re-instantiated as referenced by rest of script.
4713 */
4714 if (arrayOfType != null) return;
4715
4716 /*
4717 * This class name and extended/implemented type declaration.
4718 */
4719 sb.Append ("class ");
4720 sb.Append (shortName.val);
4721 bool first = true;
4722 if (extends != null) {
4723 sb.Append (" : ");
4724 sb.Append (extends.longName);
4725 first = false;
4726 }
4727 foreach (TokenDeclSDType impld in implements) {
4728 sb.Append (first ? " : " : ", ");
4729 sb.Append (impld.longName);
4730 first = false;
4731 }
4732 sb.Append (" {");
4733
4734 /*
4735 * Inner type definitions.
4736 */
4737 foreach (TokenDeclSDType subs in innerSDTypes.Values) {
4738 subs.DebString (sb);
4739 }
4740
4741 /*
4742 * Members (fields, methods, properties).
4743 */
4744 foreach (TokenDeclVar memb in members) {
4745 if ((memb == instFieldInit) || (memb == staticFieldInit)) {
4746 memb.DebStringInitFields (sb);
4747 } else if (memb.retType != null) {
4748 memb.DebString (sb);
4749 }
4750 }
4751
4752 sb.Append ('}');
4753 }
4754 }
4755
4756 public class TokenDeclSDTypeDelegate : TokenDeclSDType {
4757 private TokenType retType;
4758 private TokenType[] argTypes;
4759
4760 private string argSig;
4761 private string wholeSig;
4762 private Type sysType;
4763 private Type retSysType;
4764 private Type[] argSysTypes;
4765
4766 private string retStr;
4767 private string[] argStrs;
4768
4769 private static Dictionary<string, TokenDeclSDTypeDelegate> inlines = new Dictionary<string, TokenDeclSDTypeDelegate> ();
4770 private static Dictionary<Type, string> inlrevs = new Dictionary<Type, string> ();
4771
4772 public TokenDeclSDTypeDelegate (TokenName shortName) : base (shortName)
4773 {
4774 this.shortName = shortName;
4775 }
4776 public void SetRetArgTypes (TokenType retType, TokenType[] argTypes)
4777 {
4778 this.retType = retType;
4779 this.argTypes = argTypes;
4780 }
4781
4782 protected override TokenDeclSDType MakeBlank (TokenName shortName)
4783 {
4784 return new TokenDeclSDTypeDelegate (shortName);
4785 }
4786
4787 public override TokenType MakeRefToken (Token t)
4788 {
4789 return new TokenTypeSDTypeDelegate (t, this);
4790 }
4791
4792 /**
4793 * @brief Get system type for the whole delegate.
4794 */
4795 public override Type GetSysType ()
4796 {
4797 if (sysType == null) FillInStuff ();
4798 return sysType;
4799 }
4800
4801 /**
4802 * @brief Get the function's return value type (TokenTypeVoid if void, never null)
4803 */
4804 public TokenType GetRetType ()
4805 {
4806 if (retType == null) FillInStuff ();
4807 return retType;
4808 }
4809
4810 /**
4811 * @brief Get the function's argument types
4812 */
4813 public TokenType[] GetArgTypes ()
4814 {
4815 if (argTypes == null) FillInStuff ();
4816 return argTypes;
4817 }
4818
4819 /**
4820 * @brief Get signature for the whole delegate, eg, "void(integer,list)"
4821 */
4822 public string GetWholeSig ()
4823 {
4824 if (wholeSig == null) FillInStuff ();
4825 return wholeSig;
4826 }
4827
4828 /**
4829 * @brief Get signature for the arguments, eg, "(integer,list)"
4830 */
4831 public string GetArgSig ()
4832 {
4833 if (argSig == null) FillInStuff ();
4834 return argSig;
4835 }
4836
4837 /**
4838 * @brief Find out how to create one of these delegates.
4839 */
4840 public ConstructorInfo GetConstructorInfo ()
4841 {
4842 if (sysType == null) FillInStuff ();
4843 return sysType.GetConstructor (DelegateCommon.constructorArgTypes);
4844 }
4845
4846 /**
4847 * @brief Find out how to call what one of these delegates points to.
4848 */
4849 public MethodInfo GetInvokerInfo ()
4850 {
4851 if (sysType == null) FillInStuff ();
4852 return sysType.GetMethod ("Invoke", argSysTypes);
4853 }
4854
4855 /**
4856 * @brief Write enough out to a file so delegate can be reconstructed in ReadFromFile().
4857 */
4858 public override void WriteToFile (BinaryWriter objFileWriter)
4859 {
4860 objFileWriter.Write (this.file);
4861 objFileWriter.Write (this.line);
4862 objFileWriter.Write (this.posn);
4863 objFileWriter.Write ((byte)DELEGATE);
4864 objFileWriter.Write (this.sdTypeIndex);
4865
4866 objFileWriter.Write (retType.ToString ());
4867 int nArgs = argTypes.Length;
4868 objFileWriter.Write (nArgs);
4869 for (int i = 0; i < nArgs; i ++) {
4870 objFileWriter.Write (argTypes[i].ToString ());
4871 }
4872 }
4873
4874 /**
4875 * @brief Read that data from file so we can reconstruct.
4876 * Don't actually reconstruct yet in case any forward-referenced types are undefined.
4877 */
4878 public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
4879 {
4880 retStr = objFileReader.ReadString ();
4881 int nArgs = objFileReader.ReadInt32 ();
4882 if (asmFileWriter != null) {
4883 asmFileWriter.Write (" delegate " + retStr + " " + longName.val + "(");
4884 }
4885 argStrs = new string[nArgs];
4886 for (int i = 0; i < nArgs; i ++) {
4887 argStrs[i] = objFileReader.ReadString ();
4888 if (asmFileWriter != null) {
4889 if (i > 0) asmFileWriter.Write (",");
4890 asmFileWriter.Write (argStrs[i]);
4891 }
4892 }
4893 if (asmFileWriter != null) {
4894 asmFileWriter.WriteLine (");");
4895 }
4896 }
4897
4898 /**
4899 * @brief Fill in missing internal data.
4900 */
4901 private void FillInStuff ()
4902 {
4903 int nArgs;
4904
4905 /*
4906 * This happens when the node was restored via ReadFromFile().
4907 * It leaves the types in retStr/argStrs for resolution after
4908 * all definitions have been read from the object file in case
4909 * there are forward references.
4910 */
4911 if (retType == null) {
4912 retType = MakeTypeToken (retStr);
4913 }
4914 if (argTypes == null) {
4915 nArgs = argStrs.Length;
4916 argTypes = new TokenType[nArgs];
4917 for (int i = 0; i < nArgs; i ++) {
4918 argTypes[i] = MakeTypeToken (argStrs[i]);
4919 }
4920 }
4921
4922 /*
4923 * Fill in system types from token types.
4924 * Might as well build the signature strings too from token types.
4925 */
4926 retSysType = retType.ToSysType();
4927
4928 nArgs = argTypes.Length;
4929 StringBuilder sb = new StringBuilder ();
4930 argSysTypes = new Type[nArgs];
4931 sb.Append ('(');
4932 for (int i = 0; i < nArgs; i ++) {
4933 if (i > 0) sb.Append (',');
4934 sb.Append (argTypes[i].ToString ());
4935 argSysTypes[i] = argTypes[i].ToSysType ();
4936 }
4937 sb.Append (')');
4938 argSig = sb.ToString ();
4939 wholeSig = retType.ToString () + argSig;
4940
4941 /*
4942 * Now we can create a system delegate type from the given
4943 * return and argument types. Give it an unique name using
4944 * the whole signature string.
4945 */
4946 sysType = DelegateCommon.GetType (retSysType, argSysTypes, wholeSig);
4947 }
4948
4949 /**
4950 * @brief create delegate reference token for inline functions.
4951 * there is just one instance of these per inline function
4952 * shared by all scripts, and it is just used when the
4953 * script engine is loaded.
4954 */
4955 public static TokenDeclSDTypeDelegate CreateInline (TokenType retType, TokenType[] argTypes)
4956 {
4957 TokenDeclSDTypeDelegate decldel;
4958
4959 /*
4960 * Name it after the whole signature string.
4961 */
4962 StringBuilder sb = new StringBuilder ("$inline");
4963 sb.Append (retType.ToString ());
4964 sb.Append ("(");
4965 bool first = true;
4966 foreach (TokenType at in argTypes) {
4967 if (!first) sb.Append (",");
4968 sb.Append (at.ToString ());
4969 first = false;
4970 }
4971 sb.Append (")");
4972 string inlname = sb.ToString ();
4973 if (!inlines.TryGetValue (inlname, out decldel)) {
4974
4975 /*
4976 * Create the corresponding declaration and link to it
4977 */
4978 TokenName name = new TokenName (null, inlname);
4979 decldel = new TokenDeclSDTypeDelegate (name);
4980 decldel.retType = retType;
4981 decldel.argTypes = argTypes;
4982 inlines.Add (inlname, decldel);
4983 inlrevs.Add (decldel.GetSysType (), inlname);
4984 }
4985 return decldel;
4986 }
4987
4988 public static string TryGetInlineName (Type sysType)
4989 {
4990 string name;
4991 if (!inlrevs.TryGetValue (sysType, out name)) return null;
4992 return name;
4993 }
4994
4995 public static Type TryGetInlineSysType (string name)
4996 {
4997 TokenDeclSDTypeDelegate decl;
4998 if (!inlines.TryGetValue (name, out decl)) return null;
4999 return decl.GetSysType ();
5000 }
5001 }
5002
5003 public class TokenDeclSDTypeInterface : TokenDeclSDType {
5004 public VarDict methsNProps = new VarDict (false);
5005 // any class that implements this interface
5006 // must implement all of these methods & properties
5007
5008 public List<TokenDeclSDTypeInterface> implements = new List<TokenDeclSDTypeInterface> ();
5009 // any class that implements this interface
5010 // must also implement all of the methods & properties
5011 // of all of these interfaces
5012
5013 public TokenDeclSDTypeInterface (TokenName shortName) : base (shortName)
5014 {
5015 this.shortName = shortName;
5016 }
5017
5018 protected override TokenDeclSDType MakeBlank (TokenName shortName)
5019 {
5020 return new TokenDeclSDTypeInterface (shortName);
5021 }
5022
5023 public override TokenType MakeRefToken (Token t)
5024 {
5025 return new TokenTypeSDTypeInterface (t, this);
5026 }
5027
5028 public override Type GetSysType ()
5029 {
5030 // interfaces are implemented as arrays of delegates
5031 // they are taken from iDynMeths[interfaceIndex] of a script-defined class object
5032 return typeof (Delegate[]);
5033 }
5034
5035 public override void WriteToFile (BinaryWriter objFileWriter)
5036 {
5037 objFileWriter.Write (this.file);
5038 objFileWriter.Write (this.line);
5039 objFileWriter.Write (this.posn);
5040 objFileWriter.Write ((byte)INTERFACE);
5041 objFileWriter.Write (this.sdTypeIndex);
5042 }
5043
5044 public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
5045 { }
5046
5047 /**
5048 * @brief Add this interface to the list of interfaces implemented by a class if not already.
5049 * And also add this interface's implemented interfaces to the class for those not already there,
5050 * just as if the class itself had declared to implement those interfaces.
5051 */
5052 public void AddToClassDecl (TokenDeclSDTypeClass tokdeclcl)
5053 {
5054 if (!tokdeclcl.implements.Contains (this)) {
5055 tokdeclcl.implements.Add (this);
5056 foreach (TokenDeclSDTypeInterface subimpl in this.implements) {
5057 subimpl.AddToClassDecl (tokdeclcl);
5058 }
5059 }
5060 }
5061
5062 /**
5063 * @brief See if the 'this' interface implements the new interface.
5064 * Do a recursive (deep) check.
5065 */
5066 public bool Implements (TokenDeclSDTypeInterface newDecl)
5067 {
5068 foreach (TokenDeclSDTypeInterface ii in this.implements) {
5069 if (ii == newDecl) return true;
5070 if (ii.Implements (newDecl)) return true;
5071 }
5072 return false;
5073 }
5074
5075 /**
5076 * @brief Scan an interface and all its implemented interfaces for a method or property
5077 * @param scg = script code generator (ie, which script is being compiled)
5078 * @param fieldName = name of the member being looked for
5079 * @param argsig = the method's argument signature
5080 * @returns null: no such member; intf = undefined
5081 * else: member; intf = which interface actually found in
5082 */
5083 public TokenDeclVar FindIFaceMember (ScriptCodeGen scg, TokenName fieldName, TokenType[] argsig, out TokenDeclSDTypeInterface intf)
5084 {
5085 intf = this;
5086 TokenDeclVar var = scg.FindSingleMember (this.methsNProps, fieldName, argsig);
5087 if (var == null) {
5088 foreach (TokenDeclSDTypeInterface ii in this.implements) {
5089 var = ii.FindIFaceMember (scg, fieldName, argsig, out intf);
5090 if (var != null) break;
5091 }
5092 }
5093 return var;
5094 }
5095 }
5096
5097 public class TokenDeclSDTypeTypedef : TokenDeclSDType {
5098
5099 public TokenDeclSDTypeTypedef (TokenName shortName) : base (shortName)
5100 {
5101 this.shortName = shortName;
5102 }
5103
5104 protected override TokenDeclSDType MakeBlank (TokenName shortName)
5105 {
5106 return new TokenDeclSDTypeTypedef (shortName);
5107 }
5108
5109 public override TokenType MakeRefToken (Token t)
5110 {
5111 // if our body is a single type token, that is what we return
5112 // otherwise return null saying maybe our body needs some substitutions
5113 if (!(this.nextToken is TokenType)) return null;
5114 if (this.nextToken.nextToken != this.endToken) {
5115 this.nextToken.nextToken.ErrorMsg ("extra tokens for typedef");
5116 return null;
5117 }
5118 return (TokenType)this.nextToken.CopyToken (t);
5119 }
5120
5121 public override Type GetSysType ()
5122 {
5123 // we are just a macro
5124 // we are asked for system type because we are cataloged
5125 // but we don't really have one so return null
5126 return null;
5127 }
5128
5129 public override void WriteToFile (BinaryWriter objFileWriter)
5130 {
5131 objFileWriter.Write (this.file);
5132 objFileWriter.Write (this.line);
5133 objFileWriter.Write (this.posn);
5134 objFileWriter.Write ((byte)TYPEDEF);
5135 objFileWriter.Write (this.sdTypeIndex);
5136 }
5137
5138 public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
5139 { }
5140 }
5141
5142 /**
5143 * @brief Script-defined type references.
5144 * These occur in the source code wherever it specifies (eg, variable declaration) a script-defined type.
5145 * These must be copyable via CopyToken().
5146 */
5147 public abstract class TokenTypeSDType : TokenType {
5148 public TokenTypeSDType (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
5149 public TokenTypeSDType (Token t) : base (t) { }
5150 public abstract TokenDeclSDType GetDecl ();
5151 public abstract void SetDecl (TokenDeclSDType decl);
5152 }
5153
5154 public class TokenTypeSDTypeClass : TokenTypeSDType {
5155 private static readonly FieldInfo iarSDTClObjsFieldInfo = typeof (XMRInstArrays).GetField ("iarSDTClObjs");
5156
5157 public TokenDeclSDTypeClass decl;
5158
5159 public TokenTypeSDTypeClass (Token t, TokenDeclSDTypeClass decl) : base (t)
5160 {
5161 this.decl = decl;
5162 }
5163 public override TokenDeclSDType GetDecl ()
5164 {
5165 return decl;
5166 }
5167 public override void SetDecl (TokenDeclSDType decl)
5168 {
5169 this.decl = (TokenDeclSDTypeClass)decl;
5170 }
5171 public override string ToString ()
5172 {
5173 return decl.longName.val;
5174 }
5175 public override Type ToSysType ()
5176 {
5177 return typeof (XMRSDTypeClObj);
5178 }
5179
5180 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias)
5181 {
5182 declVar.vTableArray = iarSDTClObjsFieldInfo;
5183 declVar.vTableIndex = ias.iasSDTClObjs ++;
5184 }
5185
5186 // debugging
5187 public override void DebString (StringBuilder sb)
5188 {
5189 sb.Append (decl.longName);
5190 }
5191 }
5192
5193 public class TokenTypeSDTypeDelegate : TokenTypeSDType {
5194 private static readonly FieldInfo iarObjectsFieldInfo = typeof (XMRInstArrays).GetField ("iarObjects");
5195
5196 public TokenDeclSDTypeDelegate decl;
5197
5198 /**
5199 * @brief create a reference to an explicitly declared delegate
5200 * @param t = where the reference is being made in the source file
5201 * @param decl = the explicit delegate declaration
5202 */
5203 public TokenTypeSDTypeDelegate (Token t, TokenDeclSDTypeDelegate decl) : base (t)
5204 {
5205 this.decl = decl;
5206 }
5207 public override TokenDeclSDType GetDecl ()
5208 {
5209 return decl;
5210 }
5211 public override void SetDecl (TokenDeclSDType decl)
5212 {
5213 this.decl = (TokenDeclSDTypeDelegate)decl;
5214 }
5215
5216 /**
5217 * @brief create a reference to a possibly anonymous delegate
5218 * @param t = where the reference is being made in the source file
5219 * @param retType = return type (TokenTypeVoid if void, never null)
5220 * @param argTypes = script-visible argument types
5221 * @param tokenScript = what script this is part of
5222 */
5223 public TokenTypeSDTypeDelegate (Token t, TokenType retType, TokenType[] argTypes, TokenScript tokenScript) : base (t)
5224 {
5225 TokenDeclSDTypeDelegate decldel;
5226
5227 /*
5228 * See if we already have a matching declared one cataloged.
5229 */
5230 int nArgs = argTypes.Length;
5231 foreach (TokenDeclSDType decl in tokenScript.sdSrcTypesValues) {
5232 if (decl is TokenDeclSDTypeDelegate) {
5233 decldel = (TokenDeclSDTypeDelegate)decl;
5234 TokenType rt = decldel.GetRetType ();
5235 TokenType[] ats = decldel.GetArgTypes ();
5236 if ((rt.ToString () == retType.ToString ()) && (ats.Length == nArgs)) {
5237 for (int i = 0; i < nArgs; i ++) {
5238 if (ats[i].ToString () != argTypes[i].ToString ()) goto nomatch;
5239 }
5240 this.decl = decldel;
5241 return;
5242 }
5243 }
5244 nomatch:;
5245 }
5246
5247 /*
5248 * No such luck, create a new anonymous declaration.
5249 */
5250 StringBuilder sb = new StringBuilder ("$anondel$");
5251 sb.Append (retType.ToString ());
5252 sb.Append ("(");
5253 bool first = true;
5254 foreach (TokenType at in argTypes) {
5255 if (!first) sb.Append (",");
5256 sb.Append (at.ToString ());
5257 first = false;
5258 }
5259 sb.Append (")");
5260 TokenName name = new TokenName (t, sb.ToString ());
5261 decldel = new TokenDeclSDTypeDelegate (name);
5262 decldel.SetRetArgTypes (retType, argTypes);
5263 tokenScript.sdSrcTypesAdd (name.val, decldel);
5264 this.decl = decldel;
5265 }
5266
5267 public override Type ToSysType ()
5268 {
5269 return decl.GetSysType ();
5270 }
5271
5272 public override string ToString ()
5273 {
5274 return decl.longName.val;
5275 }
5276
5277 /**
5278 * @brief Assign slots in the gblObjects[] array because we have to typecast out in any case.
5279 * Likewise with the sdtcObjects[] array.
5280 */
5281 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias)
5282 {
5283 declVar.vTableArray = iarObjectsFieldInfo;
5284 declVar.vTableIndex = ias.iasObjects ++;
5285 }
5286
5287 /**
5288 * @brief create delegate reference token for inline functions.
5289 */
5290 public TokenTypeSDTypeDelegate (TokenType retType, TokenType[] argTypes) : base (null)
5291 {
5292 this.decl = TokenDeclSDTypeDelegate.CreateInline (retType, argTypes);
5293 }
5294
5295 // debugging
5296 public override void DebString (StringBuilder sb)
5297 {
5298 sb.Append (decl.longName);
5299 }
5300 }
5301
5302 public class TokenTypeSDTypeInterface : TokenTypeSDType {
5303 private static readonly FieldInfo iarSDTIntfObjsFieldInfo = typeof (XMRInstArrays).GetField ("iarSDTIntfObjs");
5304
5305 public TokenDeclSDTypeInterface decl;
5306
5307 public TokenTypeSDTypeInterface (Token t, TokenDeclSDTypeInterface decl) : base (t)
5308 {
5309 this.decl = decl;
5310 }
5311 public override TokenDeclSDType GetDecl ()
5312 {
5313 return decl;
5314 }
5315 public override void SetDecl (TokenDeclSDType decl)
5316 {
5317 this.decl = (TokenDeclSDTypeInterface)decl;
5318 }
5319
5320 public override string ToString ()
5321 {
5322 return decl.longName.val;
5323 }
5324 public override Type ToSysType ()
5325 {
5326 return typeof (Delegate[]);
5327 }
5328
5329 /**
5330 * @brief Assign slots in the gblSDTIntfObjs[] array
5331 * Likewise with the sdtcSDTIntfObjs[] array.
5332 */
5333 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias)
5334 {
5335 declVar.vTableArray = iarSDTIntfObjsFieldInfo;
5336 declVar.vTableIndex = ias.iasSDTIntfObjs ++;
5337 }
5338
5339 // debugging
5340 public override void DebString (StringBuilder sb)
5341 {
5342 sb.Append (decl.longName);
5343 }
5344 }
5345
5346 /**
5347 * @brief function argument list declaration
5348 */
5349 public class TokenArgDecl : Token
5350 {
5351 public VarDict varDict = new VarDict (false);
5352
5353 public TokenArgDecl (Token original) : base (original) { }
5354
5355 public bool AddArg (TokenType type, TokenName name)
5356 {
5357 TokenDeclVar var = new TokenDeclVar (name, null, null);
5358 var.name = name;
5359 var.type = type;
5360 var.vTableIndex = varDict.Count;
5361 return varDict.AddEntry (var);
5362 }
5363
5364 /**
5365 * @brief Get an array of the argument types.
5366 */
5367 private TokenType[] _types;
5368 public TokenType[] types {
5369 get {
5370 if (_types == null) {
5371 _types = new TokenType[varDict.Count];
5372 foreach (TokenDeclVar var in varDict) {
5373 _types[var.vTableIndex] = var.type;
5374 }
5375 }
5376 return _types;
5377 }
5378 }
5379
5380 /**
5381 * @brief Access the arguments as an array of variables.
5382 */
5383 private TokenDeclVar[] _vars;
5384 public TokenDeclVar[] vars {
5385 get {
5386 if (_vars == null) {
5387 _vars = new TokenDeclVar[varDict.Count];
5388 foreach (TokenDeclVar var in varDict) {
5389 _vars[var.vTableIndex] = var;
5390 }
5391 }
5392 return _vars;
5393 }
5394 }
5395
5396 /**
5397 * @brief Get argument signature string, eg, "(list,vector,integer)"
5398 */
5399 private string argSig = null;
5400 public string GetArgSig ()
5401 {
5402 if (argSig == null) {
5403 argSig = ScriptCodeGen.ArgSigString (types);
5404 }
5405 return argSig;
5406 }
5407 }
5408
5409 /**
5410 * @brief encapsulate a state declaration in a single token
5411 */
5412 public class TokenDeclState : Token {
5413
5414 public TokenName name; // null for default state
5415 public TokenStateBody body;
5416
5417 public TokenDeclState (Token original) : base (original) { }
5418
5419 public override void DebString (StringBuilder sb)
5420 {
5421 if (name == null) {
5422 sb.Append ("default");
5423 } else {
5424 sb.Append ("state ");
5425 sb.Append (name);
5426 }
5427 body.DebString (sb);
5428 }
5429 }
5430
5431 /**
5432 * @brief encapsulate the declaration of a field/function/method/property/variable.
5433 */
5434
5435 public enum Triviality { // function triviality: has no loops and doesn't call anything that has loops
5436 // such a function does not need all the CheckRun() and stack serialization stuff
5437 unknown, // function's Triviality unknown as of yet
5438 // - it does not have any loops or backward gotos
5439 // - nothing it calls is known to be complex
5440 trivial, // function known to be trivial
5441 // - it does not have any loops or backward gotos
5442 // - everything it calls is known to be trivial
5443 complex, // function known to be complex
5444 // - it has loops or backward gotos
5445 // - something it calls is known to be complex
5446 analyzing // triviality is being analyzed (used to detect recursive loops)
5447 };
5448
5449 public class TokenDeclVar : TokenStmt {
5450 public TokenName name; // vars: name; funcs: bare name, ie, no signature
5451 public TokenRVal init; // vars: null if none; funcs: null
5452 public bool constant; // vars: 'constant'; funcs: false
5453 public uint sdtFlags; // SDT_<*> flags
5454
5455 public CompValu location; // used by codegen to keep track of location
5456 public FieldInfo vTableArray;
5457 public int vTableIndex = -1; // local vars: not used (-1)
5458 // arg vars: index in the arg list
5459 // global vars: which slot in gbl<Type>s[] array it is stored
5460 // instance vars: which slot in inst<Types>s[] array it is stored
5461 // static vars: which slot in gbl<Type>s[] array it is stored
5462 // global funcs: not used (-1)
5463 // virt funcs: which slot in vTable[] array it is stored
5464 // instance func: not used (-1)
5465 public TokenDeclVar getProp; // if property, function that reads value
5466 public TokenDeclVar setProp; // if property, function that writes value
5467
5468 public TokenScript tokenScript; // what script this function is part of
5469 public TokenDeclSDType sdtClass; // null: script global member
5470 // else: member is part of this script-defined type
5471
5472 // function-only data:
5473
5474 public TokenType retType; // vars: null; funcs: TokenTypeVoid if void
5475 public TokenArgDecl argDecl; // vars: null; funcs: argument list prototypes
5476 public TokenStmtBlock body; // vars: null; funcs: statements (null iff abstract)
5477 public Dictionary<string, TokenStmtLabel> labels = new Dictionary<string, TokenStmtLabel> ();
5478 // all labels defined in the function
5479 public LinkedList<TokenDeclVar> localVars = new LinkedList<TokenDeclVar> ();
5480 // all local variables declared by this function
5481 // - doesn't include argument variables
5482 public TokenIntfImpl implements; // if script-defined type method, what interface method(s) this func implements
5483 public TokenRValCall baseCtorCall; // if script-defined type constructor, call to base constructor, if any
5484 public Triviality triviality = Triviality.unknown;
5485 // vars: unknown (not used for any thing); funcs: unknown/trivial/complex
5486 public LinkedList<TokenRValCall> unknownTrivialityCalls = new LinkedList<TokenRValCall> ();
5487 // reduction puts all calls here
5488 // compilation sorts it all out
5489
5490 public ScriptObjWriter ilGen; // codegen stores emitted code here
5491
5492 /**
5493 * @brief Set up a variable declaration token.
5494 * @param original = original source token that triggered definition
5495 * (for error messages)
5496 * @param func = null: global variable
5497 * else: local to the given function
5498 */
5499 public TokenDeclVar (Token original, TokenDeclVar func, TokenScript ts) : base (original)
5500 {
5501 if (func != null) {
5502 func.localVars.AddLast (this);
5503 }
5504 tokenScript = ts;
5505 }
5506
5507 /**
5508 * @brief Get/Set overall type
5509 * For vars, this is the type of the location
5510 * For funcs, this is the delegate type
5511 */
5512 private TokenType _type;
5513 public TokenType type {
5514 get {
5515 if (_type == null) {
5516 GetDelType ();
5517 }
5518 return _type;
5519 }
5520 set {
5521 _type = value;
5522 }
5523 }
5524
5525 /**
5526 * @brief Full name: <fulltype>.<name>(<argsig>)
5527 * (<argsig>) missing for fields/variables
5528 * <fulltype>. missing for top-level functions/variables
5529 */
5530 public string fullName {
5531 get {
5532 if (sdtClass == null) {
5533 if (retType == null) return name.val;
5534 return funcNameSig.val;
5535 }
5536 string ln = sdtClass.longName.val;
5537 if (retType == null) return ln + "." + name.val;
5538 return ln + "." + funcNameSig.val;
5539 }
5540 }
5541
5542 /**
5543 * @brief See if reading or writing the variable is trivial.
5544 * Note that for functions, this is reading the function itself,
5545 * as in 'someDelegate = SomeFunction;', not calling it as such.
5546 * The triviality of actually calling the function is IsFuncTrivial().
5547 */
5548 public bool IsVarTrivial (ScriptCodeGen scg)
5549 {
5550 // reading or writing a property involves a function call however
5551 // so we need to check the triviality of the property functions
5552 if ((getProp != null) && !getProp.IsFuncTrivial (scg)) return false;
5553 if ((setProp != null) && !setProp.IsFuncTrivial (scg)) return false;
5554
5555 // otherwise for variables it is a trivial access
5556 // and likewise for getting a delegate that points to a function
5557 return true;
5558 }
5559
5560 /***************************\
5561 * FUNCTION-only methods *
5562 \***************************/
5563
5564 private TokenName _funcNameSig; // vars: null; funcs: function name including argumet signature, eg, "PrintStuff(list,string)"
5565 public TokenName funcNameSig {
5566 get {
5567 if (_funcNameSig == null) {
5568 if (argDecl == null) return null;
5569 _funcNameSig = new TokenName (name, name.val + argDecl.GetArgSig ());
5570 }
5571 return _funcNameSig;
5572 }
5573 }
5574
5575 /**
5576 * @brief The bare function name, ie, without any signature info
5577 */
5578 public string GetSimpleName ()
5579 {
5580 return name.val;
5581 }
5582
5583 /**
5584 * @brief The function name as it appears in the object code,
5585 * ie, script-defined type name if any,
5586 * bare function name and argument signature,
5587 * eg, "MyClass.PrintStuff(string)"
5588 */
5589 public string GetObjCodeName ()
5590 {
5591 string objCodeName = "";
5592 if (sdtClass != null) {
5593 objCodeName += sdtClass.longName.val + ".";
5594 }
5595 objCodeName += funcNameSig.val;
5596 return objCodeName;
5597 }
5598
5599 /**
5600 * @brief Get delegate type.
5601 * This is the function's script-visible type,
5602 * It includes return type and all script-visible argument types.
5603 * @returns null for vars; else delegate type for funcs
5604 */
5605 public TokenTypeSDTypeDelegate GetDelType ()
5606 {
5607 if (argDecl == null) return null;
5608 if (_type == null) {
5609 if (tokenScript == null) {
5610 // used during startup to define inline function delegate types
5611 _type = new TokenTypeSDTypeDelegate (retType, argDecl.types);
5612 } else {
5613 // used for normal script processing
5614 _type = new TokenTypeSDTypeDelegate (this, retType, argDecl.types, tokenScript);
5615 }
5616 }
5617 if (!(_type is TokenTypeSDTypeDelegate)) return null;
5618 return (TokenTypeSDTypeDelegate)_type;
5619 }
5620
5621 /**
5622 * @brief See if the function's code itself is trivial or not.
5623 * If it contains any loops (calls to CheckRun()), it is not trivial.
5624 * If it calls anything that is not trivial, it is not trivial.
5625 * Otherwise it is trivial.
5626 */
5627 public bool IsFuncTrivial (ScriptCodeGen scg)
5628 {
5629 /*
5630 * If not really a function, assume it's a delegate.
5631 * And since we don't really know what functions it can point to,
5632 * assume it can point to a non-trivial one.
5633 */
5634 if (retType == null) return false;
5635
5636 /*
5637 * All virtual functions are non-trivial because although a particular
5638 * one might be trivial, it might be overidden with a non-trivial one.
5639 */
5640 if ((sdtFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE |
5641 ScriptReduce.SDT_VIRTUAL)) != 0) {
5642 return false;
5643 }
5644
5645 /*
5646 * Check the triviality status of the function.
5647 */
5648 switch (triviality) {
5649
5650 /*
5651 * Don't yet know if it is trivial.
5652 * We know at this point it doesn't have any direct looping.
5653 * But if it calls something that has loops, it isn't trivial.
5654 * Otherwise it is trivial.
5655 */
5656 case Triviality.unknown: {
5657
5658 /*
5659 * Mark that we are analyzing this function now. So if there are
5660 * any recursive call loops, that will show that the function is
5661 * non-trivial and the analysis will terminate at that point.
5662 */
5663 triviality = Triviality.analyzing;
5664
5665 /*
5666 * Check out everything else this function calls. If any say they
5667 * aren't trivial, then we say this function isn't trivial.
5668 */
5669 foreach (TokenRValCall call in unknownTrivialityCalls) {
5670 if (!call.IsRValTrivial (scg, null)) {
5671 triviality = Triviality.complex;
5672 return false;
5673 }
5674 }
5675
5676 /*
5677 * All functions called by this function are trivial, and this
5678 * function's code doesn't have any loops, so we can mark this
5679 * function as being trivial.
5680 */
5681 triviality = Triviality.trivial;
5682 return true;
5683 }
5684
5685 /*
5686 * We already know that it is trivial.
5687 */
5688 case Triviality.trivial: {
5689 return true;
5690 }
5691
5692 /*
5693 * We either know it is complex or are trying to analyze it already.
5694 * If we are already analyzing it, it means it has a recursive loop
5695 * and we assume those are non-trivial.
5696 */
5697 default: return false;
5698 }
5699 }
5700
5701 // debugging
5702 public override void DebString (StringBuilder sb)
5703 {
5704 DebStringSDTFlags (sb);
5705
5706 if (retType == null) {
5707 sb.Append (constant ? "constant" : type.ToString ());
5708 sb.Append (' ');
5709 sb.Append (name.val);
5710 if (init != null) {
5711 sb.Append (" = ");
5712 init.DebString (sb);
5713 }
5714 sb.Append (';');
5715 } else {
5716 if (!(retType is TokenTypeVoid)) {
5717 sb.Append (retType.ToString ());
5718 sb.Append (' ');
5719 }
5720 string namestr = name.val;
5721 if (namestr == "$ctor") namestr = "constructor";
5722 sb.Append (namestr);
5723 sb.Append (" (");
5724 for (int i = 0; i < argDecl.vars.Length; i ++) {
5725 if (i > 0) sb.Append (", ");
5726 sb.Append (argDecl.vars[i].type.ToString ());
5727 sb.Append (' ');
5728 sb.Append (argDecl.vars[i].name.val);
5729 }
5730 sb.Append (')');
5731 if (body == null) sb.Append (';');
5732 else {
5733 sb.Append (' ');
5734 body.DebString (sb);
5735 }
5736 }
5737 }
5738
5739 // debugging
5740 // - used to output contents of a $globalvarinit(), $instfieldinit() or $statisfieldinit() function
5741 // as a series of variable declaration statements with initial value assignments
5742 // so we get the initial value assignments done in same order as specified in script
5743 public void DebStringInitFields (StringBuilder sb)
5744 {
5745 if ((retType == null) || !(retType is TokenTypeVoid)) throw new Exception ("bad return type " + retType.GetType ().Name);
5746 if (argDecl.vars.Length != 0) throw new Exception ("has " + argDecl.vars.Length + " arg(s)");
5747
5748 for (Token stmt = body.statements; stmt != null; stmt = stmt.nextToken) {
5749
5750 /*
5751 * Body of the function should all be arithmetic statements (not eg for loops, if statements etc).
5752 */
5753 TokenRVal rval = ((TokenStmtRVal)stmt).rVal;
5754
5755 /*
5756 * And the opcode should be a simple assignment operator.
5757 */
5758 TokenRValOpBin rvob = (TokenRValOpBin)rval;
5759 if (!(rvob.opcode is TokenKwAssign)) throw new Exception ("bad op type " + rvob.opcode.GetType ().Name);
5760
5761 /*
5762 * Get field or variable being assigned to.
5763 */
5764 TokenDeclVar var = null;
5765 TokenRVal left = rvob.rValLeft;
5766 if (left is TokenLValIField) {
5767 TokenLValIField ifield = (TokenLValIField)left;
5768 TokenRValThis zhis = (TokenRValThis)ifield.baseRVal;
5769 TokenDeclSDTypeClass sdt = zhis.sdtClass;
5770 var = sdt.members.FindExact (ifield.fieldName.val, null);
5771 }
5772 if (left is TokenLValName) {
5773 TokenLValName global = (TokenLValName)left;
5774 var = global.stack.FindExact (global.name.val, null);
5775 }
5776 if (left is TokenLValSField) {
5777 TokenLValSField sfield = (TokenLValSField)left;
5778 TokenTypeSDTypeClass sdtc = (TokenTypeSDTypeClass)sfield.baseType;
5779 TokenDeclSDTypeClass decl = sdtc.decl;
5780 var = decl.members.FindExact (sfield.fieldName.val, null);
5781 }
5782 if (var == null) throw new Exception ("unknown var type " + left.GetType ().Name);
5783
5784 /*
5785 * Output flags, type name and bare variable name.
5786 * This should look like a declaration in the 'sb'
5787 * as it is not enclosed in a function.
5788 */
5789 var.DebStringSDTFlags (sb);
5790 var.type.DebString (sb);
5791 sb.Append (' ');
5792 sb.Append (var.name.val);
5793
5794 /*
5795 * Maybe it has a non-default initialization value.
5796 */
5797 if ((var.init != null) && !(var.init is TokenRValInitDef)) {
5798 sb.Append (" = ");
5799 var.init.DebString (sb);
5800 }
5801
5802 /*
5803 * End of declaration statement.
5804 */
5805 sb.Append (';');
5806 }
5807 }
5808
5809 private void DebStringSDTFlags (StringBuilder sb)
5810 {
5811 if ((sdtFlags & ScriptReduce.SDT_PRIVATE) != 0) sb.Append ("private ");
5812 if ((sdtFlags & ScriptReduce.SDT_PROTECTED) != 0) sb.Append ("protected ");
5813 if ((sdtFlags & ScriptReduce.SDT_PUBLIC) != 0) sb.Append ("public ");
5814 if ((sdtFlags & ScriptReduce.SDT_ABSTRACT) != 0) sb.Append ("abstract ");
5815 if ((sdtFlags & ScriptReduce.SDT_FINAL) != 0) sb.Append ("final ");
5816 if ((sdtFlags & ScriptReduce.SDT_NEW) != 0) sb.Append ("new ");
5817 if ((sdtFlags & ScriptReduce.SDT_OVERRIDE) != 0) sb.Append ("override ");
5818 if ((sdtFlags & ScriptReduce.SDT_STATIC) != 0) sb.Append ("static ");
5819 if ((sdtFlags & ScriptReduce.SDT_VIRTUAL) != 0) sb.Append ("virtual ");
5820 }
5821 }
5822
5823 /**
5824 * @brief Indicates an interface type.method that is implemented by the function
5825 */
5826 public class TokenIntfImpl : Token {
5827 public TokenTypeSDTypeInterface intfType;
5828 public TokenName methName; // simple name, no arg signature
5829
5830 public TokenIntfImpl (TokenTypeSDTypeInterface intfType, TokenName methName) : base (intfType)
5831 {
5832 this.intfType = intfType;
5833 this.methName = methName;
5834 }
5835 }
5836
5837 /**
5838 * @brief any expression that can go on left side of an "="
5839 */
5840 public abstract class TokenLVal : TokenRVal {
5841 public TokenLVal (Token original) : base (original) { }
5842 public abstract override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig);
5843 public abstract override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig);
5844 }
5845
5846 /**
5847 * @brief an element of an array is an L-value
5848 */
5849 public class TokenLValArEle : TokenLVal {
5850 public TokenRVal baseRVal;
5851 public TokenRVal subRVal;
5852
5853 public TokenLValArEle (Token original) : base (original) { }
5854
5855 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
5856 {
5857 TokenType baseType = baseRVal.GetRValType (scg, null);
5858
5859 /*
5860 * Maybe referencing element of a fixed-dimension array.
5861 */
5862 if ((baseType is TokenTypeSDTypeClass) && (((TokenTypeSDTypeClass)baseType).decl.arrayOfType != null)) {
5863 return ((TokenTypeSDTypeClass)baseType).decl.arrayOfType;
5864 }
5865
5866 /*
5867 * Maybe referencing $idxprop property of script-defined class or interface.
5868 */
5869 if (baseType is TokenTypeSDTypeClass) {
5870 TokenDeclSDTypeClass sdtDecl = ((TokenTypeSDTypeClass)baseType).decl;
5871 TokenDeclVar idxProp = scg.FindSingleMember (sdtDecl.members, new TokenName (this, "$idxprop"), null);
5872 if (idxProp != null) return idxProp.type;
5873 }
5874 if (baseType is TokenTypeSDTypeInterface) {
5875 TokenDeclSDTypeInterface sdtDecl = ((TokenTypeSDTypeInterface)baseType).decl;
5876 TokenDeclVar idxProp = sdtDecl.FindIFaceMember (scg, new TokenName (this, "$idxprop"), null, out sdtDecl);
5877 if (idxProp != null) return idxProp.type;
5878 }
5879
5880 /*
5881 * Maybe referencing single character of a string.
5882 */
5883 if ((baseType is TokenTypeKey) || (baseType is TokenTypeStr)) {
5884 return new TokenTypeChar (this);
5885 }
5886
5887 /*
5888 * Assume XMR_Array element or extracting element from list.
5889 */
5890 if ((baseType is TokenTypeArray) || (baseType is TokenTypeList)) {
5891 return new TokenTypeObject (this);
5892 }
5893
5894 scg.ErrorMsg (this, "unknown array reference");
5895 return new TokenTypeVoid (this);
5896 }
5897
5898 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
5899 {
5900 return baseRVal.IsRValTrivial (scg, null) && subRVal.IsRValTrivial (scg, null);
5901 }
5902
5903 public override void DebString (StringBuilder sb)
5904 {
5905 baseRVal.DebString (sb);
5906 sb.Append ('[');
5907 subRVal.DebString (sb);
5908 sb.Append (']');
5909 }
5910 }
5911
5912 /**
5913 * @brief 'base.' being used to reference a field/method of the extended class.
5914 */
5915 public class TokenLValBaseField : TokenLVal {
5916 public TokenName fieldName;
5917 private TokenDeclSDTypeClass thisClass;
5918
5919 public TokenLValBaseField (Token original, TokenName fieldName, TokenDeclSDTypeClass thisClass) : base (original)
5920 {
5921 this.fieldName = fieldName;
5922 this.thisClass = thisClass;
5923 }
5924
5925 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
5926 {
5927 TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig);
5928 if (var != null) return var.type;
5929 scg.ErrorMsg (fieldName, "unknown member of " + thisClass.extends.ToString ());
5930 return new TokenTypeVoid (fieldName);
5931 }
5932
5933 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
5934 {
5935 TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig);
5936 return (var != null) && var.IsVarTrivial (scg);
5937 }
5938
5939 public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
5940 {
5941 TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig);
5942 return (var != null) && var.IsFuncTrivial (scg);
5943 }
5944 }
5945
5946 /**
5947 * @brief a field within an struct is an L-value
5948 */
5949 public class TokenLValIField : TokenLVal {
5950 public TokenRVal baseRVal;
5951 public TokenName fieldName;
5952
5953 public TokenLValIField (Token original) : base (original) { }
5954
5955 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
5956 {
5957 TokenType baseType = baseRVal.GetRValType (scg, null);
5958 if (baseType is TokenTypeSDTypeClass) {
5959 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
5960 if (var != null) return var.type;
5961 }
5962 if (baseType is TokenTypeSDTypeInterface) {
5963 TokenDeclSDTypeInterface baseIntfDecl = ((TokenTypeSDTypeInterface)baseType).decl;
5964 TokenDeclVar var = baseIntfDecl.FindIFaceMember (scg, fieldName, argsig, out baseIntfDecl);
5965 if (var != null) return var.type;
5966 }
5967 if (baseType is TokenTypeArray) {
5968 return XMR_Array.GetRValType (fieldName);
5969 }
5970 if ((baseType is TokenTypeRot) || (baseType is TokenTypeVec)) {
5971 return new TokenTypeFloat (fieldName);
5972 }
5973 scg.ErrorMsg (fieldName, "unknown member of " + baseType.ToString ());
5974 return new TokenTypeVoid (fieldName);
5975 }
5976
5977 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
5978 {
5979 /*
5980 * If getting pointer to instance isn't trivial, then accessing the member isn't trivial either.
5981 */
5982 if (!baseRVal.IsRValTrivial (scg, null)) return false;
5983
5984 /*
5985 * Accessing a member of a class depends on the member.
5986 * In the case of a method, this is accessing it as a delegate, not calling it, and
5987 * argsig simply serves as selecting which of possibly overloaded methods to select.
5988 * The case of accessing a property, however, depends on the property implementation,
5989 * as there could be looping inside the property code.
5990 */
5991 TokenType baseType = baseRVal.GetRValType (scg, null);
5992 if (baseType is TokenTypeSDTypeClass) {
5993 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
5994 return (var != null) && var.IsVarTrivial (scg);
5995 }
5996
5997 /*
5998 * Accessing the members of anything else (arrays, rotations, vectors) is always trivial.
5999 */
6000 return true;
6001 }
6002
6003 /**
6004 * @brief Check to see if the case of calling an instance method of some object is trivial.
6005 * @param scg = script making the call
6006 * @param argsig = argument types of the call (used to select among overloads)
6007 * @returns true iff we can tell at compile time that the call will always call a trivial method
6008 */
6009 public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
6010 {
6011 /*
6012 * If getting pointer to instance isn't trivial, then calling the method isn't trivial either.
6013 */
6014 if (!baseRVal.IsRValTrivial (scg, null)) return false;
6015
6016 /*
6017 * Calling a method of a class depends on the method.
6018 */
6019 TokenType baseType = baseRVal.GetRValType (scg, null);
6020 if (baseType is TokenTypeSDTypeClass) {
6021 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
6022 return (var != null) && var.IsFuncTrivial (scg);
6023 }
6024
6025 /*
6026 * Calling via a pointer to an interface instance is never trivial.
6027 * (It is really a pointer to an array of delegates).
6028 * We can't tell for this call site whether the actual method being called is trivial or not,
6029 * so we have to assume it isn't.
6030 * ??? We could theoretically check to see if *all* implementations of this method of
6031 * this interface are trivial, then we could conclude that this call is trivial.
6032 */
6033 if (baseType is TokenTypeSDTypeInterface) return false;
6034
6035 /*
6036 * Calling a method of anything else (arrays, rotations, vectors) is always trivial.
6037 * Even methods of delegates, such as ".GetArgTypes()" that we may someday do is trivial.
6038 */
6039 return true;
6040 }
6041
6042 // debugging
6043 public override void DebString (StringBuilder sb)
6044 {
6045 baseRVal.DebString (sb);
6046 sb.Append ('.');
6047 sb.Append (fieldName.val);
6048 }
6049 }
6050
6051 /**
6052 * @brief a name is being used as an L-value
6053 */
6054 public class TokenLValName : TokenLVal {
6055 public TokenName name;
6056 public VarDict stack;
6057
6058 public TokenLValName (TokenName name, VarDict stack) : base (name)
6059 {
6060 /*
6061 * Save name of variable/method/function/field.
6062 */
6063 this.name = name;
6064
6065 /*
6066 * Save where in the stack it can be looked up.
6067 * If the current stack is for locals, do not allow forward references.
6068 * this allows idiocy like:
6069 * list buttons = [ 1, 2, 3 ];
6070 * x () {
6071 * list buttons = llList2List (buttons, 0, 1);
6072 * llOwnerSay (llList2CSV (buttons));
6073 * }
6074 * If it is not locals, allow forward references.
6075 * this allows function X() to call Y() and Y() to call X().
6076 */
6077 this.stack = stack.FreezeLocals ();
6078 }
6079
6080 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6081 {
6082 TokenDeclVar var = scg.FindNamedVar (this, argsig);
6083 if (var != null) return var.type;
6084 scg.ErrorMsg (name, "undefined name " + name.val + ScriptCodeGen.ArgSigString (argsig));
6085 return new TokenTypeVoid (name);
6086 }
6087
6088 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6089 {
6090 TokenDeclVar var = scg.FindNamedVar (this, argsig);
6091 return (var != null) && var.IsVarTrivial (scg);
6092 }
6093
6094 /**
6095 * @brief Check to see if the case of calling a global method is trivial.
6096 * @param scg = script making the call
6097 * @param argsig = argument types of the call (used to select among overloads)
6098 * @returns true iff we can tell at compile time that the call will always call a trivial method
6099 */
6100 public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
6101 {
6102 TokenDeclVar var = scg.FindNamedVar (this, argsig);
6103 return (var != null) && var.IsFuncTrivial (scg);
6104 }
6105
6106 // debugging
6107 public override void DebString (StringBuilder sb)
6108 {
6109 sb.Append (name.val);
6110 }
6111 }
6112
6113 /**
6114 * @brief a static field within a struct is an L-value
6115 */
6116 public class TokenLValSField : TokenLVal {
6117 public TokenType baseType;
6118 public TokenName fieldName;
6119
6120 public TokenLValSField (Token original) : base (original) { }
6121
6122 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6123 {
6124 if (baseType is TokenTypeSDTypeClass) {
6125 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
6126 if (var != null) return var.type;
6127 }
6128 scg.ErrorMsg (fieldName, "unknown member of " + baseType.ToString ());
6129 return new TokenTypeVoid (fieldName);
6130 }
6131
6132 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6133 {
6134 /*
6135 * Accessing a member of a class depends on the member.
6136 * In the case of a method, this is accessing it as a delegate, not calling it, and
6137 * argsig simply serves as selecting which of possibly overloaded methods to select.
6138 * The case of accessing a property, however, depends on the property implementation,
6139 * as there could be looping inside the property code.
6140 */
6141 if (baseType is TokenTypeSDTypeClass) {
6142 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
6143 return (var != null) && var.IsVarTrivial (scg);
6144 }
6145
6146 /*
6147 * Accessing the fields/methods/properties of anything else (arrays, rotations, vectors) is always trivial.
6148 */
6149 return true;
6150 }
6151
6152 /**
6153 * @brief Check to see if the case of calling a class' static method is trivial.
6154 * @param scg = script making the call
6155 * @param argsig = argument types of the call (used to select among overloads)
6156 * @returns true iff we can tell at compile time that the call will always call a trivial method
6157 */
6158 public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
6159 {
6160 /*
6161 * Calling a static method of a class depends on the method.
6162 */
6163 if (baseType is TokenTypeSDTypeClass) {
6164 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
6165 return (var != null) && var.IsFuncTrivial (scg);
6166 }
6167
6168 /*
6169 * Calling a static method of anything else (arrays, rotations, vectors) is always trivial.
6170 */
6171 return true;
6172 }
6173
6174 public override void DebString (StringBuilder sb)
6175 {
6176 if (fieldName.val == "$new") {
6177 sb.Append ("new ");
6178 baseType.DebString (sb);
6179 } else {
6180 baseType.DebString (sb);
6181 sb.Append ('.');
6182 fieldName.DebString (sb);
6183 }
6184 }
6185 }
6186
6187 /**
6188 * @brief any expression that can go on right side of "="
6189 */
6190 public delegate TokenRVal TCCLookup (TokenRVal rVal, ref bool didOne);
6191 public abstract class TokenRVal : Token {
6192 public TokenRVal (Token original) : base (original) { }
6193
6194 /**
6195 * @brief Tell us the type of the expression.
6196 */
6197 public abstract TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig);
6198
6199 /**
6200 * @brief Tell us if reading and writing the value is trivial.
6201 *
6202 * @param scg = script code generator of script making the access
6203 * @param argsig = argument types of the call (used to select among overloads)
6204 * @returns true: we can tell at compile time that reading/writing this location
6205 * will always be trivial (no looping or CheckRun() calls possible).
6206 * false: otherwise
6207 */
6208 public abstract bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig);
6209
6210 /**
6211 * @brief Tell us if calling the method is trivial.
6212 *
6213 * This is the default implementation that returns false.
6214 * It is only used if the location is holding a delegate
6215 * and the method that the delegate is pointing to is being
6216 * called. Since we can't tell if the actual runtime method
6217 * is trivial or not, we assume it isn't.
6218 *
6219 * For the more usual ways of calling functions, see the
6220 * various overrides of IsCallTrivial().
6221 *
6222 * @param scg = script code generator of script making the call
6223 * @param argsig = argument types of the call (used to select among overloads)
6224 * @returns true: we can tell at compile time that this call will always
6225 * be to a trivial function/method (no looping or CheckRun()
6226 * calls possible).
6227 * false: otherwise
6228 */
6229 public virtual bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
6230 {
6231 return false;
6232 }
6233
6234 /**
6235 * @brief If the result of the expression is a constant,
6236 * create a TokenRValConst equivalent, set didOne, and return that.
6237 * Otherwise, just return the original without changing didOne.
6238 */
6239 public virtual TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6240 {
6241 return lookup (this, ref didOne);
6242 }
6243 }
6244
6245 /**
6246 * @brief a postfix operator and corresponding L-value
6247 */
6248 public class TokenRValAsnPost : TokenRVal {
6249 public TokenLVal lVal;
6250 public Token postfix;
6251
6252 public TokenRValAsnPost (Token original) : base (original) { }
6253
6254 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6255 {
6256 return lVal.GetRValType (scg, argsig);
6257 }
6258
6259 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6260 {
6261 return lVal.IsRValTrivial (scg, null);
6262 }
6263
6264 public override void DebString (StringBuilder sb)
6265 {
6266 lVal.DebString (sb);
6267 sb.Append (' ');
6268 postfix.DebString (sb);
6269 }
6270 }
6271
6272 /**
6273 * @brief a prefix operator and corresponding L-value
6274 */
6275 public class TokenRValAsnPre : TokenRVal {
6276 public Token prefix;
6277 public TokenLVal lVal;
6278
6279 public TokenRValAsnPre (Token original) : base (original) { }
6280
6281 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6282 {
6283 return lVal.GetRValType (scg, argsig);
6284 }
6285
6286 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6287 {
6288 return lVal.IsRValTrivial (scg, null);
6289 }
6290
6291 public override void DebString (StringBuilder sb)
6292 {
6293 prefix.DebString (sb);
6294 sb.Append (' ');
6295 lVal.DebString (sb);
6296 }
6297 }
6298
6299 /**
6300 * @brief calling a function or method, ie, may have side-effects
6301 */
6302 public class TokenRValCall : TokenRVal {
6303
6304 public TokenRVal meth; // points to the function to be called
6305 // - might be a reference to a global function (TokenLValName)
6306 // - or an instance method of a class (TokenLValIField)
6307 // - or a static method of a class (TokenLValSField)
6308 // - or a delegate stored in a variable (assumption for anything else)
6309 public TokenRVal args; // null-terminated TokenRVal list
6310 public int nArgs; // number of elements in args
6311
6312 public TokenRValCall (Token original) : base (original) { }
6313
6314 private TokenType[] myArgSig;
6315
6316 /**
6317 * @brief The type of a call is the type of the return value.
6318 */
6319 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6320 {
6321 /*
6322 * Build type signature so we select correct overloaded function.
6323 */
6324 if (myArgSig == null) {
6325 myArgSig = new TokenType[nArgs];
6326 int i = 0;
6327 for (Token t = args; t != null; t = t.nextToken) {
6328 myArgSig[i++] = ((TokenRVal)t).GetRValType (scg, null);
6329 }
6330 }
6331
6332 /*
6333 * Get the type of the method itself. This should get us a delegate type.
6334 */
6335 TokenType delType = meth.GetRValType (scg, myArgSig);
6336 if (!(delType is TokenTypeSDTypeDelegate)) {
6337 scg.ErrorMsg (meth, "must be function or method");
6338 return new TokenTypeVoid (meth);
6339 }
6340
6341 /*
6342 * Get the return type from the delegate type.
6343 */
6344 return ((TokenTypeSDTypeDelegate)delType).decl.GetRetType ();
6345 }
6346
6347 /**
6348 * @brief See if the call to the function/method is trivial.
6349 * It is trivial if all the argument computations are trivial and
6350 * the function is not being called via virtual table or delegate
6351 * and the function body is trivial.
6352 */
6353 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6354 {
6355 /*
6356 * Build type signature so we select correct overloaded function.
6357 */
6358 if (myArgSig == null) {
6359 myArgSig = new TokenType[nArgs];
6360 int i = 0;
6361 for (Token t = args; t != null; t = t.nextToken) {
6362 myArgSig[i++] = ((TokenRVal)t).GetRValType (scg, null);
6363 }
6364 }
6365
6366 /*
6367 * Make sure all arguments can be computed trivially.
6368 */
6369 for (Token t = args; t != null; t = t.nextToken) {
6370 if (!((TokenRVal)t).IsRValTrivial (scg, null)) return false;
6371 }
6372
6373 /*
6374 * See if the function call itself and the function body are trivial.
6375 */
6376 return meth.IsCallTrivial (scg, myArgSig);
6377 }
6378
6379 // debugging
6380 public override void DebString (StringBuilder sb)
6381 {
6382 meth.DebString (sb);
6383 sb.Append (" (");
6384 bool first = true;
6385 for (Token t = args; t != null; t = t.nextToken) {
6386 if (!first) sb.Append (", ");
6387 t.DebString (sb);
6388 first = false;
6389 }
6390 sb.Append (")");
6391 }
6392 }
6393
6394 /**
6395 * @brief encapsulates a typecast, ie, (type)
6396 */
6397 public class TokenRValCast : TokenRVal {
6398 public TokenType castTo;
6399 public TokenRVal rVal;
6400
6401 public TokenRValCast (TokenType type, TokenRVal value) : base (type)
6402 {
6403 castTo = type;
6404 rVal = value;
6405 }
6406
6407 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6408 {
6409 return castTo;
6410 }
6411
6412 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6413 {
6414 argsig = null;
6415 if (castTo is TokenTypeSDTypeDelegate) {
6416 argsig = ((TokenTypeSDTypeDelegate)castTo).decl.GetArgTypes ();
6417 }
6418 return rVal.IsRValTrivial (scg, argsig);
6419 }
6420
6421 /**
6422 * @brief If operand is constant, maybe we can say the whole thing is a constant.
6423 */
6424 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6425 {
6426 rVal = rVal.TryComputeConstant (lookup, ref didOne);
6427 if (rVal is TokenRValConst) {
6428 try {
6429 object val = ((TokenRValConst)rVal).val;
6430 object nval = null;
6431 if (castTo is TokenTypeChar) {
6432 if (val is char) return rVal;
6433 if (val is int) nval = (char)(int)val;
6434 }
6435 if (castTo is TokenTypeFloat) {
6436 if (val is double) return rVal;
6437 if (val is int) nval = (double)(int)val;
6438 if (val is string) nval = new LSL_Float ((string)val).value;
6439 }
6440 if (castTo is TokenTypeInt) {
6441 if (val is int) return rVal;
6442 if (val is char) nval = (int)(char)val;
6443 if (val is double) nval = (int)(double)val;
6444 if (val is string) nval = new LSL_Integer ((string)val).value;
6445 }
6446 if (castTo is TokenTypeRot) {
6447 if (val is LSL_Rotation) return rVal;
6448 if (val is string) nval = new LSL_Rotation ((string)val);
6449 }
6450 if ((castTo is TokenTypeKey) || (castTo is TokenTypeStr)) {
6451 if (val is string) nval = val; // in case of key/string conversion
6452 if (val is char) nval = TypeCast.CharToString ((char)val);
6453 if (val is double) nval = TypeCast.FloatToString ((double)val);
6454 if (val is int) nval = TypeCast.IntegerToString ((int)val);
6455 if (val is LSL_Rotation) nval = TypeCast.RotationToString ((LSL_Rotation)val);
6456 if (val is LSL_Vector) nval = TypeCast.VectorToString ((LSL_Vector)val);
6457 }
6458 if (castTo is TokenTypeVec) {
6459 if (val is LSL_Vector) return rVal;
6460 if (val is string) nval = new LSL_Vector ((string)val);
6461 }
6462 if (nval != null) {
6463 TokenRVal rValConst = new TokenRValConst (castTo, nval);
6464 didOne = true;
6465 return rValConst;
6466 }
6467 } catch {
6468 }
6469 }
6470 return this;
6471 }
6472
6473 public override void DebString (StringBuilder sb)
6474 {
6475 sb.Append ('(');
6476 castTo.DebString (sb);
6477 sb.Append (')');
6478 rVal.DebString (sb);
6479 }
6480 }
6481
6482 /**
6483 * @brief Encapsulate a conditional expression:
6484 * <condExpr> ? <trueExpr> : <falseExpr>
6485 */
6486 public class TokenRValCondExpr : TokenRVal {
6487 public TokenRVal condExpr;
6488 public TokenRVal trueExpr;
6489 public TokenRVal falseExpr;
6490
6491 public TokenRValCondExpr (Token original) : base (original)
6492 { }
6493
6494 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6495 {
6496 TokenType trueType = trueExpr.GetRValType (scg, argsig);
6497 TokenType falseType = falseExpr.GetRValType (scg, argsig);
6498 if (trueType.ToString () != falseType.ToString ()) {
6499 scg.ErrorMsg (condExpr, "true & false expr types don't match");
6500 }
6501 return trueType;
6502 }
6503
6504 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6505 {
6506 return condExpr.IsRValTrivial (scg, null) &&
6507 trueExpr.IsRValTrivial (scg, argsig) &&
6508 falseExpr.IsRValTrivial (scg, argsig);
6509 }
6510
6511 /**
6512 * @brief If condition is constant, then the whole expression is constant
6513 * iff the corresponding trueExpr or falseExpr is constant.
6514 */
6515 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6516 {
6517 TokenRVal rValCond = condExpr.TryComputeConstant (lookup, ref didOne);
6518 if (rValCond is TokenRValConst) {
6519 didOne = true;
6520 bool isTrue = ((TokenRValConst)rValCond).IsConstBoolTrue ();
6521 return (isTrue ? trueExpr : falseExpr).TryComputeConstant (lookup, ref didOne);
6522 }
6523 return this;
6524 }
6525
6526 // debugging
6527 public override void DebString (StringBuilder sb)
6528 {
6529 condExpr.DebString (sb);
6530 sb.Append (" ? ");
6531 trueExpr.DebString (sb);
6532 sb.Append (" : ");
6533 falseExpr.DebString (sb);
6534 }
6535 }
6536
6537 /**
6538 * @brief all constants supposed to end up here
6539 */
6540 public enum TokenRValConstType : byte { CHAR = 0, FLOAT = 1, INT = 2, KEY = 3, STRING = 4 };
6541 public class TokenRValConst : TokenRVal {
6542 public object val; // always a system type (char, int, double, string), never LSL-wrapped
6543 public TokenRValConstType type;
6544 public TokenType tokType;
6545
6546 public TokenRValConst (Token original, object value) : base (original)
6547 {
6548 val = value;
6549
6550 TokenType tt = null;
6551 if (val is char) {
6552 type = TokenRValConstType.CHAR;
6553 tt = new TokenTypeChar (this);
6554 } else if (val is int) {
6555 type = TokenRValConstType.INT;
6556 tt = new TokenTypeInt (this);
6557 } else if (val is double) {
6558 type = TokenRValConstType.FLOAT;
6559 tt = new TokenTypeFloat (this);
6560 } else if (val is string) {
6561 type = TokenRValConstType.STRING;
6562 tt = new TokenTypeStr (this);
6563 } else {
6564 throw new Exception ("invalid constant type " + val.GetType ());
6565 }
6566
6567 tokType = (original is TokenType) ? (TokenType)original : tt;
6568 if (tokType is TokenTypeKey) {
6569 type = TokenRValConstType.KEY;
6570 }
6571 }
6572
6573 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6574 {
6575 return tokType;
6576 }
6577
6578 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6579 {
6580 return true;
6581 }
6582
6583 public CompValu GetCompValu ()
6584 {
6585 switch (type) {
6586 case TokenRValConstType.CHAR: { return new CompValuChar (tokType, (char)val); }
6587 case TokenRValConstType.FLOAT: { return new CompValuFloat (tokType, (double)val); }
6588 case TokenRValConstType.INT: { return new CompValuInteger (tokType, (int)val); }
6589 case TokenRValConstType.KEY:
6590 case TokenRValConstType.STRING: { return new CompValuString (tokType, (string)val); }
6591 default: throw new Exception ("unknown type");
6592 }
6593 }
6594
6595 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6596 {
6597 // gotta end somewhere
6598 return this;
6599 }
6600
6601 public bool IsConstBoolTrue ()
6602 {
6603 switch (type) {
6604 case TokenRValConstType.CHAR: { return (char)val != 0; }
6605 case TokenRValConstType.FLOAT: { return (double)val != 0; }
6606 case TokenRValConstType.INT: { return (int)val != 0; }
6607 case TokenRValConstType.KEY: { return (string)val != "" && (string)val != ScriptBaseClass.NULL_KEY; }
6608 case TokenRValConstType.STRING: { return (string)val != ""; }
6609 default: throw new Exception ("unknown type");
6610 }
6611 }
6612
6613 public override void DebString (StringBuilder sb)
6614 {
6615 if (val is char) {
6616 sb.Append ('\'');
6617 EscapeQuotes (sb, new string (new char [] { (char)val }));
6618 sb.Append ('\'');
6619 } else if (val is int) {
6620 sb.Append ((int)val);
6621 } else if (val is double) {
6622 string str = ((double)val).ToString ();
6623 sb.Append (str);
6624 if ((str.IndexOf ('.') < 0) &&
6625 (str.IndexOf ('E') < 0) &&
6626 (str.IndexOf ('e') < 0)) {
6627 sb.Append (".0");
6628 }
6629 } else if (val is string) {
6630 sb.Append ('"');
6631 EscapeQuotes (sb, (string)val);
6632 sb.Append ('"');
6633 } else {
6634 throw new Exception ("invalid constant type " + val.GetType ());
6635 }
6636 }
6637 private static void EscapeQuotes (StringBuilder sb, string s)
6638 {
6639 foreach (char c in s) {
6640 switch (c) {
6641 case '\n': {
6642 sb.Append ("\\n");
6643 break;
6644 }
6645 case '\t': {
6646 sb.Append ("\\t");
6647 break;
6648 }
6649 case '\\': {
6650 sb.Append ("\\\\");
6651 break;
6652 }
6653 case '\'': {
6654 sb.Append ("\\'");
6655 break;
6656 }
6657 case '\"': {
6658 sb.Append ("\\\"");
6659 break;
6660 }
6661 default: {
6662 sb.Append (c);
6663 break;
6664 }
6665 }
6666 }
6667 }
6668 }
6669
6670 /**
6671 * @brief Default initialization value for the corresponding variable.
6672 */
6673 public class TokenRValInitDef : TokenRVal {
6674 public TokenType type;
6675
6676 public static TokenRValInitDef Construct (TokenDeclVar tokenDeclVar)
6677 {
6678 TokenRValInitDef zhis = new TokenRValInitDef (tokenDeclVar);
6679 zhis.type = tokenDeclVar.type;
6680 return zhis;
6681 }
6682 private TokenRValInitDef (Token original) : base (original) { }
6683
6684 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6685 {
6686 return type;
6687 }
6688
6689 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6690 {
6691 // it's always just a constant so it's always very trivial
6692 return true;
6693 }
6694
6695 public override void DebString (StringBuilder sb)
6696 {
6697 sb.Append ("<default ");
6698 sb.Append (type.ToString ());
6699 sb.Append ('>');
6700 }
6701 }
6702
6703 /**
6704 * @brief encapsulation of <rval> is <typeexp>
6705 */
6706 public class TokenRValIsType : TokenRVal {
6707 public TokenRVal rValExp;
6708 public TokenTypeExp typeExp;
6709
6710 public TokenRValIsType (Token original) : base (original) { }
6711
6712 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6713 {
6714 return new TokenTypeBool (rValExp);
6715 }
6716
6717 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6718 {
6719 return rValExp.IsRValTrivial (scg, argsig);
6720 }
6721 }
6722
6723 /**
6724 * @brief an R-value enclosed in brackets is an LSLList
6725 */
6726 public class TokenRValList : TokenRVal {
6727
6728 public TokenRVal rVal; // null-terminated list of TokenRVal objects
6729 public int nItems;
6730
6731 public TokenRValList (Token original) : base (original) { }
6732
6733 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6734 {
6735 return new TokenTypeList (rVal);
6736 }
6737
6738 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6739 {
6740 for (Token t = rVal; t != null; t = t.nextToken) {
6741 if (!((TokenRVal)t).IsRValTrivial (scg, null)) return false;
6742 }
6743 return true;
6744 }
6745
6746 public override void DebString (StringBuilder sb)
6747 {
6748 bool first = true;
6749 sb.Append ('[');
6750 for (Token t = rVal; t != null; t = t.nextToken) {
6751 if (!first) sb.Append (',');
6752 sb.Append (' ');
6753 t.DebString (sb);
6754 first = false;
6755 }
6756 sb.Append (" ]");
6757 }
6758 }
6759
6760 /**
6761 * @brief encapsulates '$new' arraytype '{' ... '}'
6762 */
6763 public class TokenRValNewArIni : TokenRVal {
6764 public TokenType arrayType;
6765 public TokenList valueList; // TokenList : a sub-list
6766 // TokenKwComma : a default value
6767 // TokenRVal : init expression
6768
6769 public TokenRValNewArIni (Token original) : base (original)
6770 {
6771 valueList = new TokenList (original);
6772 }
6773
6774 // type of the expression = the array type allocated by $new()
6775 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6776 {
6777 return arrayType;
6778 }
6779
6780 // The expression is trivial if all the initializers are trivial.
6781 // An array's constructor is always trivial (no CheckRun() calls).
6782 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6783 {
6784 return ListIsTrivial (scg, valueList);
6785 }
6786 private bool ListIsTrivial (ScriptCodeGen scg, TokenList valList)
6787 {
6788 foreach (Token val in valList.tl) {
6789 if (val is TokenRVal) {
6790 if (!((TokenRVal)val).IsRValTrivial (scg, null)) return false;
6791 }
6792 if (val is TokenList) {
6793 if (!ListIsTrivial (scg, (TokenList)val)) return false;
6794 }
6795 }
6796 return true;
6797 }
6798
6799 public override void DebString (StringBuilder sb)
6800 {
6801 sb.Append ("new ");
6802 arrayType.DebString (sb);
6803 sb.Append (' ');
6804 valueList.DebString (sb);
6805 }
6806 }
6807 public class TokenList : Token {
6808 public List<Token> tl = new List<Token> ();
6809 public TokenList (Token original) : base (original) { }
6810
6811 public override void DebString (StringBuilder sb)
6812 {
6813 sb.Append ('{');
6814 bool first = true;
6815 foreach (Token t in tl) {
6816 if (!first) sb.Append (", ");
6817 t.DebString (sb);
6818 first = false;
6819 }
6820 sb.Append ('}');
6821 }
6822 }
6823
6824 /**
6825 * @brief a binary operator and its two operands
6826 */
6827 public class TokenRValOpBin : TokenRVal {
6828 public TokenRVal rValLeft;
6829 public TokenKw opcode;
6830 public TokenRVal rValRight;
6831
6832 public TokenRValOpBin (TokenRVal left, TokenKw op, TokenRVal right) : base (op)
6833 {
6834 rValLeft = left;
6835 opcode = op;
6836 rValRight = right;
6837 }
6838
6839 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6840 {
6841 /*
6842 * Comparisons and the like always return bool.
6843 */
6844 string opstr = opcode.ToString ();
6845 if ((opstr == "==") || (opstr == "!=") || (opstr == ">=") || (opstr == ">") ||
6846 (opstr == "&&") || (opstr == "||") || (opstr == "<=") || (opstr == "<") ||
6847 (opstr == "&&&") || (opstr == "|||")) {
6848 return new TokenTypeBool (opcode);
6849 }
6850
6851 /*
6852 * Comma is always type of right-hand operand.
6853 */
6854 if (opstr == ",") return rValRight.GetRValType (scg, argsig);
6855
6856 /*
6857 * Assignments are always the type of the left-hand operand,
6858 * including stuff like "+=".
6859 */
6860 if (opstr.EndsWith ("=")) {
6861 return rValLeft.GetRValType (scg, argsig);
6862 }
6863
6864 /*
6865 * string+something or something+string is always string.
6866 * except list+something or something+list is always a list.
6867 */
6868 string lType = rValLeft.GetRValType (scg, argsig).ToString ();
6869 string rType = rValRight.GetRValType (scg, argsig).ToString ();
6870 if ((opstr == "+") && ((lType == "list") || (rType == "list"))) {
6871 return new TokenTypeList (opcode);
6872 }
6873 if ((opstr == "+") && ((lType == "key") || (lType == "string") ||
6874 (rType == "key") || (rType == "string"))) {
6875 return new TokenTypeStr (opcode);
6876 }
6877
6878 /*
6879 * Everything else depends on both operands.
6880 */
6881 string key = lType + opstr + rType;
6882 BinOpStr binOpStr;
6883 if (BinOpStr.defined.TryGetValue (key, out binOpStr)) {
6884 return TokenType.FromSysType (opcode, binOpStr.outtype);
6885 }
6886
6887 scg.ErrorMsg (opcode, "undefined operation " + key);
6888 return new TokenTypeVoid (opcode);
6889 }
6890
6891 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6892 {
6893 return rValLeft.IsRValTrivial (scg, null) && rValRight.IsRValTrivial (scg, null);
6894 }
6895
6896 /**
6897 * @brief If both operands are constants, maybe we can say the whole thing is a constant.
6898 */
6899 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6900 {
6901 rValLeft = rValLeft.TryComputeConstant (lookup, ref didOne);
6902 rValRight = rValRight.TryComputeConstant (lookup, ref didOne);
6903 if ((rValLeft is TokenRValConst) && (rValRight is TokenRValConst)) {
6904 try {
6905 object val = opcode.binOpConst (((TokenRValConst)rValLeft).val,
6906 ((TokenRValConst)rValRight).val);
6907 TokenRVal rValConst = new TokenRValConst (opcode, val);
6908 didOne = true;
6909 return rValConst;
6910 } catch {
6911 }
6912 }
6913 return this;
6914 }
6915
6916 // debugging
6917 public override void DebString (StringBuilder sb)
6918 {
6919 rValLeft.DebString (sb);
6920 sb.Append (' ');
6921 sb.Append (opcode.ToString ());
6922 sb.Append (' ');
6923 rValRight.DebString (sb);
6924 }
6925 }
6926
6927 /**
6928 * @brief an unary operator and its one operand
6929 */
6930 public class TokenRValOpUn : TokenRVal {
6931 public TokenKw opcode;
6932 public TokenRVal rVal;
6933
6934 public TokenRValOpUn (TokenKw op, TokenRVal right) : base (op)
6935 {
6936 opcode = op;
6937 rVal = right;
6938 }
6939
6940 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6941 {
6942 if (opcode is TokenKwExclam) return new TokenTypeInt (opcode);
6943 return rVal.GetRValType (scg, null);
6944 }
6945
6946 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6947 {
6948 return rVal.IsRValTrivial (scg, null);
6949 }
6950
6951 /**
6952 * @brief If operand is constant, maybe we can say the whole thing is a constant.
6953 */
6954 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6955 {
6956 rVal = rVal.TryComputeConstant (lookup, ref didOne);
6957 if (rVal is TokenRValConst) {
6958 try {
6959 object val = opcode.unOpConst (((TokenRValConst)rVal).val);
6960 TokenRVal rValConst = new TokenRValConst (opcode, val);
6961 didOne = true;
6962 return rValConst;
6963 } catch {
6964 }
6965 }
6966 return this;
6967 }
6968
6969 /**
6970 * @brief Serialization/Deserialization.
6971 */
6972 public TokenRValOpUn (Token original) : base (original) { }
6973
6974 // debugging
6975 public override void DebString (StringBuilder sb)
6976 {
6977 sb.Append (opcode.ToString ());
6978 rVal.DebString (sb);
6979 }
6980 }
6981
6982 /**
6983 * @brief an R-value enclosed in parentheses
6984 */
6985 public class TokenRValParen : TokenRVal {
6986
6987 public TokenRVal rVal;
6988
6989 public TokenRValParen (Token original) : base (original) { }
6990
6991 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6992 {
6993 // pass argsig through in this simple case, ie, let
6994 // them do something like (llOwnerSay)("blabla...");
6995 return rVal.GetRValType (scg, argsig);
6996 }
6997
6998 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6999 {
7000 // pass argsig through in this simple case, ie, let
7001 // them do something like (llOwnerSay)("blabla...");
7002 return rVal.IsRValTrivial (scg, argsig);
7003 }
7004
7005 /**
7006 * @brief If operand is constant, we can say the whole thing is a constant.
7007 */
7008 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
7009 {
7010 rVal = rVal.TryComputeConstant (lookup, ref didOne);
7011 if (rVal is TokenRValConst) {
7012 didOne = true;
7013 return rVal;
7014 }
7015 return this;
7016 }
7017
7018 public override void DebString (StringBuilder sb)
7019 {
7020 sb.Append ('(');
7021 rVal.DebString (sb);
7022 sb.Append (')');
7023 }
7024 }
7025
7026 public class TokenRValRot : TokenRVal {
7027
7028 public TokenRVal xRVal;
7029 public TokenRVal yRVal;
7030 public TokenRVal zRVal;
7031 public TokenRVal wRVal;
7032
7033 public TokenRValRot (Token original) : base (original) { }
7034
7035 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
7036 {
7037 return new TokenTypeRot (xRVal);
7038 }
7039
7040 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
7041 {
7042 return xRVal.IsRValTrivial (scg, null) &&
7043 yRVal.IsRValTrivial (scg, null) &&
7044 zRVal.IsRValTrivial (scg, null) &&
7045 wRVal.IsRValTrivial (scg, null);
7046 }
7047
7048 public override void DebString (StringBuilder sb)
7049 {
7050 sb.Append ('<');
7051 xRVal.DebString (sb);
7052 sb.Append (',');
7053 yRVal.DebString (sb);
7054 sb.Append (',');
7055 zRVal.DebString (sb);
7056 sb.Append (',');
7057 wRVal.DebString (sb);
7058 sb.Append ('>');
7059 }
7060 }
7061
7062 /**
7063 * @brief 'this' is being used as an rval inside an instance method.
7064 */
7065 public class TokenRValThis : TokenRVal {
7066 public Token original;
7067 public TokenDeclSDTypeClass sdtClass;
7068
7069 public TokenRValThis (Token original, TokenDeclSDTypeClass sdtClass) : base (original)
7070 {
7071 this.original = original;
7072 this.sdtClass = sdtClass;
7073 }
7074
7075 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
7076 {
7077 return sdtClass.MakeRefToken (original);
7078 }
7079
7080 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
7081 {
7082 return true; // ldarg.0/starg.0 can't possibly loop
7083 }
7084
7085 // debugging
7086 public override void DebString (StringBuilder sb)
7087 {
7088 sb.Append ("this");
7089 }
7090 }
7091
7092 /**
7093 * @brief the 'undef' keyword is being used as a value in an expression.
7094 * It is the null object pointer and has type TokenTypeUndef.
7095 */
7096 public class TokenRValUndef : TokenRVal {
7097 Token original;
7098
7099 public TokenRValUndef (Token original) : base (original)
7100 {
7101 this.original = original;
7102 }
7103
7104 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
7105 {
7106 return new TokenTypeUndef (original);
7107 }
7108
7109 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
7110 {
7111 return true;
7112 }
7113
7114 public override void DebString (StringBuilder sb)
7115 {
7116 sb.Append ("undef");
7117 }
7118 }
7119
7120 /**
7121 * @brief put 3 RVals together as a Vector value.
7122 */
7123 public class TokenRValVec : TokenRVal {
7124
7125 public TokenRVal xRVal;
7126 public TokenRVal yRVal;
7127 public TokenRVal zRVal;
7128
7129 public TokenRValVec (Token original) : base (original) { }
7130
7131 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
7132 {
7133 return new TokenTypeVec (xRVal);
7134 }
7135
7136 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
7137 {
7138 return xRVal.IsRValTrivial (scg, null) &&
7139 yRVal.IsRValTrivial (scg, null) &&
7140 zRVal.IsRValTrivial (scg, null);
7141 }
7142
7143 public override void DebString (StringBuilder sb)
7144 {
7145 sb.Append ('<');
7146 xRVal.DebString (sb);
7147 sb.Append (',');
7148 yRVal.DebString (sb);
7149 sb.Append (',');
7150 zRVal.DebString (sb);
7151 sb.Append ('>');
7152 }
7153 }
7154
7155 /**
7156 * @brief encapsulates the whole script in a single token
7157 */
7158 public class TokenScript : Token {
7159 public int expiryDays = Int32.MaxValue;
7160 public TokenDeclState defaultState;
7161 public Dictionary<string, TokenDeclState> states = new Dictionary<string, TokenDeclState> ();
7162 public VarDict variablesStack = new VarDict (false); // initial one is used for global functions and variables
7163 public TokenDeclVar globalVarInit; // $globalvarinit function
7164 // - performs explicit global var and static field inits
7165
7166 private Dictionary<string, TokenDeclSDType> sdSrcTypes = new Dictionary<string, TokenDeclSDType> ();
7167 private bool sdSrcTypesSealed = false;
7168
7169 public TokenScript (Token original) : base (original) { }
7170
7171 /*
7172 * Handle variable definition stack.
7173 * Generally a '{' pushes a new frame and a '}' pops the frame.
7174 * Function parameters are pushed in an additional frame (just outside the body's { ... } block)
7175 */
7176 public void PushVarFrame (bool locals)
7177 {
7178 PushVarFrame (new VarDict (locals));
7179 }
7180 public void PushVarFrame (VarDict newFrame)
7181 {
7182 newFrame.outerVarDict = variablesStack;
7183 variablesStack = newFrame;
7184 }
7185 public void PopVarFrame ()
7186 {
7187 variablesStack = variablesStack.outerVarDict;
7188 }
7189 public bool AddVarEntry (TokenDeclVar var)
7190 {
7191 return variablesStack.AddEntry (var);
7192 }
7193
7194 /*
7195 * Handle list of script-defined types.
7196 */
7197 public void sdSrcTypesSeal ()
7198 {
7199 sdSrcTypesSealed = true;
7200 }
7201 public bool sdSrcTypesContainsKey (string key)
7202 {
7203 return sdSrcTypes.ContainsKey (key);
7204 }
7205 public bool sdSrcTypesTryGetValue (string key, out TokenDeclSDType value)
7206 {
7207 return sdSrcTypes.TryGetValue (key, out value);
7208 }
7209 public void sdSrcTypesAdd (string key, TokenDeclSDType value)
7210 {
7211 if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed");
7212 value.sdTypeIndex = sdSrcTypes.Count;
7213 sdSrcTypes.Add (key, value);
7214 }
7215 public void sdSrcTypesRep (string key, TokenDeclSDType value)
7216 {
7217 if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed");
7218 value.sdTypeIndex = sdSrcTypes[key].sdTypeIndex;
7219 sdSrcTypes[key] = value;
7220 }
7221 public void sdSrcTypesReplace (string key, TokenDeclSDType value)
7222 {
7223 if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed");
7224 sdSrcTypes[key] = value;
7225 }
7226 public Dictionary<string, TokenDeclSDType>.ValueCollection sdSrcTypesValues
7227 {
7228 get {
7229 return sdSrcTypes.Values;
7230 }
7231 }
7232 public int sdSrcTypesCount
7233 {
7234 get {
7235 return sdSrcTypes.Count;
7236 }
7237 }
7238
7239 /**
7240 * @brief Debug output.
7241 */
7242 public override void DebString (StringBuilder sb)
7243 {
7244 /*
7245 * Script-defined types.
7246 */
7247 foreach (TokenDeclSDType srcType in sdSrcTypes.Values) {
7248 srcType.DebString (sb);
7249 }
7250
7251 /*
7252 * Global constants.
7253 * Variables are handled by outputting the $globalvarinit function.
7254 */
7255 foreach (TokenDeclVar var in variablesStack) {
7256 if (var.constant) {
7257 var.DebString (sb);
7258 }
7259 }
7260
7261 /*
7262 * Global functions.
7263 */
7264 foreach (TokenDeclVar var in variablesStack) {
7265 if (var == globalVarInit) {
7266 var.DebStringInitFields (sb);
7267 } else if (var.retType != null) {
7268 var.DebString (sb);
7269 }
7270 }
7271
7272 /*
7273 * States and their event handler functions.
7274 */
7275 defaultState.DebString (sb);
7276 foreach (TokenDeclState st in states.Values) {
7277 st.DebString (sb);
7278 }
7279 }
7280 }
7281
7282 /**
7283 * @brief state body declaration
7284 */
7285 public class TokenStateBody : Token {
7286
7287 public TokenDeclVar eventFuncs;
7288
7289 public int index = -1; // (codegen) row in ScriptHandlerEventTable (0=default)
7290
7291 public TokenStateBody (Token original) : base (original) { }
7292
7293 public override void DebString (StringBuilder sb)
7294 {
7295 sb.Append (" { ");
7296 for (Token t = eventFuncs; t != null; t = t.nextToken) {
7297 t.DebString (sb);
7298 }
7299 sb.Append (" } ");
7300 }
7301 }
7302
7303 /**
7304 * @brief a single statement, such as ending on a semicolon or enclosed in braces
7305 * TokenStmt includes the terminating semicolon or the enclosing braces
7306 * Also includes @label; for jump targets.
7307 * Also includes stray ; null statements.
7308 * Also includes local variable declarations with or without initialization value.
7309 */
7310 public class TokenStmt : Token {
7311 public TokenStmt (Token original) : base (original) { }
7312 }
7313
7314 /**
7315 * @brief a group of statements enclosed in braces
7316 */
7317 public class TokenStmtBlock : TokenStmt {
7318
7319 public Token statements; // null-terminated list of statements, can also have TokenDeclVar's in here
7320 public TokenStmtBlock outerStmtBlock; // next outer stmtBlock or null if top-level, ie, function definition
7321 public TokenDeclVar function; // function it is part of
7322 public bool isTry; // true iff it's a try statement block
7323 public bool isCatch; // true iff it's a catch statement block
7324 public bool isFinally; // true iff it's a finally statement block
7325 public TokenStmtTry tryStmt; // set iff isTry|isCatch|isFinally is set
7326
7327 public TokenStmtBlock (Token original) : base (original) { }
7328
7329 // debugging
7330 public override void DebString (StringBuilder sb)
7331 {
7332 sb.Append ("{ ");
7333 for (Token stmt = statements; stmt != null; stmt = stmt.nextToken) {
7334 stmt.DebString (sb);
7335 }
7336 sb.Append ("} ");
7337 }
7338 }
7339
7340 /**
7341 * @brief definition of branch target name
7342 */
7343 public class TokenStmtLabel : TokenStmt {
7344
7345 public TokenName name; // the label's name
7346 public TokenStmtBlock block; // which block it is defined in
7347 public bool hasBkwdRefs = false;
7348
7349 public bool labelTagged; // code gen: location of label
7350 public ScriptMyLabel labelStruct;
7351
7352 public TokenStmtLabel (Token original) : base (original) { }
7353
7354 public override void DebString (StringBuilder sb)
7355 {
7356 sb.Append ('@');
7357 name.DebString (sb);
7358 sb.Append (';');
7359 }
7360 }
7361
7362 /**
7363 * @brief those types of RVals with a semi-colon on the end
7364 * that are allowed to stand alone as statements
7365 */
7366 public class TokenStmtRVal : TokenStmt {
7367 public TokenRVal rVal;
7368
7369 public TokenStmtRVal (Token original) : base (original) { }
7370
7371 // debugging
7372 public override void DebString (StringBuilder sb)
7373 {
7374 rVal.DebString (sb);
7375 sb.Append ("; ");
7376 }
7377 }
7378
7379 public class TokenStmtBreak : TokenStmt {
7380 public TokenStmtBreak (Token original) : base (original) { }
7381
7382 public override void DebString (StringBuilder sb)
7383 {
7384 sb.Append ("break;");
7385 }
7386 }
7387
7388 public class TokenStmtCont : TokenStmt {
7389 public TokenStmtCont (Token original) : base (original) { }
7390
7391 public override void DebString (StringBuilder sb)
7392 {
7393 sb.Append ("continue;");
7394 }
7395 }
7396
7397 /**
7398 * @brief "do" statement
7399 */
7400 public class TokenStmtDo : TokenStmt {
7401
7402 public TokenStmt bodyStmt;
7403 public TokenRValParen testRVal;
7404
7405 public TokenStmtDo (Token original) : base (original) { }
7406
7407 public override void DebString (StringBuilder sb)
7408 {
7409 sb.Append ("do ");
7410 bodyStmt.DebString (sb);
7411 sb.Append (" while ");
7412 testRVal.DebString (sb);
7413 sb.Append (';');
7414 }
7415 }
7416
7417 /**
7418 * @brief "for" statement
7419 */
7420 public class TokenStmtFor : TokenStmt {
7421
7422 public TokenStmt initStmt; // there is always an init statement, though it may be a null statement
7423 public TokenRVal testRVal; // there may or may not be a test (null if not)
7424 public TokenRVal incrRVal; // there may or may not be an increment (null if not)
7425 public TokenStmt bodyStmt; // there is always a body statement, though it may be a null statement
7426
7427 public TokenStmtFor (Token original) : base (original) { }
7428
7429 public override void DebString (StringBuilder sb)
7430 {
7431 sb.Append ("for (");
7432 if (initStmt != null) initStmt.DebString (sb);
7433 else sb.Append (';');
7434 if (testRVal != null) testRVal.DebString (sb);
7435 sb.Append (';');
7436 if (incrRVal != null) incrRVal.DebString (sb);
7437 sb.Append (") ");
7438 bodyStmt.DebString (sb);
7439 }
7440 }
7441
7442 /**
7443 * @brief "foreach" statement
7444 */
7445 public class TokenStmtForEach : TokenStmt {
7446
7447 public TokenLVal keyLVal;
7448 public TokenLVal valLVal;
7449 public TokenRVal arrayRVal;
7450 public TokenStmt bodyStmt; // there is always a body statement, though it may be a null statement
7451
7452 public TokenStmtForEach (Token original) : base (original) { }
7453
7454 public override void DebString (StringBuilder sb)
7455 {
7456 sb.Append ("foreach (");
7457 if (keyLVal != null) keyLVal.DebString (sb);
7458 sb.Append (',');
7459 if (valLVal != null) valLVal.DebString (sb);
7460 sb.Append (" in ");
7461 arrayRVal.DebString (sb);
7462 sb.Append (')');
7463 bodyStmt.DebString (sb);
7464 }
7465 }
7466
7467 public class TokenStmtIf : TokenStmt {
7468
7469 public TokenRValParen testRVal;
7470 public TokenStmt trueStmt;
7471 public TokenStmt elseStmt;
7472
7473 public TokenStmtIf (Token original) : base (original) { }
7474
7475 public override void DebString (StringBuilder sb)
7476 {
7477 sb.Append ("if ");
7478 testRVal.DebString (sb);
7479 sb.Append (" ");
7480 trueStmt.DebString (sb);
7481 if (elseStmt != null) {
7482 sb.Append (" else ");
7483 elseStmt.DebString (sb);
7484 }
7485 }
7486 }
7487
7488 public class TokenStmtJump : TokenStmt {
7489
7490 public TokenName label;
7491
7492 public TokenStmtJump (Token original) : base (original) { }
7493
7494 public override void DebString (StringBuilder sb)
7495 {
7496 sb.Append ("jump ");
7497 label.DebString (sb);
7498 sb.Append (';');
7499 }
7500 }
7501
7502 public class TokenStmtNull : TokenStmt {
7503
7504 public TokenStmtNull (Token original) : base (original) { }
7505
7506 public override void DebString (StringBuilder sb)
7507 {
7508 sb.Append (';');
7509 }
7510 }
7511
7512 public class TokenStmtRet : TokenStmt {
7513
7514 public TokenRVal rVal; // null if void
7515
7516 public TokenStmtRet (Token original) : base (original) { }
7517
7518 public override void DebString (StringBuilder sb)
7519 {
7520 sb.Append ("return");
7521 if (rVal != null) {
7522 sb.Append (' ');
7523 rVal.DebString (sb);
7524 }
7525 sb.Append (';');
7526 }
7527 }
7528
7529 /**
7530 * @brief statement that changes the current state.
7531 */
7532 public class TokenStmtState : TokenStmt {
7533
7534 public TokenName state; // null for default
7535
7536 public TokenStmtState (Token original) : base (original) { }
7537
7538 public override void DebString (StringBuilder sb)
7539 {
7540 sb.Append ("state ");
7541 sb.Append ((state == null) ? "default" : state.val);
7542 sb.Append (';');
7543 }
7544 }
7545
7546 /**
7547 * @brief Encapsulates a whole switch statement including the body and all cases.
7548 */
7549 public class TokenStmtSwitch : TokenStmt {
7550
7551 public TokenRValParen testRVal; // the integer index expression
7552 public TokenSwitchCase cases = null; // list of all cases, linked by .nextCase
7553 public TokenSwitchCase lastCase = null; // used during reduce to point to last in 'cases' list
7554
7555 public TokenStmtSwitch (Token original) : base (original) { }
7556
7557 public override void DebString (StringBuilder sb)
7558 {
7559 sb.Append ("switch ");
7560 testRVal.DebString (sb);
7561 sb.Append ('{');
7562 for (TokenSwitchCase kase = cases; kase != null; kase = kase.nextCase) {
7563 kase.DebString (sb);
7564 }
7565 sb.Append ('}');
7566 }
7567 }
7568
7569 /**
7570 * @brief Encapsulates a case/default clause from a switch statement including the
7571 * two values and the corresponding body statements.
7572 */
7573 public class TokenSwitchCase : Token {
7574 public TokenSwitchCase nextCase; // next case in source-code order
7575 public TokenRVal rVal1; // null means 'default', else 'case'
7576 public TokenRVal rVal2; // null means 'case expr:', else 'case expr ... expr:'
7577 public TokenStmt stmts; // statements associated with the case
7578 public TokenStmt lastStmt; // used during reduce for building statement list
7579
7580 public int val1; // codegen: value of rVal1 here
7581 public int val2; // codegen: value of rVal2 here
7582 public ScriptMyLabel label; // codegen: target label here
7583 public TokenSwitchCase nextSortedCase; // codegen: next case in ascending val order
7584
7585 public string str1;
7586 public string str2;
7587 public TokenSwitchCase lowerCase;
7588 public TokenSwitchCase higherCase;
7589
7590 public TokenSwitchCase (Token original) : base (original) { }
7591
7592 public override void DebString (StringBuilder sb)
7593 {
7594 if (rVal1 == null) {
7595 sb.Append ("default: ");
7596 } else {
7597 sb.Append ("case ");
7598 rVal1.DebString (sb);
7599 if (rVal2 != null) {
7600 sb.Append (" ... ");
7601 rVal2.DebString (sb);
7602 }
7603 sb.Append (": ");
7604 }
7605 for (Token t = stmts; t != null; t = t.nextToken) {
7606 t.DebString (sb);
7607 }
7608 }
7609 }
7610
7611 public class TokenStmtThrow : TokenStmt {
7612
7613 public TokenRVal rVal; // null if rethrow style
7614
7615 public TokenStmtThrow (Token original) : base (original) { }
7616
7617 public override void DebString (StringBuilder sb)
7618 {
7619 sb.Append ("throw ");
7620 rVal.DebString (sb);
7621 sb.Append (';');
7622 }
7623 }
7624
7625 /**
7626 * @brief Encapsulates related try, catch and finally statements.
7627 */
7628 public class TokenStmtTry : TokenStmt {
7629
7630 public TokenStmtBlock tryStmt;
7631 public TokenDeclVar catchVar; // null iff catchStmt is null
7632 public TokenStmtBlock catchStmt; // can be null
7633 public TokenStmtBlock finallyStmt; // can be null
7634 public Dictionary<string, IntermediateLeave> iLeaves = new Dictionary<string, IntermediateLeave> ();
7635
7636 public TokenStmtTry (Token original) : base (original) { }
7637
7638 public override void DebString (StringBuilder sb)
7639 {
7640 sb.Append ("try ");
7641 tryStmt.DebString (sb);
7642 if (catchStmt != null) {
7643 sb.Append ("catch (");
7644 sb.Append (catchVar.type.ToString ());
7645 sb.Append (' ');
7646 sb.Append (catchVar.name.val);
7647 sb.Append (") ");
7648 catchStmt.DebString (sb);
7649 }
7650 if (finallyStmt != null) {
7651 sb.Append ("finally ");
7652 finallyStmt.DebString (sb);
7653 }
7654 }
7655 }
7656
7657 public class IntermediateLeave {
7658 public ScriptMyLabel jumpIntoLabel;
7659 public ScriptMyLabel jumpAwayLabel;
7660 }
7661
7662 public class TokenStmtVarIniDef : TokenStmt {
7663 public TokenLVal var;
7664 public TokenStmtVarIniDef (Token original) : base (original) { }
7665 }
7666
7667 public class TokenStmtWhile : TokenStmt {
7668
7669 public TokenRValParen testRVal;
7670 public TokenStmt bodyStmt;
7671
7672 public TokenStmtWhile (Token original) : base (original) { }
7673
7674 public override void DebString (StringBuilder sb)
7675 {
7676 sb.Append ("while ");
7677 testRVal.DebString (sb);
7678 sb.Append (' ');
7679 bodyStmt.DebString (sb);
7680 }
7681 }
7682
7683 /**
7684 * @brief type expressions (right-hand of 'is' keyword).
7685 */
7686 public class TokenTypeExp : Token {
7687 public TokenTypeExp (Token original) : base (original) { }
7688 }
7689
7690 public class TokenTypeExpBinOp : TokenTypeExp {
7691 public TokenTypeExp leftOp;
7692 public Token binOp;
7693 public TokenTypeExp rightOp;
7694
7695 public TokenTypeExpBinOp (Token original) : base (original) { }
7696 }
7697
7698 public class TokenTypeExpNot : TokenTypeExp {
7699 public TokenTypeExp typeExp;
7700
7701 public TokenTypeExpNot (Token original) : base (original) { }
7702 }
7703
7704 public class TokenTypeExpPar : TokenTypeExp {
7705 public TokenTypeExp typeExp;
7706
7707 public TokenTypeExpPar (Token original) : base (original) { }
7708 }
7709
7710 public class TokenTypeExpType : TokenTypeExp {
7711 public TokenType typeToken;
7712
7713 public TokenTypeExpType (Token original) : base (original) { }
7714 }
7715
7716 public class TokenTypeExpUndef : TokenTypeExp {
7717 public TokenTypeExpUndef (Token original) : base (original) { }
7718 }
7719}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTokenize.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTokenize.cs
new file mode 100644
index 0000000..a767dcf
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTokenize.cs
@@ -0,0 +1,1729 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/**
29 * @brief Parse raw source file string into token list.
30 *
31 * Usage:
32 *
33 * emsg = some function to output error messages to
34 * source = string containing entire source file
35 *
36 * TokenBegin tokenBegin = TokenBegin.Construct (emsg, source);
37 *
38 * tokenBegin = null: tokenizing error
39 * else: first (dummy) token in file
40 * the rest are chained by nextToken,prevToken
41 * final token is always a (dummy) TokenEnd
42 */
43
44using System;
45using System.Collections.Generic;
46using System.IO;
47using System.Net;
48using System.Reflection;
49using System.Reflection.Emit;
50using System.Text;
51
52using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
53using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
54using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
55using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
56using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
57using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
58using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
59
60namespace OpenSim.Region.ScriptEngine.XMREngine {
61
62 public delegate void TokenErrorMessage (Token token, string message);
63
64 /**
65 * @brief base class for all tokens
66 */
67 public class Token {
68 public static readonly int MAX_NAME_LEN = 255;
69 public static readonly int MAX_STRING_LEN = 4096;
70
71 public Token nextToken;
72 public Token prevToken;
73 public bool nr2l;
74
75 // used for error message printing
76 public TokenErrorMessage emsg;
77 public string file = "";
78 public int line;
79 public int posn;
80 public Token copiedFrom;
81
82 /**
83 * @brief construct a token coming directly from a source file
84 * @param emsg = object that error messages get sent to
85 * @param file = source file name (or "" if none)
86 * @param line = source file line number
87 * @param posn = token's position within that source line
88 */
89 public Token (TokenErrorMessage emsg, string file, int line, int posn)
90 {
91 this.emsg = emsg;
92 this.file = file;
93 this.line = line;
94 this.posn = posn;
95 }
96
97 /**
98 * @brief construct a token with same error message parameters
99 * @param original = original token to create from
100 */
101 public Token (Token original)
102 {
103 if (original != null) {
104 this.emsg = original.emsg;
105 this.file = original.file;
106 this.line = original.line;
107 this.posn = original.posn;
108 this.nr2l = original.nr2l;
109 }
110 }
111
112 /**
113 * @brief output an error message associated with this token
114 * sends the message to the token's error object
115 * @param message = error message string
116 */
117 public void ErrorMsg (string message)
118 {
119 if (emsg != null) {
120 emsg (this, message);
121 }
122 }
123
124 /*
125 * Generate a unique string (for use in CIL label names, etc)
126 */
127 public string Unique
128 {
129 get { return file + "_" + line + "_" + posn; }
130 }
131
132 /*
133 * Generate source location string (for use in error messages)
134 */
135 public string SrcLoc
136 {
137 get {
138 string loc = file + "(" + line + "," + posn + ")";
139 if (copiedFrom == null) return loc;
140 string fromLoc = copiedFrom.SrcLoc;
141 if (fromLoc.StartsWith (loc)) return fromLoc;
142 return loc + ":" + fromLoc;
143 }
144 }
145
146 /*
147 * Used in generic instantiation to copy token.
148 * Only valid for parsing tokens, not reduction tokens
149 * because it is a shallow copy.
150 */
151 public Token CopyToken (Token src)
152 {
153 Token t = (Token)this.MemberwiseClone ();
154 t.file = src.file;
155 t.line = src.line;
156 t.posn = src.posn;
157 t.copiedFrom = this;
158 return t;
159 }
160
161 /*
162 * Generate debugging string - should look like source code.
163 */
164 public virtual void DebString (StringBuilder sb)
165 {
166 sb.Append (this.ToString ());
167 }
168 }
169
170
171 /**
172 * @brief token that begins a source file
173 * Along with TokenEnd, it keeps insertion/removal of intermediate tokens
174 * simple as the intermediate tokens always have non-null nextToken,prevToken.
175 */
176 public class TokenBegin : Token {
177
178 public int expiryDays = Int32.MaxValue; // has seen 'XMROption expiryDays;'
179
180 private class Options {
181 public bool arrays; // has seen 'XMROption arrays;'
182 public bool advFlowCtl; // has seen 'XMROption advFlowCtl;'
183 public bool tryCatch; // has seen 'XMROption tryCatch;'
184 public bool objects; // has seen 'XMROption objects;'
185 public bool chars; // has seen 'XMROption chars;'
186 public bool noRightToLeft; // has seen 'XMROption noRightToLeft;'
187 public bool dollarsigns; // has seen 'XMROption dollarsigns;'
188 }
189
190 private bool youveAnError; // there was some error tokenizing
191 private int bolIdx; // index in 'source' at begining of current line
192 private int lineNo; // current line in source file, starting at 0
193 private string filNam; // current source file name
194 private string source; // the whole script source code
195 private Token lastToken; // last token created so far
196 private string cameFrom; // where the source came from
197 private TextWriter saveSource; // save copy of source here (or null)
198 private Options options = new Options ();
199
200 /**
201 * @brief convert a source file in the form of a string
202 * to a list of raw tokens
203 * @param cameFrom = where the source came from
204 * @param emsg = where to output messages to
205 * @param source = whole source file contents
206 * @returns null: conversion error, message already output
207 * else: list of tokens, starting with TokenBegin, ending with TokenEnd.
208 */
209 public static TokenBegin Construct (string cameFrom, TextWriter saveSource, TokenErrorMessage emsg, string source, out string sourceHash)
210 {
211 sourceHash = null;
212
213 /*
214 * Now do the tokenization.
215 */
216 TokenBegin tokenBegin = new TokenBegin (emsg, "", 0, 0);
217 tokenBegin.cameFrom = cameFrom;
218 tokenBegin.saveSource = saveSource;
219 tokenBegin.lastToken = tokenBegin;
220 tokenBegin.source = source;
221 tokenBegin.filNam = cameFrom;
222 if (saveSource != null) saveSource.WriteLine (source);
223 tokenBegin.Tokenize ();
224 if (tokenBegin.youveAnError) return null;
225 tokenBegin.AppendToken (new TokenEnd (emsg, tokenBegin.filNam, ++ tokenBegin.lineNo, 0));
226
227 /*
228 * Return source hash so caller can know if source changes.
229 */
230 System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create ();
231 byte[] hashBytes = md5.ComputeHash (new TokenStream (tokenBegin));
232 int hashBytesLen = hashBytes.Length;
233 StringBuilder sb = new StringBuilder (hashBytesLen * 2);
234 for (int i = 0; i < hashBytesLen; i ++) {
235 sb.Append (hashBytes[i].ToString ("X2"));
236 }
237 sourceHash = sb.ToString ();
238 if (saveSource != null) {
239 saveSource.WriteLine (" ");
240 saveSource.WriteLine ("********************************************************************************");
241 saveSource.WriteLine ("**** source hash: " + sourceHash);
242 saveSource.WriteLine ("********************************************************************************");
243 }
244
245 return tokenBegin;
246 }
247
248 private TokenBegin (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
249
250 /*
251 * Stream consisting of all the tokens.
252 * Null delimeters between the tokens.
253 * Used for creating the source hash.
254 */
255 private class TokenStream : Stream {
256 private Token curTok;
257 private bool delim;
258 private byte[] curBuf;
259 private int curOfs;
260 private int curLen;
261
262 public TokenStream (Token t)
263 {
264 curTok = t;
265 }
266
267 public override bool CanRead { get { return true; } }
268 public override bool CanSeek { get { return false; } }
269 public override bool CanWrite { get { return false; } }
270 public override long Length { get { return 0; } }
271 public override long Position { get { return 0; } set { } }
272
273 public override void Write (byte[] buffer, int offset, int count) { }
274 public override void Flush () { }
275 public override long Seek (long offset, SeekOrigin origin) { return 0; }
276 public override void SetLength (long value) { }
277
278 public override int Read (byte[] buffer, int offset, int count)
279 {
280 int len, total;
281 for (total = 0; total < count; total += len) {
282 while ((len = curLen - curOfs) <= 0) {
283 if (curTok is TokenEnd) goto done;
284 curTok = curTok.nextToken;
285 if (curTok is TokenEnd) goto done;
286 curBuf = System.Text.Encoding.UTF8.GetBytes (curTok.ToString ());
287 curOfs = 0;
288 curLen = curBuf.Length;
289 delim = true;
290 }
291 if (delim) {
292 buffer[offset+total] = 0;
293 delim = false;
294 len = 1;
295 } else {
296 if (len > count - total) len = count - total;
297 Array.Copy (curBuf, curOfs, buffer, offset + total, len);
298 curOfs += len;
299 }
300 }
301 done:
302 return total;
303 }
304 }
305
306 /*
307 * Produces raw token stream: names, numbers, strings, keywords/delimeters.
308 * @param this.source = whole source file in one string
309 * @returns this.nextToken = filled in with tokens
310 * this.youveAnError = true: some tokenizing error
311 * false: successful
312 */
313 private void Tokenize ()
314 {
315 bolIdx = 0;
316 lineNo = 0;
317 for (int i = 0; i < source.Length; i ++) {
318 char c = source[i];
319 if (c == '\n') {
320
321 /*
322 * Increment source line number and set char index of beg of next line.
323 */
324 lineNo ++;
325 bolIdx = i + 1;
326
327 /*
328 * Check for '#' lineno filename newline
329 * lineno is line number of next line in file
330 * If found, save values and remove tokens from stream
331 */
332 if ((lastToken is TokenStr) &&
333 (lastToken.prevToken is TokenInt) &&
334 (lastToken.prevToken.prevToken is TokenKwHash)) {
335 filNam = ((TokenStr)lastToken).val;
336 lineNo = ((TokenInt)lastToken.prevToken).val;
337 lastToken = lastToken.prevToken.prevToken.prevToken;
338 lastToken.nextToken = null;
339 }
340 continue;
341 }
342
343 /*
344 * Skip over whitespace.
345 */
346 if (c <= ' ') continue;
347
348 /*
349 * Skip over comments.
350 */
351 if ((i + 2 <= source.Length) && source.Substring (i, 2).Equals ("//")) {
352 while ((i < source.Length) && (source[i] != '\n')) i ++;
353 lineNo ++;
354 bolIdx = i + 1;
355 continue;
356 }
357 if ((i + 2 <= source.Length) && (source.Substring (i, 2).Equals ("/*"))) {
358 i += 2;
359 while ((i + 1 < source.Length) && (((c = source[i]) != '*') || (source[i+1] != '/'))) {
360 if (c == '\n') {
361 lineNo ++;
362 bolIdx = i + 1;
363 }
364 i ++;
365 }
366 i ++;
367 continue;
368 }
369
370 /*
371 * Check for numbers.
372 */
373 if ((c >= '0') && (c <= '9')) {
374 int j = TryParseFloat (i);
375 if (j == 0) j = TryParseInt (i);
376 i = -- j;
377 continue;
378 }
379 if ((c == '.') && (source[i+1] >= '0') && (source[i+1] <= '9')) {
380 int j = TryParseFloat (i);
381 if (j > 0) i = -- j;
382 continue;
383 }
384
385 /*
386 * Check for quoted strings.
387 */
388 if (c == '"') {
389 StringBuilder sb = new StringBuilder ();
390 bool backslash;
391 int j;
392
393 backslash = false;
394 for (j = i; ++ j < source.Length;) {
395 c = source[j];
396 if (c == '\\' && !backslash) {
397 backslash = true;
398 continue;
399 }
400 if (c == '\n') {
401 lineNo ++;
402 bolIdx = j + 1;
403 } else {
404 if (!backslash && (c == '"')) break;
405 if (backslash && (c == 'n')) c = '\n';
406 if (backslash && (c == 't')) {
407 sb.Append (" ");
408 c = ' ';
409 }
410 }
411 backslash = false;
412 sb.Append (c);
413 }
414 if (j - i > MAX_STRING_LEN) {
415 TokenError (i, "string too long, max " + MAX_STRING_LEN);
416 } else {
417 AppendToken (new TokenStr (emsg, filNam, lineNo, i - bolIdx, sb.ToString ()));
418 }
419 i = j;
420 continue;
421 }
422
423 /*
424 * Check for quoted characters.
425 */
426 if (c == '\'') {
427 char cb = (char)0;
428 bool backslash, overflow, underflow;
429 int j;
430
431 backslash = false;
432 overflow = false;
433 underflow = true;
434 for (j = i; ++ j < source.Length;) {
435 c = source[j];
436 if (c == '\\' && !backslash) {
437 backslash = true;
438 continue;
439 }
440 if (c == '\n') {
441 lineNo ++;
442 bolIdx = j + 1;
443 } else {
444 if (!backslash && (c == '\'')) break;
445 if (backslash && (c == 'n')) c = '\n';
446 if (backslash && (c == 't')) c = '\t';
447 }
448 backslash = false;
449 overflow = !underflow;
450 underflow = false;
451 cb = c;
452 }
453 if (underflow || overflow) {
454 TokenError (i, "character must be exactly one character");
455 } else {
456 AppendToken (new TokenChar (emsg, filNam, lineNo, i - bolIdx, cb));
457 }
458 i = j;
459 continue;
460 }
461
462 /*
463 * Check for keywords/names.
464 */
465 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || (c == '$' && options.dollarsigns)) {
466 int j;
467
468 for (j = i; ++ j < source.Length;) {
469 c = source[j];
470 if (c >= 'a' && c <= 'z') continue;
471 if (c >= 'A' && c <= 'Z') continue;
472 if (c >= '0' && c <= '9') continue;
473 if (c == '$' && options.dollarsigns) continue;
474 if (c != '_') break;
475 }
476 if (j - i > MAX_NAME_LEN) {
477 TokenError (i, "name too long, max " + MAX_NAME_LEN);
478 } else {
479 string name = source.Substring (i, j - i);
480 if (name == "quaternion") name = "rotation"; // see lslangtest1.lsl
481 if (keywords.ContainsKey (name)) {
482 Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
483 AppendToken ((Token)keywords[name].Invoke (args));
484 } else if (options.arrays && arrayKeywords.ContainsKey (name)) {
485 Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
486 AppendToken ((Token)arrayKeywords[name].Invoke (args));
487 } else if (options.advFlowCtl && advFlowCtlKeywords.ContainsKey (name)) {
488 Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
489 AppendToken ((Token)advFlowCtlKeywords[name].Invoke (args));
490 } else if (options.tryCatch && tryCatchKeywords.ContainsKey (name)) {
491 Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
492 AppendToken ((Token)tryCatchKeywords[name].Invoke (args));
493 } else if (options.objects && objectsKeywords.ContainsKey (name)) {
494 Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
495 AppendToken ((Token)objectsKeywords[name].Invoke (args));
496 } else if (options.chars && charsKeywords.ContainsKey (name)) {
497 Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
498 AppendToken ((Token)charsKeywords[name].Invoke (args));
499 } else {
500 AppendToken (new TokenName (emsg, filNam, lineNo, i - bolIdx, name));
501 }
502 }
503 i = -- j;
504 continue;
505 }
506
507 /*
508 * Check for option enables.
509 */
510 if ((c == ';') && (lastToken is TokenName) &&
511 (lastToken.prevToken is TokenName) &&
512 (strcasecmp(((TokenName)lastToken.prevToken).val, "xmroption") == 0)) {
513 string opt = ((TokenName)lastToken).val;
514 if (strcasecmp (opt, "arrays") == 0) {
515 options.arrays = true;
516 } else if (strcasecmp (opt, "advflowctl") == 0) {
517 options.advFlowCtl = true;
518 } else if (strcasecmp (opt, "trycatch") == 0) {
519 options.tryCatch = true;
520 } else if (strcasecmp (opt, "objects") == 0) {
521 options.objects = true;
522 } else if (strcasecmp (opt, "chars") == 0) {
523 options.chars = true;
524 } else if (strcasecmp (opt, "norighttoleft") == 0) {
525 options.noRightToLeft = true;
526 } else if (strcasecmp (opt, "dollarsigns") == 0) {
527 options.dollarsigns = true;
528 } else {
529 lastToken.ErrorMsg ("unknown XMROption");
530 }
531 lastToken = lastToken.prevToken.prevToken;
532 lastToken.nextToken = null;
533 continue;
534 }
535
536 /*
537 * Handle 'xmroption' 'expirydays' numberofdays ';'
538 */
539 if ((c == ';') &&
540 (lastToken is TokenInt) &&
541 (lastToken.prevToken is TokenName) &&
542 (lastToken.prevToken.prevToken is TokenName) &&
543 (strcasecmp(((TokenName)lastToken.prevToken.prevToken).val, "xmroption") == 0) &&
544 (strcasecmp(((TokenName)lastToken.prevToken).val, "expirydays") == 0)) {
545 expiryDays = ((TokenInt)lastToken).val;
546 this.lastToken = lastToken.prevToken.prevToken.prevToken;
547 this.lastToken.nextToken = null;
548 continue;
549 }
550
551
552 /*
553 * Handle 'xmroption' 'include' sourceurl ';'
554 */
555 if ((c == ';') &&
556 (lastToken is TokenStr) &&
557 (lastToken.prevToken is TokenName) &&
558 (lastToken.prevToken.prevToken is TokenName) &&
559 (strcasecmp(((TokenName)lastToken.prevToken.prevToken).val, "xmroption") == 0) &&
560 (strcasecmp(((TokenName)lastToken.prevToken).val, "include") == 0)) {
561
562 string newURL = ((TokenStr)lastToken).val;
563 if (newURL == "") {
564 lastToken.ErrorMsg ("empty URL string");
565 continue;
566 }
567 string newCameFrom = CreateURL (this.cameFrom, newURL);
568 string newSource = ReadSourceFromURL (newCameFrom);
569
570 this.lastToken = lastToken.prevToken.prevToken.prevToken;
571 this.lastToken.nextToken = null;
572
573 if (newSource != null) {
574 if (saveSource != null) {
575 saveSource.WriteLine (" ");
576 saveSource.WriteLine ("********************************************************************************");
577 saveSource.WriteLine ("**** include url: " + newCameFrom);
578 saveSource.WriteLine ("********************************************************************************");
579 saveSource.WriteLine (newSource);
580 }
581
582 string saveSourc = this.source;
583 string saveFilNam = this.filNam;
584 string saveCameFrom = this.cameFrom;
585 int saveBolIdx = this.bolIdx;
586 int saveLineNo = this.lineNo;
587 Options saveOptions = this.options;
588 this.source = newSource;
589 this.filNam = newURL;
590 this.cameFrom = newCameFrom;
591 this.options = new Options ();
592 this.Tokenize ();
593 this.source = saveSourc;
594 this.filNam = saveFilNam;
595 this.cameFrom = saveCameFrom;
596 this.bolIdx = saveBolIdx;
597 this.lineNo = saveLineNo;
598 this.options = saveOptions;
599 }
600 continue;
601 }
602
603 /*
604 * Lastly, check for delimeters.
605 */
606 {
607 int j;
608 int len = 0;
609
610 for (j = 0; j < delims.Length; j ++) {
611 len = delims[j].str.Length;
612 if ((i + len <= source.Length) && (source.Substring (i, len).Equals (delims[j].str))) break;
613 }
614 if (j < delims.Length) {
615 Object[] args = { emsg, filNam, lineNo, i - bolIdx };
616 Token kwToken = (Token)delims[j].ctorInfo.Invoke (args);
617 AppendToken (kwToken);
618 i += -- len;
619 continue;
620 }
621 }
622
623 /*
624 * Don't know what it is!
625 */
626 TokenError (i, "unknown character '" + c + "'");
627 }
628 }
629
630 private static int strcasecmp (String s, String t)
631 {
632 return String.Compare(s,t,StringComparison.OrdinalIgnoreCase);
633 }
634
635 /**
636 * @brief try to parse a floating-point number from the source
637 * @param i = starting position within this.source of number
638 * @returns 0: not a floating point number, try something else
639 * else: position in this.source of terminating character, ie, past number
640 * TokenFloat appended to token list
641 * or error message has been output
642 */
643 private int TryParseFloat (int i)
644 {
645 bool decimals, error, negexp, nulexp;
646 char c;
647 double f, f10;
648 int exponent, j, x, y;
649 ulong m, mantissa;
650
651 decimals = false;
652 error = false;
653 exponent = 0;
654 mantissa = 0;
655 for (j = i; j < source.Length; j ++) {
656 c = source[j];
657 if ((c >= '0') && (c <= '9')) {
658 m = mantissa * 10 + (ulong)(c - '0');
659 if (m / 10 != mantissa) {
660 if (!decimals) exponent ++;
661 } else {
662 mantissa = m;
663 if (decimals) exponent --;
664 }
665 continue;
666 }
667 if (c == '.') {
668 if (decimals) {
669 TokenError (i, "more than one decimal point");
670 return j;
671 }
672 decimals = true;
673 continue;
674 }
675 if ((c == 'E') || (c == 'e')) {
676 if (++ j >= source.Length) {
677 TokenError (i, "floating exponent off end of source");
678 return j;
679 }
680 c = source[j];
681 negexp = (c == '-');
682 if (negexp || (c == '+')) j ++;
683 y = 0;
684 nulexp = true;
685 for (; j < source.Length; j ++) {
686 c = source[j];
687 if ((c < '0') || (c > '9')) break;
688 x = y * 10 + (c - '0');
689 if (x / 10 != y) {
690 if (!error) TokenError (i, "floating exponent overflow");
691 error = true;
692 }
693 y = x;
694 nulexp = false;
695 }
696 if (nulexp) {
697 TokenError (i, "bad or missing floating exponent");
698 return j;
699 }
700 if (negexp) {
701 x = exponent - y;
702 if (x > exponent) {
703 if (!error) TokenError (i, "floating exponent overflow");
704 error = true;
705 }
706 } else {
707 x = exponent + y;
708 if (x < exponent) {
709 if (!error) TokenError (i, "floating exponent overflow");
710 error = true;
711 }
712 }
713 exponent = x;
714 }
715 break;
716 }
717 if (!decimals) {
718 return 0;
719 }
720
721 f = mantissa;
722 if ((exponent != 0) && (mantissa != 0) && !error) {
723 f10 = 10.0;
724 if (exponent < 0) {
725 exponent = -exponent;
726 while (exponent > 0) {
727 if ((exponent & 1) != 0) {
728 f /= f10;
729 }
730 exponent /= 2;
731 f10 *= f10;
732 }
733 } else {
734 while (exponent > 0) {
735 if ((exponent & 1) != 0) {
736 f *= f10;
737 }
738 exponent /= 2;
739 f10 *= f10;
740 }
741 }
742 }
743 if (!error) {
744 AppendToken (new TokenFloat (emsg, filNam, lineNo, i - bolIdx, f));
745 }
746 return j;
747 }
748
749 /**
750 * @brief try to parse an integer number from the source
751 * @param i = starting position within this.source of number
752 * @returns 0: not an integer number, try something else
753 * else: position in this.source of terminating character, ie, past number
754 * TokenInt appended to token list
755 * or error message has been output
756 */
757 private int TryParseInt (int i)
758 {
759 bool error;
760 char c;
761 int j;
762 uint basse, m, mantissa;
763
764 basse = 10;
765 error = false;
766 mantissa = 0;
767 for (j = i; j < source.Length; j ++) {
768 c = source[j];
769 if ((c >= '0') && (c <= '9')) {
770 m = mantissa * basse + (uint)(c - '0');
771 if (m / basse != mantissa) {
772 if (!error) TokenError (i, "integer overflow");
773 error = true;
774 }
775 mantissa = m;
776 continue;
777 }
778 if ((basse == 16) && ((c >= 'A') && (c <= 'F'))) {
779 m = mantissa * basse + (uint)(c - 'A') + 10U;
780 if (m / basse != mantissa) {
781 if (!error) TokenError (i, "integer overflow");
782 error = true;
783 }
784 mantissa = m;
785 continue;
786 }
787 if ((basse == 16) && ((c >= 'a') && (c <= 'f'))) {
788 m = mantissa * basse + (uint)(c - 'a') + 10U;
789 if (m / basse != mantissa) {
790 if (!error) TokenError (i, "integer overflow");
791 error = true;
792 }
793 mantissa = m;
794 continue;
795 }
796 if (((c == 'x') || (c == 'X')) && (mantissa == 0) && (basse == 10)) {
797 basse = 16;
798 continue;
799 }
800 break;
801 }
802 if (!error) {
803 AppendToken (new TokenInt (emsg, filNam, lineNo, i - bolIdx, (int)mantissa));
804 }
805 return j;
806 }
807
808 /**
809 * @brief append token on to end of list
810 * @param newToken = token to append
811 * @returns with token appended onto this.lastToken
812 */
813 private void AppendToken (Token newToken)
814 {
815 newToken.nextToken = null;
816 newToken.prevToken = lastToken;
817 newToken.nr2l = this.options.noRightToLeft;
818 lastToken.nextToken = newToken;
819 lastToken = newToken;
820 }
821
822 /**
823 * @brief print tokenizing error message
824 * and remember that we've an error
825 * @param i = position within source file of the error
826 * @param message = error message text
827 * @returns with this.youveAnError set
828 */
829 private void TokenError (int i, string message)
830 {
831 Token temp = new Token (this.emsg, this.filNam, this.lineNo, i - this.bolIdx);
832 temp.ErrorMsg (message);
833 youveAnError = true;
834 }
835
836 /**
837 * @brief get a token's constructor
838 * @param tokenType = token's type
839 * @returns token's constructor
840 */
841 private static Type[] constrTypes = new Type[] {
842 typeof (TokenErrorMessage), typeof (string), typeof (int), typeof (int)
843 };
844
845 private static System.Reflection.ConstructorInfo GetTokenCtor (Type tokenType)
846 {
847 return tokenType.GetConstructor (constrTypes);
848 }
849
850 /**
851 * @brief delimeter table
852 */
853 private class Delim {
854 public string str;
855 public System.Reflection.ConstructorInfo ctorInfo;
856 public Delim (string str, Type type)
857 {
858 this.str = str;
859 ctorInfo = GetTokenCtor (type);
860 }
861 }
862
863 private static Delim[] delims = new Delim[] {
864 new Delim ("...", typeof (TokenKwDotDotDot)),
865 new Delim ("&&&", typeof (TokenKwAndAndAnd)),
866 new Delim ("|||", typeof (TokenKwOrOrOr)),
867 new Delim ("<<=", typeof (TokenKwAsnLSh)),
868 new Delim (">>=", typeof (TokenKwAsnRSh)),
869 new Delim ("<=", typeof (TokenKwCmpLE)),
870 new Delim (">=", typeof (TokenKwCmpGE)),
871 new Delim ("==", typeof (TokenKwCmpEQ)),
872 new Delim ("!=", typeof (TokenKwCmpNE)),
873 new Delim ("++", typeof (TokenKwIncr)),
874 new Delim ("--", typeof (TokenKwDecr)),
875 new Delim ("&&", typeof (TokenKwAndAnd)),
876 new Delim ("||", typeof (TokenKwOrOr)),
877 new Delim ("+=", typeof (TokenKwAsnAdd)),
878 new Delim ("&=", typeof (TokenKwAsnAnd)),
879 new Delim ("-=", typeof (TokenKwAsnSub)),
880 new Delim ("*=", typeof (TokenKwAsnMul)),
881 new Delim ("/=", typeof (TokenKwAsnDiv)),
882 new Delim ("%=", typeof (TokenKwAsnMod)),
883 new Delim ("|=", typeof (TokenKwAsnOr)),
884 new Delim ("^=", typeof (TokenKwAsnXor)),
885 new Delim ("<<", typeof (TokenKwLSh)),
886 new Delim (">>", typeof (TokenKwRSh)),
887 new Delim ("~", typeof (TokenKwTilde)),
888 new Delim ("!", typeof (TokenKwExclam)),
889 new Delim ("@", typeof (TokenKwAt)),
890 new Delim ("%", typeof (TokenKwMod)),
891 new Delim ("^", typeof (TokenKwXor)),
892 new Delim ("&", typeof (TokenKwAnd)),
893 new Delim ("*", typeof (TokenKwMul)),
894 new Delim ("(", typeof (TokenKwParOpen)),
895 new Delim (")", typeof (TokenKwParClose)),
896 new Delim ("-", typeof (TokenKwSub)),
897 new Delim ("+", typeof (TokenKwAdd)),
898 new Delim ("=", typeof (TokenKwAssign)),
899 new Delim ("{", typeof (TokenKwBrcOpen)),
900 new Delim ("}", typeof (TokenKwBrcClose)),
901 new Delim ("[", typeof (TokenKwBrkOpen)),
902 new Delim ("]", typeof (TokenKwBrkClose)),
903 new Delim (";", typeof (TokenKwSemi)),
904 new Delim (":", typeof (TokenKwColon)),
905 new Delim ("<", typeof (TokenKwCmpLT)),
906 new Delim (">", typeof (TokenKwCmpGT)),
907 new Delim (",", typeof (TokenKwComma)),
908 new Delim (".", typeof (TokenKwDot)),
909 new Delim ("?", typeof (TokenKwQMark)),
910 new Delim ("/", typeof (TokenKwDiv)),
911 new Delim ("|", typeof (TokenKwOr)),
912 new Delim ("#", typeof (TokenKwHash))
913 };
914
915 /**
916 * @brief keyword tables
917 * The keyword tables translate a keyword string
918 * to the corresponding token constructor.
919 */
920 private static Dictionary<string, System.Reflection.ConstructorInfo> keywords = BuildKeywords ();
921 private static Dictionary<string, System.Reflection.ConstructorInfo> arrayKeywords = BuildArrayKeywords ();
922 private static Dictionary<string, System.Reflection.ConstructorInfo> advFlowCtlKeywords = BuildAdvFlowCtlKeywords ();
923 private static Dictionary<string, System.Reflection.ConstructorInfo> tryCatchKeywords = BuildTryCatchKeywords ();
924 private static Dictionary<string, System.Reflection.ConstructorInfo> objectsKeywords = BuildObjectsKeywords ();
925 private static Dictionary<string, System.Reflection.ConstructorInfo> charsKeywords = BuildCharsKeywords ();
926
927 private static Dictionary<string, System.Reflection.ConstructorInfo> BuildKeywords ()
928 {
929 Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
930
931 kws.Add ("default", GetTokenCtor (typeof (TokenKwDefault)));
932 kws.Add ("do", GetTokenCtor (typeof (TokenKwDo)));
933 kws.Add ("else", GetTokenCtor (typeof (TokenKwElse)));
934 kws.Add ("float", GetTokenCtor (typeof (TokenTypeFloat)));
935 kws.Add ("for", GetTokenCtor (typeof (TokenKwFor)));
936 kws.Add ("if", GetTokenCtor (typeof (TokenKwIf)));
937 kws.Add ("integer", GetTokenCtor (typeof (TokenTypeInt)));
938 kws.Add ("list", GetTokenCtor (typeof (TokenTypeList)));
939 kws.Add ("jump", GetTokenCtor (typeof (TokenKwJump)));
940 kws.Add ("key", GetTokenCtor (typeof (TokenTypeKey)));
941 kws.Add ("return", GetTokenCtor (typeof (TokenKwRet)));
942 kws.Add ("rotation", GetTokenCtor (typeof (TokenTypeRot)));
943 kws.Add ("state", GetTokenCtor (typeof (TokenKwState)));
944 kws.Add ("string", GetTokenCtor (typeof (TokenTypeStr)));
945 kws.Add ("vector", GetTokenCtor (typeof (TokenTypeVec)));
946 kws.Add ("while", GetTokenCtor (typeof (TokenKwWhile)));
947
948 return kws;
949 }
950
951 private static Dictionary<string, System.Reflection.ConstructorInfo> BuildArrayKeywords ()
952 {
953 Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
954
955 kws.Add ("array", GetTokenCtor (typeof (TokenTypeArray)));
956 kws.Add ("foreach", GetTokenCtor (typeof (TokenKwForEach)));
957 kws.Add ("in", GetTokenCtor (typeof (TokenKwIn)));
958 kws.Add ("is", GetTokenCtor (typeof (TokenKwIs)));
959 kws.Add ("object", GetTokenCtor (typeof (TokenTypeObject)));
960 kws.Add ("undef", GetTokenCtor (typeof (TokenKwUndef)));
961
962 return kws;
963 }
964
965 private static Dictionary<string, System.Reflection.ConstructorInfo> BuildAdvFlowCtlKeywords ()
966 {
967 Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
968
969 kws.Add ("break", GetTokenCtor (typeof (TokenKwBreak)));
970 kws.Add ("case", GetTokenCtor (typeof (TokenKwCase)));
971 kws.Add ("constant", GetTokenCtor (typeof (TokenKwConst)));
972 kws.Add ("continue", GetTokenCtor (typeof (TokenKwCont)));
973 kws.Add ("switch", GetTokenCtor (typeof (TokenKwSwitch)));
974
975 return kws;
976 }
977
978 private static Dictionary<string, System.Reflection.ConstructorInfo> BuildTryCatchKeywords ()
979 {
980 Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
981
982 kws.Add ("catch", GetTokenCtor (typeof (TokenKwCatch)));
983 kws.Add ("exception", GetTokenCtor (typeof (TokenTypeExc)));
984 kws.Add ("finally", GetTokenCtor (typeof (TokenKwFinally)));
985 kws.Add ("throw", GetTokenCtor (typeof (TokenKwThrow)));
986 kws.Add ("try", GetTokenCtor (typeof (TokenKwTry)));
987
988 return kws;
989 }
990
991 private static Dictionary<string, System.Reflection.ConstructorInfo> BuildObjectsKeywords ()
992 {
993 Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
994
995 kws.Add ("abstract", GetTokenCtor (typeof (TokenKwAbstract)));
996 kws.Add ("base", GetTokenCtor (typeof (TokenKwBase)));
997 kws.Add ("class", GetTokenCtor (typeof (TokenKwClass)));
998 kws.Add ("constructor", GetTokenCtor (typeof (TokenKwConstructor)));
999 kws.Add ("delegate", GetTokenCtor (typeof (TokenKwDelegate)));
1000 kws.Add ("destructor", GetTokenCtor (typeof (TokenKwDestructor)));
1001 kws.Add ("final", GetTokenCtor (typeof (TokenKwFinal)));
1002 kws.Add ("get", GetTokenCtor (typeof (TokenKwGet)));
1003 kws.Add ("interface", GetTokenCtor (typeof (TokenKwInterface)));
1004 kws.Add ("new", GetTokenCtor (typeof (TokenKwNew)));
1005 kws.Add ("override", GetTokenCtor (typeof (TokenKwOverride)));
1006 kws.Add ("partial", GetTokenCtor (typeof (TokenKwPartial)));
1007 kws.Add ("private", GetTokenCtor (typeof (TokenKwPrivate)));
1008 kws.Add ("protected", GetTokenCtor (typeof (TokenKwProtected)));
1009 kws.Add ("public", GetTokenCtor (typeof (TokenKwPublic)));
1010 kws.Add ("set", GetTokenCtor (typeof (TokenKwSet)));
1011 kws.Add ("static", GetTokenCtor (typeof (TokenKwStatic)));
1012 kws.Add ("this", GetTokenCtor (typeof (TokenKwThis)));
1013 kws.Add ("typedef", GetTokenCtor (typeof (TokenKwTypedef)));
1014 kws.Add ("virtual", GetTokenCtor (typeof (TokenKwVirtual)));
1015
1016 return kws;
1017 }
1018
1019 private static Dictionary<string, System.Reflection.ConstructorInfo> BuildCharsKeywords ()
1020 {
1021 Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
1022
1023 kws.Add ("char", GetTokenCtor (typeof (TokenTypeChar)));
1024
1025 return kws;
1026 }
1027
1028 /**
1029 * @brief Create a URL from a base URL and a relative string
1030 * @param oldurl = base url string
1031 * @param relurl = relative url
1032 * @returns new url string
1033 */
1034 private static string CreateURL (string oldurl, string relurl)
1035 {
1036 if (relurl.IndexOf ("://") >= 0) return relurl;
1037 StringBuilder newurl = new StringBuilder (oldurl.Length + relurl.Length);
1038 if (relurl[0] == '/') {
1039 // file:///oldname + /newname => file:///newname
1040 // http://webserver.com/oldname + /newname => http://webserver.com/newname
1041 int i = oldurl.IndexOf ("://") + 3;
1042 int j = oldurl.IndexOf ('/', i);
1043 if (j < 0) j = oldurl.Length;
1044 newurl.Append (oldurl.Substring (0, j));
1045 newurl.Append (relurl);
1046 } else {
1047 // file:///oldname + newname => file:///newname
1048 // http://webserver.com/oldname + newname => http://webserver.com/newname
1049 int i = oldurl.LastIndexOf ('/') + 1;
1050 newurl.Append (oldurl.Substring (0, i));
1051 newurl.Append (relurl);
1052 }
1053 return newurl.ToString ();
1054 }
1055
1056 /**
1057 * @brief Read source file from a webserver somewhere out there.
1058 */
1059 private const int MAX_INCLUDE_SIZE = 100000;
1060 private Dictionary<string, string> scriptIncludes = new Dictionary<string, string> ();
1061 private string ReadSourceFromURL (string url)
1062 {
1063 Stream stream = null;
1064 StreamReader reader = null;
1065
1066 try {
1067
1068 /*
1069 * Get stream to read from webserver.
1070 */
1071 stream = MMRWebRequest.MakeRequest ("GET", url, null, 0);
1072 reader = new StreamReader (stream);
1073
1074 /*
1075 * Read file from stream.
1076 */
1077 char[] buf = new char[4000];
1078 int len = 0;
1079 int total = 0;
1080 List<char[]> fullBuffs = new List<char[]> ();
1081 string signature = null;
1082
1083 while (true) {
1084
1085 /*
1086 * Read a big chunk of characters.
1087 */
1088 len = reader.ReadBlock (buf, 0, buf.Length);
1089
1090 /*
1091 * Signature is first line of the first chunk read and must be contained therein.
1092 * If an include file with the same signature has already been seen by this script,
1093 * this include file is ignored.
1094 */
1095 if (signature == null) {
1096 signature = new String (buf, 0, len);
1097 int siglen = signature.IndexOf ('\n');
1098 if (siglen <= 0) {
1099 throw new Exception ("missing signature in first " + len + " characters: " + url);
1100 }
1101 signature = signature.Substring (0, siglen);
1102 if (scriptIncludes.ContainsKey (signature)) return null;
1103 scriptIncludes.Add (signature, "");
1104 }
1105
1106 /*
1107 * Signature is ok, stash full blocks away and keep reading.
1108 * If short read, means we have hit the end of the stream.
1109 */
1110 total += len;
1111 if (total > MAX_INCLUDE_SIZE) {
1112 throw new Exception ("script include exceeds maximum " + MAX_INCLUDE_SIZE + ": " + url);
1113 }
1114 if (len < buf.Length) break;
1115 fullBuffs.Add (buf);
1116 buf = new char[4000];
1117 }
1118
1119 /*
1120 * Return the whole thing as one string.
1121 */
1122 StringBuilder sb = new StringBuilder (total + url.Length + 20);
1123 sb.Append ("# 1 \"");
1124 sb.Append (url);
1125 sb.Append ("\"\n");
1126 foreach (char[] fullBuff in fullBuffs) {
1127 sb.Append (fullBuff);
1128 }
1129 sb.Append (buf, 0, len);
1130 return sb.ToString ();
1131 } finally {
1132 if (reader != null) reader.Close ();
1133 else if (stream != null) stream.Close ();
1134 }
1135 }
1136 }
1137
1138 /**
1139 * @brief All output token types in addition to TokenBegin.
1140 * They are all sub-types of Token.
1141 */
1142
1143 public class TokenChar : Token {
1144 public char val;
1145 public TokenChar (TokenErrorMessage emsg, string file, int line, int posn, char val) : base (emsg, file, line, posn)
1146 {
1147 this.val = val;
1148 }
1149 public TokenChar (Token original, char val) : base (original)
1150 {
1151 this.val = val;
1152 }
1153 public override string ToString ()
1154 {
1155 switch (val) {
1156 case '\'': return "'\\''";
1157 case '\\': return "'\\\\'";
1158 case '\n': return "'\\n'";
1159 case '\t': return "'\\t'";
1160 default: return "'" + val + "'";
1161 }
1162 }
1163 }
1164
1165 public class TokenFloat : Token {
1166 public double val;
1167 public TokenFloat (TokenErrorMessage emsg, string file, int line, int posn, double val) : base (emsg, file, line, posn)
1168 {
1169 this.val = val;
1170 }
1171 public override string ToString ()
1172 {
1173 return val.ToString ();
1174 }
1175 }
1176
1177 public class TokenInt : Token {
1178 public int val;
1179 public TokenInt (TokenErrorMessage emsg, string file, int line, int posn, int val) : base (emsg, file, line, posn)
1180 {
1181 this.val = val;
1182 }
1183 public TokenInt (Token original, int val) : base (original)
1184 {
1185 this.val = val;
1186 }
1187 public override string ToString ()
1188 {
1189 return val.ToString ();
1190 }
1191 }
1192
1193 public class TokenName : Token {
1194 public string val;
1195 public TokenName (TokenErrorMessage emsg, string file, int line, int posn, string val) : base (emsg, file, line, posn)
1196 {
1197 this.val = val;
1198 }
1199 public TokenName (Token original, string val) : base (original)
1200 {
1201 this.val = val;
1202 }
1203 public override string ToString ()
1204 {
1205 return this.val;
1206 }
1207 }
1208
1209 public class TokenStr : Token {
1210 public string val;
1211 public TokenStr (TokenErrorMessage emsg, string file, int line, int posn, string val) : base (emsg, file, line, posn)
1212 {
1213 this.val = val;
1214 }
1215 public override string ToString ()
1216 {
1217 if ((val.IndexOf ('"') < 0) &&
1218 (val.IndexOf ('\\') < 0) &&
1219 (val.IndexOf ('\n') < 0) &&
1220 (val.IndexOf ('\t') < 0)) return "\"" + val + "\"";
1221
1222 int len = val.Length;
1223 StringBuilder sb = new StringBuilder (len * 2 + 2);
1224 sb.Append ('"');
1225 for (int i = 0; i < len; i ++) {
1226 char c = val[i];
1227 switch (c) {
1228 case '"': {
1229 sb.Append ('\\');
1230 sb.Append ('"');
1231 break;
1232 }
1233 case '\\': {
1234 sb.Append ('\\');
1235 sb.Append ('\\');
1236 break;
1237 }
1238 case '\n': {
1239 sb.Append ('\\');
1240 sb.Append ('n');
1241 break;
1242 }
1243 case '\t': {
1244 sb.Append ('\\');
1245 sb.Append ('t');
1246 break;
1247 }
1248 default: {
1249 sb.Append (c);
1250 break;
1251 }
1252 }
1253 }
1254 return sb.ToString ();
1255 }
1256 }
1257
1258 /*
1259 * This one marks the end-of-file.
1260 */
1261 public class TokenEnd : Token { public TokenEnd (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { } }
1262
1263 /*
1264 * Various keywords and delimeters.
1265 */
1266 public delegate object TokenRValConstBinOpDelegate (object left, object right);
1267 public delegate object TokenRValConstUnOpDelegate (object right);
1268
1269 public class TokenKw : Token {
1270 public TokenRValConstBinOpDelegate binOpConst;
1271 public TokenRValConstUnOpDelegate unOpConst;
1272 public bool sdtClassOp;
1273 public TokenKw (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1274 public TokenKw (Token original) : base (original) { }
1275 }
1276
1277 public class TokenKwDotDotDot : TokenKw { public TokenKwDotDotDot (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwDotDotDot (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "..."; } }
1278 public class TokenKwAndAndAnd : TokenKw { public TokenKwAndAndAnd (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwAndAndAnd (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "&&&"; } }
1279 public class TokenKwOrOrOr : TokenKw { public TokenKwOrOrOr (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwOrOrOr (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "|||"; } }
1280 public class TokenKwAsnLSh : TokenKw { public TokenKwAsnLSh (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAsnLSh (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "<<="; } }
1281 public class TokenKwAsnRSh : TokenKw { public TokenKwAsnRSh (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAsnRSh (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return ">>="; } }
1282 public class TokenKwCmpLE : TokenKw { public TokenKwCmpLE (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwCmpLE (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "<="; } }
1283 public class TokenKwCmpGE : TokenKw { public TokenKwCmpGE (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwCmpGE (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return ">="; } }
1284 public class TokenKwCmpEQ : TokenKw { public TokenKwCmpEQ (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwCmpEQ (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "=="; } }
1285 public class TokenKwCmpNE : TokenKw { public TokenKwCmpNE (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwCmpNE (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "!="; } }
1286 public class TokenKwIncr : TokenKw { public TokenKwIncr (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwIncr (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "++"; } }
1287 public class TokenKwDecr : TokenKw { public TokenKwDecr (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwDecr (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "--"; } }
1288 public class TokenKwAndAnd : TokenKw { public TokenKwAndAnd (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAndAnd (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "&&"; } }
1289 public class TokenKwOrOr : TokenKw { public TokenKwOrOr (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwOrOr (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "||"; } }
1290 public class TokenKwAsnAdd : TokenKw { public TokenKwAsnAdd (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAsnAdd (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "+="; } }
1291 public class TokenKwAsnAnd : TokenKw { public TokenKwAsnAnd (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAsnAnd (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "&="; } }
1292 public class TokenKwAsnSub : TokenKw { public TokenKwAsnSub (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAsnSub (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "-="; } }
1293 public class TokenKwAsnMul : TokenKw { public TokenKwAsnMul (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAsnMul (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "*="; } }
1294 public class TokenKwAsnDiv : TokenKw { public TokenKwAsnDiv (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAsnDiv (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "/="; } }
1295 public class TokenKwAsnMod : TokenKw { public TokenKwAsnMod (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAsnMod (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "%="; } }
1296 public class TokenKwAsnOr : TokenKw { public TokenKwAsnOr (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAsnOr (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "|="; } }
1297 public class TokenKwAsnXor : TokenKw { public TokenKwAsnXor (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAsnXor (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "^="; } }
1298 public class TokenKwLSh : TokenKw { public TokenKwLSh (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.LSh; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwLSh (Token original) : base (original) { binOpConst = TokenRValConstOps.LSh; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "<<"; } }
1299 public class TokenKwRSh : TokenKw { public TokenKwRSh (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.RSh; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwRSh (Token original) : base (original) { binOpConst = TokenRValConstOps.RSh; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return ">>"; } }
1300 public class TokenKwTilde : TokenKw { public TokenKwTilde (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Not; sdtClassOp = true; } public TokenKwTilde (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Not; sdtClassOp = true; } public override string ToString () { return "~"; } }
1301 public class TokenKwExclam : TokenKw { public TokenKwExclam (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwExclam (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "!"; } }
1302 public class TokenKwAt : TokenKw { public TokenKwAt (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwAt (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "@"; } }
1303 public class TokenKwMod : TokenKw { public TokenKwMod (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Mod; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwMod (Token original) : base (original) { binOpConst = TokenRValConstOps.Mod; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "%"; } }
1304 public class TokenKwXor : TokenKw { public TokenKwXor (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Xor; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwXor (Token original) : base (original) { binOpConst = TokenRValConstOps.Xor; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "^"; } }
1305 public class TokenKwAnd : TokenKw { public TokenKwAnd (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.And; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAnd (Token original) : base (original) { binOpConst = TokenRValConstOps.And; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "&"; } }
1306 public class TokenKwMul : TokenKw { public TokenKwMul (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Mul; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwMul (Token original) : base (original) { binOpConst = TokenRValConstOps.Mul; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "*"; } }
1307 public class TokenKwParOpen : TokenKw { public TokenKwParOpen (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwParOpen (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "("; } }
1308 public class TokenKwParClose : TokenKw { public TokenKwParClose (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwParClose (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return ")"; } }
1309 public class TokenKwSub : TokenKw { public TokenKwSub (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Sub; unOpConst = TokenRValConstOps.Neg; sdtClassOp = true; } public TokenKwSub (Token original) : base (original) { binOpConst = TokenRValConstOps.Sub; unOpConst = TokenRValConstOps.Neg; sdtClassOp = true; } public override string ToString () { return "-"; } }
1310 public class TokenKwAdd : TokenKw { public TokenKwAdd (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Add; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwAdd (Token original) : base (original) { binOpConst = TokenRValConstOps.Add; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "+"; } }
1311 public class TokenKwAssign : TokenKw { public TokenKwAssign (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwAssign (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "="; } }
1312 public class TokenKwBrcOpen : TokenKw { public TokenKwBrcOpen (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwBrcOpen (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "{"; } }
1313 public class TokenKwBrcClose : TokenKw { public TokenKwBrcClose (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwBrcClose (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "}"; } }
1314 public class TokenKwBrkOpen : TokenKw { public TokenKwBrkOpen (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwBrkOpen (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "["; } }
1315 public class TokenKwBrkClose : TokenKw { public TokenKwBrkClose (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwBrkClose (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "]"; } }
1316 public class TokenKwSemi : TokenKw { public TokenKwSemi (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwSemi (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return ";"; } }
1317 public class TokenKwColon : TokenKw { public TokenKwColon (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwColon (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return ":"; } }
1318 public class TokenKwCmpLT : TokenKw { public TokenKwCmpLT (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwCmpLT (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "<"; } }
1319 public class TokenKwCmpGT : TokenKw { public TokenKwCmpGT (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwCmpGT (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return ">"; } }
1320 public class TokenKwComma : TokenKw { public TokenKwComma (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwComma (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return ","; } }
1321 public class TokenKwDot : TokenKw { public TokenKwDot (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwDot (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "."; } }
1322 public class TokenKwQMark : TokenKw { public TokenKwQMark (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwQMark (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "?"; } }
1323 public class TokenKwDiv : TokenKw { public TokenKwDiv (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Div; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwDiv (Token original) : base (original) { binOpConst = TokenRValConstOps.Div; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "/"; } }
1324 public class TokenKwOr : TokenKw { public TokenKwOr (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Or; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public TokenKwOr (Token original) : base (original) { binOpConst = TokenRValConstOps.Or; unOpConst = TokenRValConstOps.Null; sdtClassOp = true; } public override string ToString () { return "|"; } }
1325 public class TokenKwHash : TokenKw { public TokenKwHash (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwHash (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "#"; } }
1326
1327 public class TokenKwAbstract : TokenKw { public TokenKwAbstract (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwAbstract (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "abstract"; } }
1328 public class TokenKwBase : TokenKw { public TokenKwBase (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwBase (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "base"; } }
1329 public class TokenKwBreak : TokenKw { public TokenKwBreak (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwBreak (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "break"; } }
1330 public class TokenKwCase : TokenKw { public TokenKwCase (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwCase (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "case"; } }
1331 public class TokenKwCatch : TokenKw { public TokenKwCatch (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwCatch (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "catch"; } }
1332 public class TokenKwClass : TokenKw { public TokenKwClass (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwClass (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "class"; } }
1333 public class TokenKwConst : TokenKw { public TokenKwConst (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwConst (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "constant"; } }
1334 public class TokenKwConstructor : TokenKw { public TokenKwConstructor (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwConstructor (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "constructor"; } }
1335 public class TokenKwCont : TokenKw { public TokenKwCont (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwCont (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "continue"; } }
1336 public class TokenKwDelegate : TokenKw { public TokenKwDelegate (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwDelegate (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "delegate"; } }
1337 public class TokenKwDefault : TokenKw { public TokenKwDefault (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwDefault (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "default"; } }
1338 public class TokenKwDestructor : TokenKw { public TokenKwDestructor (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwDestructor (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "destructor"; } }
1339 public class TokenKwDo : TokenKw { public TokenKwDo (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwDo (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "do"; } }
1340 public class TokenKwElse : TokenKw { public TokenKwElse (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwElse (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "else"; } }
1341 public class TokenKwFinal : TokenKw { public TokenKwFinal (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwFinal (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "final"; } }
1342 public class TokenKwFinally : TokenKw { public TokenKwFinally (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwFinally (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "finally"; } }
1343 public class TokenKwFor : TokenKw { public TokenKwFor (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwFor (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "for"; } }
1344 public class TokenKwForEach : TokenKw { public TokenKwForEach (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwForEach (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "foreach"; } }
1345 public class TokenKwGet : TokenKw { public TokenKwGet (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwGet (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "get"; } }
1346 public class TokenKwIf : TokenKw { public TokenKwIf (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwIf (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "if"; } }
1347 public class TokenKwIn : TokenKw { public TokenKwIn (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwIn (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "in"; } }
1348 public class TokenKwInterface : TokenKw { public TokenKwInterface (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwInterface (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "interface"; } }
1349 public class TokenKwIs : TokenKw { public TokenKwIs (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwIs (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "is"; } }
1350 public class TokenKwJump : TokenKw { public TokenKwJump (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwJump (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "jump"; } }
1351 public class TokenKwNew : TokenKw { public TokenKwNew (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwNew (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "new"; } }
1352 public class TokenKwOverride : TokenKw { public TokenKwOverride (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwOverride (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "override"; } }
1353 public class TokenKwPartial : TokenKw { public TokenKwPartial (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwPartial (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "partial"; } }
1354 public class TokenKwPrivate : TokenKw { public TokenKwPrivate (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwPrivate (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "private"; } }
1355 public class TokenKwProtected : TokenKw { public TokenKwProtected (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwProtected (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "protected"; } }
1356 public class TokenKwPublic : TokenKw { public TokenKwPublic (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwPublic (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "public"; } }
1357 public class TokenKwRet : TokenKw { public TokenKwRet (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwRet (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "return"; } }
1358 public class TokenKwSet : TokenKw { public TokenKwSet (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwSet (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "set"; } }
1359 public class TokenKwState : TokenKw { public TokenKwState (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwState (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "state"; } }
1360 public class TokenKwStatic : TokenKw { public TokenKwStatic (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwStatic (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "static"; } }
1361 public class TokenKwSwitch : TokenKw { public TokenKwSwitch (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwSwitch (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "switch"; } }
1362 public class TokenKwThis : TokenKw { public TokenKwThis (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwThis (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "this"; } }
1363 public class TokenKwThrow : TokenKw { public TokenKwThrow (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwThrow (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "throw"; } }
1364 public class TokenKwTry : TokenKw { public TokenKwTry (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwTry (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "try"; } }
1365 public class TokenKwTypedef : TokenKw { public TokenKwTypedef (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwTypedef (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "typedef"; } }
1366 public class TokenKwUndef : TokenKw { public TokenKwUndef (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwUndef (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "undef"; } }
1367 public class TokenKwVirtual : TokenKw { public TokenKwVirtual (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwVirtual (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "virtual"; } }
1368 public class TokenKwWhile : TokenKw { public TokenKwWhile (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public TokenKwWhile (Token original) : base (original) { binOpConst = TokenRValConstOps.Null; unOpConst = TokenRValConstOps.Null; sdtClassOp = false; } public override string ToString () { return "while"; } }
1369
1370 /**
1371 * @brief These static functions attempt to perform arithmetic on two constant
1372 * operands to generate the resultant constant.
1373 * Likewise for unary operators.
1374 *
1375 * @param left = left-hand value
1376 * @param right = right-hand value
1377 * @returns null: not able to perform computation
1378 * else: resultant value object
1379 *
1380 * Note: it is ok for these to throw any exception (such as overflow or div-by-zero),
1381 * and it will be treated as the 'not able to perform computation' case.
1382 */
1383 public class TokenRValConstOps {
1384 public static object Null (object left, object right) { return null; }
1385 public static object Div (object left, object right) { if ((left is int) && (right is int)) { return (int)left / (int)right; } if ((left is int) && (right is double)) { return (int)left / (double)right; } if ((left is double) && (right is int)) { return (double)left / (int)right; } if ((left is double) && (right is double)) { return (double)left / (double)right; } return null; }
1386 public static object Mod (object left, object right) { if ((left is int) && (right is int)) { return (int)left % (int)right; } if ((left is int) && (right is double)) { return (int)left % (double)right; } if ((left is double) && (right is int)) { return (double)left % (int)right; } if ((left is double) && (right is double)) { return (double)left % (double)right; } return null; }
1387 public static object Mul (object left, object right) { if ((left is int) && (right is int)) { return (int)left * (int)right; } if ((left is int) && (right is double)) { return (int)left * (double)right; } if ((left is double) && (right is int)) { return (double)left * (int)right; } if ((left is double) && (right is double)) { return (double)left * (double)right; } return null; }
1388 public static object And (object left, object right) { if ((left is int) && (right is int)) { return (int)left & (int)right; } if ((left is int) && (right is double)) { return (int)left & (int)(double)right; } if ((left is double) && (right is int)) { return (int)(double)left & (int)right; } if ((left is double) && (right is double)) { return (int)(double)left & (int)(double)right; } return null; }
1389 public static object LSh (object left, object right) { if ((left is int) && (right is int)) { return (int)left << (int)right; } if ((left is int) && (right is double)) { return (int)left << (int)(double)right; } if ((left is double) && (right is int)) { return (int)(double)left << (int)right; } if ((left is double) && (right is double)) { return (int)(double)left << (int)(double)right; } return null; }
1390 public static object Or (object left, object right) { if ((left is int) && (right is int)) { return (int)left | (int)right; } if ((left is int) && (right is double)) { return (int)left | (int)(double)right; } if ((left is double) && (right is int)) { return (int)(double)left | (int)right; } if ((left is double) && (right is double)) { return (int)(double)left | (int)(double)right; } return null; }
1391 public static object RSh (object left, object right) { if ((left is int) && (right is int)) { return (int)left >> (int)right; } if ((left is int) && (right is double)) { return (int)left >> (int)(double)right; } if ((left is double) && (right is int)) { return (int)(double)left >> (int)right; } if ((left is double) && (right is double)) { return (int)(double)left >> (int)(double)right; } return null; }
1392 public static object Xor (object left, object right) { if ((left is int) && (right is int)) { return (int)left ^ (int)right; } if ((left is int) && (right is double)) { return (int)left ^ (int)(double)right; } if ((left is double) && (right is int)) { return (int)(double)left ^ (int)right; } if ((left is double) && (right is double)) { return (int)(double)left ^ (int)(double)right; } return null; }
1393 public static object Add (object left, object right) { if ((left is char) && (right is int)) { return (char)((char)left + (int)right); } if ((left is double) && (right is double)) { return (double)left + (double)right; } if ((left is double) && (right is int)) { return (double)left + (int)right; } if ((left is double) && (right is string)) { return TypeCast.FloatToString((double)left) + (string)right; } if ((left is int) && (right is double)) { return (int)left + (double)right; } if ((left is int) && (right is int)) { return (int)left + (int)right; } if ((left is int) && (right is string)) { return TypeCast.IntegerToString((int)left) + (string)right; } if ((left is string) && (right is char)) { return (string)left + (char)right; } if ((left is string) && (right is double)) { return (string)left + TypeCast.FloatToString((double)right); } if ((left is string) && (right is int)) { return (string)left + TypeCast.IntegerToString ((int)right); } if ((left is string) && (right is string)) { return (string)left + (string)right; } return null; }
1394 public static object Sub (object left, object right) { if ((left is char) && (right is int)) { return (char)((char)left - (int)right); } if ((left is int) && (right is int)) { return (int)left - (int)right; } if ((left is int) && (right is double)) { return (int)left - (double)right; } if ((left is double) && (right is int)) { return (double)left - (int)right; } if ((left is double) && (right is double)) { return (double)left - (double)right; } return null; }
1395 public static object Null (object right) { return null; }
1396 public static object Neg (object right) { if (right is int) { return - (int)right; } if (right is double) { return - (double)right; } return null; }
1397 public static object Not (object right) { if (right is int) { return ~ (int)right; } return null; }
1398 }
1399
1400 /*
1401 * Various datatypes.
1402 */
1403 public abstract class TokenType : Token {
1404
1405 public TokenType (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1406 public TokenType (Token original) : base (original) { }
1407
1408 public static TokenType FromSysType (Token original, System.Type typ)
1409 {
1410 if (typ == typeof (LSL_List)) return new TokenTypeList (original);
1411 if (typ == typeof (LSL_Rotation)) return new TokenTypeRot (original);
1412 if (typ == typeof (void)) return new TokenTypeVoid (original);
1413 if (typ == typeof (LSL_Vector)) return new TokenTypeVec (original);
1414 if (typ == typeof (float)) return new TokenTypeFloat (original);
1415 if (typ == typeof (int)) return new TokenTypeInt (original);
1416 if (typ == typeof (string)) return new TokenTypeStr (original);
1417 if (typ == typeof (double)) return new TokenTypeFloat (original);
1418 if (typ == typeof (bool)) return new TokenTypeBool (original);
1419 if (typ == typeof (object)) return new TokenTypeObject (original);
1420 if (typ == typeof (XMR_Array)) return new TokenTypeArray (original);
1421 if (typ == typeof (LSL_Integer)) return new TokenTypeLSLInt (original);
1422 if (typ == typeof (LSL_Float)) return new TokenTypeLSLFloat (original);
1423 if (typ == typeof (LSL_String)) return new TokenTypeLSLString (original);
1424 if (typ == typeof (char)) return new TokenTypeChar (original);
1425 if (typ == typeof (Exception)) return new TokenTypeExc (original);
1426
1427 throw new Exception ("unknown script type " + typ.ToString ());
1428 }
1429
1430 public static TokenType FromLSLType (Token original, string typ)
1431 {
1432 if (typ == "list") return new TokenTypeList (original);
1433 if (typ == "rotation") return new TokenTypeRot (original);
1434 if (typ == "vector") return new TokenTypeVec (original);
1435 if (typ == "float") return new TokenTypeFloat (original);
1436 if (typ == "integer") return new TokenTypeInt (original);
1437 if (typ == "key") return new TokenTypeKey (original);
1438 if (typ == "string") return new TokenTypeStr (original);
1439 if (typ == "object") return new TokenTypeObject (original);
1440 if (typ == "array") return new TokenTypeArray (original);
1441 if (typ == "bool") return new TokenTypeBool (original);
1442 if (typ == "void") return new TokenTypeVoid (original);
1443 if (typ == "char") return new TokenTypeChar (original);
1444 if (typ == "exception") return new TokenTypeExc (original);
1445
1446 throw new Exception ("unknown type " + typ);
1447 }
1448
1449 /**
1450 * @brief Estimate the number of bytes of memory taken by one of these
1451 * objects. For objects with widely varying size, return the
1452 * smallest it can be.
1453 */
1454 public static int StaticSize (System.Type typ)
1455 {
1456 if (typ == typeof (LSL_List)) return 96;
1457 if (typ == typeof (LSL_Rotation)) return 80;
1458 if (typ == typeof (void)) return 0;
1459 if (typ == typeof (LSL_Vector)) return 72;
1460 if (typ == typeof (float)) return 8;
1461 if (typ == typeof (int)) return 8;
1462 if (typ == typeof (string)) return 40;
1463 if (typ == typeof (double)) return 8;
1464 if (typ == typeof (bool)) return 8;
1465 if (typ == typeof (XMR_Array)) return 96;
1466 if (typ == typeof (object)) return 32;
1467 if (typ == typeof (char)) return 2;
1468
1469 if (typ == typeof (LSL_Integer)) return 32;
1470 if (typ == typeof (LSL_Float)) return 32;
1471 if (typ == typeof (LSL_String)) return 40;
1472
1473 throw new Exception ("unknown type " + typ.ToString ());
1474 }
1475
1476 /**
1477 * @brief Return the corresponding system type.
1478 */
1479 public abstract Type ToSysType ();
1480
1481 /**
1482 * @brief Return the equivalent LSL wrapping type.
1483 *
1484 * null: normal
1485 * else: LSL-style wrapping, ie, LSL_Integer, LSL_Float, LSL_String
1486 * ToSysType()=System.Int32; lslWrapping=LSL_Integer
1487 * ToSysType()=System.Float; lslWrapping=LSL_Float
1488 * ToSysType()=System.String; lslWrapping=LSL_String
1489 */
1490 public virtual Type ToLSLWrapType ()
1491 {
1492 return null;
1493 }
1494
1495 /**
1496 * @brief Assign slots in either the global variable arrays or the script-defined type instance arrays.
1497 * These only need to be implemented for script-visible types, ie, those that a script writer
1498 * can actually define a variable as.
1499 */
1500 public virtual void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1501 {
1502 throw new Exception ("not implemented for " + ToString () + " (" + GetType () + ")");
1503 }
1504
1505 /**
1506 * @brief Get heap tracking type.
1507 */
1508 public virtual Type ToHeapTrackerType ()
1509 {
1510 return null;
1511 }
1512 public virtual ConstructorInfo GetHeapTrackerCtor ()
1513 {
1514 return null;
1515 }
1516 public virtual MethodInfo GetHeapTrackerPopMeth ()
1517 {
1518 return null;
1519 }
1520 public virtual MethodInfo GetHeapTrackerPushMeth ()
1521 {
1522 return null;
1523 }
1524 }
1525
1526 public class TokenTypeArray : TokenType {
1527 private static readonly FieldInfo iarArraysFieldInfo = typeof (XMRInstArrays).GetField ("iarArrays");
1528
1529 public TokenTypeArray (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1530 public TokenTypeArray (Token original) : base (original) { }
1531 public override Type ToSysType () { return typeof (XMR_Array); }
1532 public override string ToString () { return "array"; }
1533 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1534 {
1535 declVar.vTableArray = iarArraysFieldInfo;
1536 declVar.vTableIndex = arSizes.iasArrays ++;
1537 }
1538 }
1539 public class TokenTypeBool : TokenType {
1540 public TokenTypeBool (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1541 public TokenTypeBool (Token original) : base (original) { }
1542 public override Type ToSysType () { return typeof (bool); }
1543 public override string ToString () { return "bool"; }
1544 }
1545 public class TokenTypeChar : TokenType {
1546 private static readonly FieldInfo iarCharsFieldInfo = typeof (XMRInstArrays).GetField ("iarChars");
1547
1548 public TokenTypeChar (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1549 public TokenTypeChar (Token original) : base (original) { }
1550 public override Type ToSysType () { return typeof (char); }
1551 public override string ToString () { return "char"; }
1552 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1553 {
1554 declVar.vTableArray = iarCharsFieldInfo;
1555 declVar.vTableIndex = arSizes.iasChars ++;
1556 }
1557 }
1558 public class TokenTypeExc : TokenType {
1559 private static readonly FieldInfo iarObjectsFieldInfo = typeof (XMRInstArrays).GetField ("iarObjects");
1560
1561 public TokenTypeExc (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1562 public TokenTypeExc (Token original) : base (original) { }
1563 public override Type ToSysType () { return typeof (Exception); }
1564 public override string ToString () { return "exception"; }
1565 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1566 {
1567 declVar.vTableArray = iarObjectsFieldInfo;
1568 declVar.vTableIndex = arSizes.iasObjects ++;
1569 }
1570 }
1571 public class TokenTypeFloat : TokenType {
1572 private static readonly FieldInfo iarFloatsFieldInfo = typeof (XMRInstArrays).GetField ("iarFloats");
1573
1574 public TokenTypeFloat (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1575 public TokenTypeFloat (Token original) : base (original) { }
1576 public override Type ToSysType () { return typeof (double); }
1577 public override string ToString () { return "float"; }
1578 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1579 {
1580 declVar.vTableArray = iarFloatsFieldInfo;
1581 declVar.vTableIndex = arSizes.iasFloats ++;
1582 }
1583 }
1584 public class TokenTypeInt : TokenType {
1585 private static readonly FieldInfo iarIntegersFieldInfo = typeof (XMRInstArrays).GetField ("iarIntegers");
1586
1587 public TokenTypeInt (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1588 public TokenTypeInt (Token original) : base (original) { }
1589 public override Type ToSysType () { return typeof (int); }
1590 public override string ToString () { return "integer"; }
1591 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1592 {
1593 declVar.vTableArray = iarIntegersFieldInfo;
1594 declVar.vTableIndex = arSizes.iasIntegers ++;
1595 }
1596 }
1597 public class TokenTypeKey : TokenType {
1598 private static readonly FieldInfo iarStringsFieldInfo = typeof (XMRInstArrays).GetField ("iarStrings");
1599
1600 public TokenTypeKey (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1601 public TokenTypeKey (Token original) : base (original) { }
1602 public override Type ToSysType () { return typeof (string); }
1603 public override string ToString () { return "key"; }
1604 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1605 {
1606 declVar.vTableArray = iarStringsFieldInfo;
1607 declVar.vTableIndex = arSizes.iasStrings ++;
1608 }
1609 }
1610 public class TokenTypeList : TokenType {
1611 private static readonly FieldInfo iarListsFieldInfo = typeof (XMRInstArrays).GetField ("iarLists");
1612 private static readonly ConstructorInfo htListCtor = typeof (HeapTrackerList).GetConstructor (new Type [] { typeof (XMRInstAbstract) });
1613 private static readonly MethodInfo htListPopMeth = typeof (HeapTrackerList).GetMethod ("Pop", new Type[] { typeof (LSL_List) });
1614 private static readonly MethodInfo htListPushMeth = typeof (HeapTrackerList).GetMethod ("Push", new Type[0]);
1615
1616 public TokenTypeList (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1617 public TokenTypeList (Token original) : base (original) { }
1618 public override Type ToSysType () { return typeof (LSL_List); }
1619 public override string ToString () { return "list"; }
1620 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1621 {
1622 declVar.vTableArray = iarListsFieldInfo;
1623 declVar.vTableIndex = arSizes.iasLists ++;
1624 }
1625 public override Type ToHeapTrackerType () { return typeof (HeapTrackerList); }
1626 public override ConstructorInfo GetHeapTrackerCtor () { return htListCtor; }
1627 public override MethodInfo GetHeapTrackerPopMeth () { return htListPopMeth; }
1628 public override MethodInfo GetHeapTrackerPushMeth () { return htListPushMeth; }
1629 }
1630 public class TokenTypeObject : TokenType {
1631 private static readonly FieldInfo iarObjectsFieldInfo = typeof (XMRInstArrays).GetField ("iarObjects");
1632 private static readonly ConstructorInfo htObjectCtor = typeof (HeapTrackerObject).GetConstructor (new Type [] { typeof (XMRInstAbstract) });
1633 private static readonly MethodInfo htObjectPopMeth = typeof (HeapTrackerObject).GetMethod ("Pop", new Type[] { typeof (object) });
1634 private static readonly MethodInfo htObjectPushMeth = typeof (HeapTrackerObject).GetMethod ("Push", new Type[0]);
1635
1636 public TokenTypeObject (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1637 public TokenTypeObject (Token original) : base (original) { }
1638 public override Type ToSysType () { return typeof (object); }
1639 public override string ToString () { return "object"; }
1640 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1641 {
1642 declVar.vTableArray = iarObjectsFieldInfo;
1643 declVar.vTableIndex = arSizes.iasObjects ++;
1644 }
1645 public override Type ToHeapTrackerType () { return typeof (HeapTrackerObject); }
1646 public override ConstructorInfo GetHeapTrackerCtor () { return htObjectCtor; }
1647 public override MethodInfo GetHeapTrackerPopMeth () { return htObjectPopMeth; }
1648 public override MethodInfo GetHeapTrackerPushMeth () { return htObjectPushMeth; }
1649 }
1650 public class TokenTypeRot : TokenType {
1651 private static readonly FieldInfo iarRotationsFieldInfo = typeof (XMRInstArrays).GetField ("iarRotations");
1652
1653 public TokenTypeRot (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1654 public TokenTypeRot (Token original) : base (original) { }
1655 public override Type ToSysType () { return typeof (LSL_Rotation); }
1656 public override string ToString () { return "rotation"; }
1657 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1658 {
1659 declVar.vTableArray = iarRotationsFieldInfo;
1660 declVar.vTableIndex = arSizes.iasRotations ++;
1661 }
1662 }
1663 public class TokenTypeStr : TokenType {
1664 private static readonly FieldInfo iarStringsFieldInfo = typeof (XMRInstArrays).GetField ("iarStrings");
1665 private static readonly ConstructorInfo htStringCtor = typeof (HeapTrackerString).GetConstructor (new Type [] { typeof (XMRInstAbstract) });
1666 private static readonly MethodInfo htStringPopMeth = typeof (HeapTrackerString).GetMethod ("Pop", new Type[] { typeof (string) });
1667 private static readonly MethodInfo htStringPushMeth = typeof (HeapTrackerString).GetMethod ("Push", new Type[0]);
1668
1669 public TokenTypeStr (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1670 public TokenTypeStr (Token original) : base (original) { }
1671 public override Type ToSysType () { return typeof (string); }
1672 public override string ToString () { return "string"; }
1673 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1674 {
1675 declVar.vTableArray = iarStringsFieldInfo;
1676 declVar.vTableIndex = arSizes.iasStrings ++;
1677 }
1678 public override Type ToHeapTrackerType () { return typeof (HeapTrackerString); }
1679 public override ConstructorInfo GetHeapTrackerCtor () { return htStringCtor; }
1680 public override MethodInfo GetHeapTrackerPopMeth () { return htStringPopMeth; }
1681 public override MethodInfo GetHeapTrackerPushMeth () { return htStringPushMeth; }
1682 }
1683 public class TokenTypeUndef : TokenType { // for the 'undef' constant, ie, null object pointer
1684 public TokenTypeUndef (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1685 public TokenTypeUndef (Token original) : base (original) { }
1686 public override Type ToSysType () { return typeof (object); }
1687 public override string ToString () { return "undef"; }
1688 }
1689 public class TokenTypeVec : TokenType {
1690 private static readonly FieldInfo iarVectorsFieldInfo = typeof (XMRInstArrays).GetField ("iarVectors");
1691
1692 public TokenTypeVec (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1693 public TokenTypeVec (Token original) : base (original) { }
1694 public override Type ToSysType () { return typeof (LSL_Vector); }
1695 public override string ToString () { return "vector"; }
1696 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
1697 {
1698 declVar.vTableArray = iarVectorsFieldInfo;
1699 declVar.vTableIndex = arSizes.iasVectors ++;
1700 }
1701 }
1702 public class TokenTypeVoid : TokenType { // used only for function/method return types
1703 public TokenTypeVoid (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1704 public TokenTypeVoid (Token original) : base (original) { }
1705 public override Type ToSysType () { return typeof (void); }
1706 public override string ToString () { return "void"; }
1707 }
1708
1709 public class TokenTypeLSLFloat : TokenTypeFloat {
1710 public TokenTypeLSLFloat (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1711 public TokenTypeLSLFloat (Token original) : base (original) { }
1712 public override Type ToLSLWrapType () { return typeof (LSL_Float); }
1713 }
1714 public class TokenTypeLSLInt : TokenTypeInt {
1715 public TokenTypeLSLInt (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1716 public TokenTypeLSLInt (Token original) : base (original) { }
1717 public override Type ToLSLWrapType () { return typeof (LSL_Integer); }
1718 }
1719 public class TokenTypeLSLKey : TokenTypeKey {
1720 public TokenTypeLSLKey (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1721 public TokenTypeLSLKey (Token original) : base (original) { }
1722 public override Type ToLSLWrapType () { return typeof (LSL_Key); }
1723 }
1724 public class TokenTypeLSLString : TokenTypeStr {
1725 public TokenTypeLSLString (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
1726 public TokenTypeLSLString (Token original) : base (original) { }
1727 public override Type ToLSLWrapType () { return typeof (LSL_String); }
1728 }
1729}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTypeCast.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTypeCast.cs
new file mode 100644
index 0000000..c8be7bb
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTypeCast.cs
@@ -0,0 +1,819 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using OpenSim.Region.ScriptEngine.XMREngine;
30using System;
31using System.Collections.Generic;
32using System.Reflection;
33using System.Reflection.Emit;
34
35using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
36using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
37using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
38using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
39using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
40using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
41using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
42
43/**
44 * @brief Generate script object code to perform type casting
45 */
46
47namespace OpenSim.Region.ScriptEngine.XMREngine
48{
49
50 public class TypeCast {
51 private delegate void CastDelegate (IScriptCodeGen scg, Token errorAt);
52
53 private static ConstructorInfo floatConstructorStringInfo = typeof (LSL_Float).GetConstructor (new Type[] { typeof (string) });
54 private static ConstructorInfo integerConstructorStringInfo = typeof (LSL_Integer).GetConstructor (new Type[] { typeof (string) });
55 private static ConstructorInfo lslFloatConstructorInfo = typeof (LSL_Float).GetConstructor (new Type[] { typeof (double) });
56 private static ConstructorInfo lslIntegerConstructorInfo = typeof (LSL_Integer).GetConstructor (new Type[] { typeof (int) });
57 private static ConstructorInfo lslStringConstructorInfo = typeof (LSL_String).GetConstructor (new Type[] { typeof (string) });
58 private static ConstructorInfo rotationConstrucorStringInfo = typeof (LSL_Rotation).GetConstructor (new Type[] { typeof (string) });
59 private static ConstructorInfo vectorConstrucorStringInfo = typeof (LSL_Vector).GetConstructor (new Type[] { typeof (string) });
60 private static FieldInfo lslFloatValueFieldInfo = typeof (LSL_Float).GetField ("value");
61 private static FieldInfo lslIntegerValueFieldInfo = typeof (LSL_Integer).GetField ("value");
62 private static FieldInfo lslStringValueFieldInfo = typeof (LSL_String).GetField ("m_string");
63 private static FieldInfo sdtcITableFieldInfo = typeof (XMRSDTypeClObj).GetField ("sdtcITable");
64 private static MethodInfo boolToListMethodInfo = typeof (TypeCast).GetMethod ("BoolToList", new Type[] { typeof (bool) });
65 private static MethodInfo boolToStringMethodInfo = typeof (TypeCast).GetMethod ("BoolToString", new Type[] { typeof (bool) });
66 private static MethodInfo charToStringMethodInfo = typeof (TypeCast).GetMethod ("CharToString", new Type[] { typeof (char) });
67 private static MethodInfo excToStringMethodInfo = typeof (TypeCast).GetMethod ("ExceptionToString", new Type[] { typeof (Exception), typeof (XMRInstAbstract) });
68 private static MethodInfo floatToStringMethodInfo = typeof (TypeCast).GetMethod ("FloatToString", new Type[] { typeof (double) });
69 private static MethodInfo intToStringMethodInfo = typeof (TypeCast).GetMethod ("IntegerToString", new Type[] { typeof (int) });
70 private static MethodInfo keyToBoolMethodInfo = typeof (TypeCast).GetMethod ("KeyToBool", new Type[] { typeof (string) });
71 private static MethodInfo listToBoolMethodInfo = typeof (TypeCast).GetMethod ("ListToBool", new Type[] { typeof (LSL_List) });
72 private static MethodInfo listToStringMethodInfo = typeof (TypeCast).GetMethod ("ListToString", new Type[] { typeof (LSL_List) });
73 private static MethodInfo objectToFloatMethodInfo = typeof (TypeCast).GetMethod ("ObjectToFloat", new Type[] { typeof (object) });
74 private static MethodInfo objectToIntegerMethodInfo = typeof (TypeCast).GetMethod ("ObjectToInteger", new Type[] { typeof (object) });
75 private static MethodInfo objectToListMethodInfo = typeof (TypeCast).GetMethod ("ObjectToList", new Type[] { typeof (object) });
76 private static MethodInfo objectToRotationMethodInfo = typeof (TypeCast).GetMethod ("ObjectToRotation", new Type[] { typeof (object) });
77 private static MethodInfo objectToStringMethodInfo = typeof (TypeCast).GetMethod ("ObjectToString", new Type[] { typeof (object) });
78 private static MethodInfo objectToVectorMethodInfo = typeof (TypeCast).GetMethod ("ObjectToVector", new Type[] { typeof (object) });
79 private static MethodInfo rotationToBoolMethodInfo = typeof (TypeCast).GetMethod ("RotationToBool", new Type[] { typeof (LSL_Rotation) });
80 private static MethodInfo rotationToStringMethodInfo = typeof (TypeCast).GetMethod ("RotationToString", new Type[] { typeof (LSL_Rotation) });
81 private static MethodInfo stringToBoolMethodInfo = typeof (TypeCast).GetMethod ("StringToBool", new Type[] { typeof (string) });
82 private static MethodInfo vectorToBoolMethodInfo = typeof (TypeCast).GetMethod ("VectorToBool", new Type[] { typeof (LSL_Vector) });
83 private static MethodInfo vectorToStringMethodInfo = typeof (TypeCast).GetMethod ("VectorToString", new Type[] { typeof (LSL_Vector) });
84 private static MethodInfo sdTypeClassCastClass2ClassMethodInfo = typeof (XMRSDTypeClObj).GetMethod ("CastClass2Class", new Type[] { typeof (object), typeof (int) });
85 private static MethodInfo sdTypeClassCastIFace2ClassMethodInfo = typeof (XMRSDTypeClObj).GetMethod ("CastIFace2Class", new Type[] { typeof (Delegate[]), typeof (int) });
86 private static MethodInfo sdTypeClassCastObj2IFaceMethodInfo = typeof (XMRSDTypeClObj).GetMethod ("CastObj2IFace", new Type[] { typeof (object), typeof (string) });
87 private static MethodInfo charToListMethodInfo = typeof (TypeCast).GetMethod ("CharToList", new Type[] { typeof (char) });
88 private static MethodInfo excToListMethodInfo = typeof (TypeCast).GetMethod ("ExcToList", new Type[] { typeof (Exception) });
89 private static MethodInfo vectorToListMethodInfo = typeof (TypeCast).GetMethod ("VectorToList", new Type[] { typeof (LSL_Vector) });
90 private static MethodInfo floatToListMethodInfo = typeof (TypeCast).GetMethod ("FloatToList", new Type[] { typeof (double) });
91 private static MethodInfo integerToListMethodInfo = typeof (TypeCast).GetMethod ("IntegerToList", new Type[] { typeof (int) });
92 private static MethodInfo rotationToListMethodInfo = typeof (TypeCast).GetMethod ("RotationToList", new Type[] { typeof (LSL_Rotation) });
93 private static MethodInfo stringToListMethodInfo = typeof (TypeCast).GetMethod ("StringToList", new Type[] { typeof (string) });
94
95 /*
96 * List of all allowed type casts and how to perform the casting.
97 */
98 private static Dictionary<string, CastDelegate> legalTypeCasts = CreateLegalTypeCasts ();
99
100 /**
101 * @brief create a dictionary of legal type casts.
102 * Defines what EXPLICIT type casts are allowed in addition to the IMPLICIT ones.
103 * Key is of the form <oldtype> <newtype> for IMPLICIT casting.
104 * Key is of the form <oldtype>*<newtype> for EXPLICIT casting.
105 * Value is a delegate that generates code to perform the type cast.
106 */
107 private static Dictionary<string, CastDelegate> CreateLegalTypeCasts ()
108 {
109 Dictionary<string, CastDelegate> ltc = new Dictionary<string, CastDelegate> ();
110
111 // IMPLICIT type casts (a space is in middle of the key)
112 // EXPLICIT type casts (an * is in middle of the key)
113 // In general, only mark explicit if it might throw an exception
114 ltc.Add ("array object", TypeCastArray2Object);
115 ltc.Add ("bool float", TypeCastBool2Float);
116 ltc.Add ("bool integer", TypeCastBool2Integer);
117 ltc.Add ("bool list", TypeCastBool2List);
118 ltc.Add ("bool object", TypeCastBool2Object);
119 ltc.Add ("bool string", TypeCastBool2String);
120 ltc.Add ("char integer", TypeCastChar2Integer);
121 ltc.Add ("char list", TypeCastChar2List);
122 ltc.Add ("char object", TypeCastChar2Object);
123 ltc.Add ("char string", TypeCastChar2String);
124 ltc.Add ("exception list", TypeCastExc2List);
125 ltc.Add ("exception object", TypeCastExc2Object);
126 ltc.Add ("exception string", TypeCastExc2String);
127 ltc.Add ("float bool", TypeCastFloat2Bool);
128 ltc.Add ("float integer", TypeCastFloat2Integer);
129 ltc.Add ("float list", TypeCastFloat2List);
130 ltc.Add ("float object", TypeCastFloat2Object);
131 ltc.Add ("float string", TypeCastFloat2String);
132 ltc.Add ("integer bool", TypeCastInteger2Bool);
133 ltc.Add ("integer char", TypeCastInteger2Char);
134 ltc.Add ("integer float", TypeCastInteger2Float);
135 ltc.Add ("integer list", TypeCastInteger2List);
136 ltc.Add ("integer object", TypeCastInteger2Object);
137 ltc.Add ("integer string", TypeCastInteger2String);
138 ltc.Add ("list bool", TypeCastList2Bool);
139 ltc.Add ("list object", TypeCastList2Object);
140 ltc.Add ("list string", TypeCastList2String);
141 ltc.Add ("object*array", TypeCastObject2Array);
142 ltc.Add ("object*bool", TypeCastObject2Bool);
143 ltc.Add ("object*char", TypeCastObject2Char);
144 ltc.Add ("object*exception", TypeCastObject2Exc);
145 ltc.Add ("object*float", TypeCastObject2Float);
146 ltc.Add ("object*integer", TypeCastObject2Integer);
147 ltc.Add ("object*list", TypeCastObject2List);
148 ltc.Add ("object*rotation", TypeCastObject2Rotation);
149 ltc.Add ("object string", TypeCastObject2String);
150 ltc.Add ("object*vector", TypeCastObject2Vector);
151 ltc.Add ("rotation bool", TypeCastRotation2Bool);
152 ltc.Add ("rotation list", TypeCastRotation2List);
153 ltc.Add ("rotation object", TypeCastRotation2Object);
154 ltc.Add ("rotation string", TypeCastRotation2String);
155 ltc.Add ("string bool", TypeCastString2Bool);
156 ltc.Add ("string float", TypeCastString2Float);
157 ltc.Add ("string integer", TypeCastString2Integer);
158 ltc.Add ("string list", TypeCastString2List);
159 ltc.Add ("string object", TypeCastString2Object);
160 ltc.Add ("string rotation", TypeCastString2Rotation);
161 ltc.Add ("string vector", TypeCastString2Vector);
162 ltc.Add ("vector bool", TypeCastVector2Bool);
163 ltc.Add ("vector list", TypeCastVector2List);
164 ltc.Add ("vector object", TypeCastVector2Object);
165 ltc.Add ("vector string", TypeCastVector2String);
166
167 return ltc;
168 }
169
170 /**
171 * @brief See if the given type can be cast to the other implicitly.
172 * @param dstType = type being cast to
173 * @param srcType = type being cast from
174 * @returns false: implicit cast not allowed
175 * true: implicit cast allowed
176 */
177 public static bool IsAssignableFrom (TokenType dstType, TokenType srcType)
178 {
179 /*
180 * Do a 'dry run' of the casting operation, discarding any emits and not printing any errors.
181 * But if the casting tries to print error(s), return false.
182 * Otherwise assume the cast is allowed and return true.
183 */
184 SCGIAF scg = new SCGIAF ();
185 scg.ok = true;
186 scg._ilGen = migiaf;
187 CastTopOfStack (scg, null, srcType, dstType, false);
188 return scg.ok;
189 }
190
191 private struct SCGIAF : IScriptCodeGen {
192 public bool ok;
193 public ScriptMyILGen _ilGen;
194
195 // IScriptCodeGen
196 public ScriptMyILGen ilGen { get { return _ilGen; } }
197 public void ErrorMsg (Token token, string message) { ok = false; }
198 public void PushDefaultValue (TokenType type) { }
199 public void PushXMRInst () { }
200 }
201
202 private static readonly MIGIAF migiaf = new MIGIAF ();
203 private struct MIGIAF : ScriptMyILGen {
204 // ScriptMyILGen
205 public string methName { get { return null; } }
206 public ScriptMyLocal DeclareLocal (Type type, string name) { return null; }
207 public ScriptMyLabel DefineLabel (string name) { return null; }
208 public void BeginExceptionBlock () { }
209 public void BeginCatchBlock (Type excType) { }
210 public void BeginFinallyBlock () { }
211 public void EndExceptionBlock () { }
212 public void Emit (Token errorAt, OpCode opcode) { }
213 public void Emit (Token errorAt, OpCode opcode, FieldInfo field) { }
214 public void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal) { }
215 public void Emit (Token errorAt, OpCode opcode, Type type) { }
216 public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel) { }
217 public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels) { }
218 public void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method) { }
219 public void Emit (Token errorAt, OpCode opcode, MethodInfo method) { }
220 public void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor) { }
221 public void Emit (Token errorAt, OpCode opcode, double value) { }
222 public void Emit (Token errorAt, OpCode opcode, float value) { }
223 public void Emit (Token errorAt, OpCode opcode, int value) { }
224 public void Emit (Token errorAt, OpCode opcode, string value) { }
225 public void MarkLabel (ScriptMyLabel myLabel) { }
226 }
227
228 /**
229 * @brief Emit code that converts the top stack item from 'oldType' to 'newType'
230 * @param scg = what script we are compiling
231 * @param errorAt = token used for source location for error messages
232 * @param oldType = type of item currently on the stack
233 * @param newType = type to convert it to
234 * @param explicitAllowed = false: only consider implicit casts
235 * true: consider both implicit and explicit casts
236 * @returns with code emitted for conversion (or error message output if not allowed, and stack left unchanged)
237 */
238 public static void CastTopOfStack (IScriptCodeGen scg, Token errorAt, TokenType oldType, TokenType newType, bool explicitAllowed)
239 {
240 CastDelegate castDelegate;
241 string oldString = oldType.ToString ();
242 string newString = newType.ToString ();
243
244 /*
245 * 'key' -> 'bool' is the only time we care about key being different than string.
246 */
247 if ((oldString == "key") && (newString == "bool")) {
248 LSLUnwrap (scg, errorAt, oldType);
249 scg.ilGen.Emit (errorAt, OpCodes.Call, keyToBoolMethodInfo);
250 LSLWrap (scg, errorAt, newType);
251 return;
252 }
253
254 /*
255 * Treat key and string as same type for all other type casts.
256 */
257 if (oldString == "key") oldString = "string";
258 if (newString == "key") newString = "string";
259
260 /*
261 * If the types are the same, there is no conceptual casting needed.
262 * However, there may be wraping/unwraping to/from the LSL wrappers.
263 */
264 if (oldString == newString) {
265 if (oldType.ToLSLWrapType () != newType.ToLSLWrapType ()) {
266 LSLUnwrap (scg, errorAt, oldType);
267 LSLWrap (scg, errorAt, newType);
268 }
269 return;
270 }
271
272 /*
273 * Script-defined classes can be cast up and down the tree.
274 */
275 if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeSDTypeClass)) {
276 TokenDeclSDTypeClass oldSDTC = ((TokenTypeSDTypeClass)oldType).decl;
277 TokenDeclSDTypeClass newSDTC = ((TokenTypeSDTypeClass)newType).decl;
278
279 // implicit cast allowed from leaf toward root
280 for (TokenDeclSDTypeClass sdtc = oldSDTC; sdtc != null; sdtc = sdtc.extends) {
281 if (sdtc == newSDTC) return;
282 }
283
284 // explicit cast allowed from root toward leaf
285 for (TokenDeclSDTypeClass sdtc = newSDTC; sdtc != null; sdtc = sdtc.extends) {
286 if (sdtc == oldSDTC) {
287 ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
288 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, newSDTC.sdTypeIndex);
289 scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastClass2ClassMethodInfo);
290 return;
291 }
292 }
293
294 // not on same branch
295 goto illcast;
296 }
297
298 /*
299 * One script-defined interface type cannot be cast to another script-defined interface type,
300 * unless the old interface declares that it implements the new interface. That proves that
301 * the underlying object, no matter what type, implements the new interface.
302 */
303 if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeSDTypeInterface)) {
304 TokenDeclSDTypeInterface oldDecl = ((TokenTypeSDTypeInterface)oldType).decl;
305 TokenDeclSDTypeInterface newDecl = ((TokenTypeSDTypeInterface)newType).decl;
306 if (!oldDecl.Implements (newDecl)) goto illcast;
307 scg.ilGen.Emit (errorAt, OpCodes.Ldstr, newType.ToString ());
308 scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastObj2IFaceMethodInfo);
309 return;
310 }
311
312 /*
313 * A script-defined class type can be implicitly cast to a script-defined interface type that it
314 * implements. The result is an array of delegates that give the class's implementation of the
315 * various methods defined by the interface.
316 */
317 if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeSDTypeInterface)) {
318 TokenDeclSDTypeClass oldSDTC = ((TokenTypeSDTypeClass)oldType).decl;
319 int intfIndex;
320 if (!oldSDTC.intfIndices.TryGetValue (newType.ToString (), out intfIndex)) goto illcast;
321 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, sdtcITableFieldInfo);
322 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, intfIndex);
323 scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (Delegate[]));
324 return;
325 }
326
327 /*
328 * A script-defined interface type can be explicitly cast to a script-defined class type by
329 * extracting the Target property from element 0 of the delegate array that is the interface
330 * object and making sure it casts to the correct script-defined class type.
331 *
332 * But then only if the class type implements the interface type.
333 */
334 if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeSDTypeClass)) {
335 TokenTypeSDTypeInterface oldSDTI = (TokenTypeSDTypeInterface)oldType;
336 TokenTypeSDTypeClass newSDTC = (TokenTypeSDTypeClass) newType;
337
338 if (!newSDTC.decl.CanCastToIntf (oldSDTI.decl)) goto illcast;
339
340 ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
341 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, newSDTC.decl.sdTypeIndex);
342 scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastIFace2ClassMethodInfo);
343 return;
344 }
345
346 /*
347 * A script-defined interface type can be implicitly cast to object.
348 */
349 if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeObject)) {
350 return;
351 }
352
353 /*
354 * An object can be explicitly cast to a script-defined interface.
355 */
356 if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeInterface)) {
357 ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
358 scg.ilGen.Emit (errorAt, OpCodes.Ldstr, newString);
359 scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastObj2IFaceMethodInfo);
360 return;
361 }
362
363 /*
364 * Cast to void is always allowed, such as discarding value from 'i++' or function return value.
365 */
366 if (newType is TokenTypeVoid) {
367 scg.ilGen.Emit (errorAt, OpCodes.Pop);
368 return;
369 }
370
371 /*
372 * Cast from undef to object or script-defined type is always allowed.
373 */
374 if ((oldType is TokenTypeUndef) &&
375 ((newType is TokenTypeObject) ||
376 (newType is TokenTypeSDTypeClass) ||
377 (newType is TokenTypeSDTypeInterface))) {
378 return;
379 }
380
381 /*
382 * Script-defined classes can be implicitly cast to objects.
383 */
384 if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeObject)) {
385 return;
386 }
387
388 /*
389 * Script-defined classes can be explicitly cast from objects and other script-defined classes.
390 * Note that we must manually check that it is the correct SDTypeClass however because as far as
391 * mono is concerned, all SDTypeClass's are the same.
392 */
393 if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeClass)) {
394 ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
395 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, ((TokenTypeSDTypeClass)newType).decl.sdTypeIndex);
396 scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastClass2ClassMethodInfo);
397 return;
398 }
399
400 /*
401 * Delegates can be implicitly cast to/from objects.
402 */
403 if ((oldType is TokenTypeSDTypeDelegate) && (newType is TokenTypeObject)) {
404 return;
405 }
406 if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeDelegate)) {
407 scg.ilGen.Emit (errorAt, OpCodes.Castclass, newType.ToSysType ());
408 return;
409 }
410
411 /*
412 * Some actual conversion is needed, see if it is in table of legal casts.
413 */
414 string key = oldString + " " + newString;
415 if (!legalTypeCasts.TryGetValue (key, out castDelegate)) {
416 key = oldString + "*" + newString;
417 if (!legalTypeCasts.TryGetValue (key, out castDelegate)) goto illcast;
418 ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
419 }
420
421 /*
422 * Ok, output cast. But make sure it is in native form without any LSL wrapping
423 * before passing to our casting routine. Then if caller is expecting an LSL-
424 * wrapped value on the stack upon return, wrap it up after our casting.
425 */
426 LSLUnwrap (scg, errorAt, oldType);
427 castDelegate (scg, errorAt);
428 LSLWrap (scg, errorAt, newType);
429 return;
430
431 illcast:
432 scg.ErrorMsg (errorAt, "illegal to cast from " + oldString + " to " + newString);
433 if (!(oldType is TokenTypeVoid)) scg.ilGen.Emit (errorAt, OpCodes.Pop);
434 scg.PushDefaultValue (newType);
435 }
436 private static void ExplCheck (IScriptCodeGen scg, Token errorAt, bool explicitAllowed, string oldString, string newString)
437 {
438 if (!explicitAllowed) {
439 scg.ErrorMsg (errorAt, "must explicitly cast from " + oldString + " to " + newString);
440 }
441 }
442
443 /**
444 * @brief If value on the stack is an LSL-style wrapped value, unwrap it.
445 */
446 public static void LSLUnwrap (IScriptCodeGen scg, Token errorAt, TokenType type)
447 {
448 if (type.ToLSLWrapType () == typeof (LSL_Float)) {
449 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslFloatValueFieldInfo);
450 }
451 if (type.ToLSLWrapType () == typeof (LSL_Integer)) {
452 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslIntegerValueFieldInfo);
453 }
454 if (type.ToLSLWrapType () == typeof (LSL_String)) {
455 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslStringValueFieldInfo);
456 }
457 }
458
459 /**
460 * @brief If caller wants the unwrapped value on stack wrapped LSL-style, wrap it.
461 */
462 private static void LSLWrap (IScriptCodeGen scg, Token errorAt, TokenType type)
463 {
464 if (type.ToLSLWrapType () == typeof (LSL_Float)) {
465 scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslFloatConstructorInfo);
466 }
467 if (type.ToLSLWrapType () == typeof (LSL_Integer)) {
468 scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslIntegerConstructorInfo);
469 }
470 if (type.ToLSLWrapType () == typeof (LSL_String)) {
471 scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslStringConstructorInfo);
472 }
473 }
474
475 /**
476 * @brief These routines output code to perform casting.
477 * They can assume there are no LSL wrapped values on input
478 * and they should not output an LSL wrapped value.
479 */
480 private static void TypeCastArray2Object (IScriptCodeGen scg, Token errorAt)
481 {
482 }
483 private static void TypeCastBool2Float (IScriptCodeGen scg, Token errorAt)
484 {
485 if (typeof (double) == typeof (float)) {
486 scg.ilGen.Emit (errorAt, OpCodes.Conv_R4);
487 } else if (typeof (double) == typeof (double)) {
488 scg.ilGen.Emit (errorAt, OpCodes.Conv_R8);
489 } else {
490 throw new Exception ("unknown type");
491 }
492 }
493 private static void TypeCastBool2Integer (IScriptCodeGen scg, Token errorAt)
494 {
495 }
496 private static void TypeCastBool2Object (IScriptCodeGen scg, Token errorAt)
497 {
498 scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (bool));
499 }
500 private static void TypeCastChar2Integer (IScriptCodeGen scg, Token errorAt)
501 {
502 }
503 private static void TypeCastChar2List (IScriptCodeGen scg, Token errorAt)
504 {
505 scg.ilGen.Emit (errorAt, OpCodes.Call, charToListMethodInfo);
506 }
507 private static void TypeCastChar2Object (IScriptCodeGen scg, Token errorAt)
508 {
509 scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (char));
510 }
511 private static void TypeCastChar2String (IScriptCodeGen scg, Token errorAt)
512 {
513 scg.ilGen.Emit (errorAt, OpCodes.Call, charToStringMethodInfo);
514 }
515 private static void TypeCastExc2List (IScriptCodeGen scg, Token errorAt)
516 {
517 scg.ilGen.Emit (errorAt, OpCodes.Call, excToListMethodInfo);
518 }
519 private static void TypeCastExc2Object (IScriptCodeGen scg, Token errorAt)
520 {
521 }
522 private static void TypeCastExc2String (IScriptCodeGen scg, Token errorAt)
523 {
524 scg.PushXMRInst ();
525 scg.ilGen.Emit (errorAt, OpCodes.Call, excToStringMethodInfo);
526 }
527 private static void TypeCastFloat2Bool (IScriptCodeGen scg, Token errorAt)
528 {
529 scg.ilGen.Emit (errorAt, OpCodes.Ldc_R4, 0.0f);
530 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
531 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
532 scg.ilGen.Emit (errorAt, OpCodes.Xor);
533 }
534 private static void TypeCastFloat2Integer (IScriptCodeGen scg, Token errorAt)
535 {
536 scg.ilGen.Emit (errorAt, OpCodes.Conv_I4);
537 }
538 private static void TypeCastFloat2Object (IScriptCodeGen scg, Token errorAt)
539 {
540 scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (double));
541 }
542 private static void TypeCastInteger2Bool (IScriptCodeGen scg, Token errorAt)
543 {
544 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
545 scg.ilGen.Emit (errorAt, OpCodes.Ceq);
546 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
547 scg.ilGen.Emit (errorAt, OpCodes.Xor);
548 }
549 private static void TypeCastInteger2Char (IScriptCodeGen scg, Token errorAt)
550 {
551 }
552 private static void TypeCastInteger2Float (IScriptCodeGen scg, Token errorAt)
553 {
554 if (typeof (double) == typeof (float)) {
555 scg.ilGen.Emit (errorAt, OpCodes.Conv_R4);
556 } else if (typeof (double) == typeof (double)) {
557 scg.ilGen.Emit (errorAt, OpCodes.Conv_R8);
558 } else {
559 throw new Exception ("unknown type");
560 }
561 }
562 private static void TypeCastInteger2Object (IScriptCodeGen scg, Token errorAt)
563 {
564 scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (int));
565 }
566 private static void TypeCastList2Bool (IScriptCodeGen scg, Token errorAt)
567 {
568 scg.ilGen.Emit (errorAt, OpCodes.Call, listToBoolMethodInfo);
569 }
570 private static void TypeCastList2Object (IScriptCodeGen scg, Token errorAt)
571 {
572 if (typeof (LSL_List).IsValueType) {
573 scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (LSL_List));
574 }
575 }
576 private static void TypeCastObject2Array (IScriptCodeGen scg, Token errorAt)
577 {
578 scg.ilGen.Emit (errorAt, OpCodes.Castclass, typeof (XMR_Array));
579 }
580 private static void TypeCastObject2Bool (IScriptCodeGen scg, Token errorAt)
581 {
582 scg.ilGen.Emit (errorAt, OpCodes.Unbox_Any, typeof (bool));
583 }
584 private static void TypeCastObject2Char (IScriptCodeGen scg, Token errorAt)
585 {
586 scg.ilGen.Emit (errorAt, OpCodes.Unbox_Any, typeof (char));
587 }
588 private static void TypeCastObject2Exc (IScriptCodeGen scg, Token errorAt)
589 {
590 scg.ilGen.Emit (errorAt, OpCodes.Castclass, typeof (Exception));
591 }
592 private static void TypeCastObject2Float (IScriptCodeGen scg, Token errorAt)
593 {
594 scg.ilGen.Emit (errorAt, OpCodes.Call, objectToFloatMethodInfo);
595 }
596 private static void TypeCastObject2Integer (IScriptCodeGen scg, Token errorAt)
597 {
598 scg.ilGen.Emit (errorAt, OpCodes.Call, objectToIntegerMethodInfo);
599 }
600 private static void TypeCastObject2List (IScriptCodeGen scg, Token errorAt)
601 {
602 if (typeof (LSL_List).IsValueType) {
603 scg.ilGen.Emit (errorAt, OpCodes.Call, objectToListMethodInfo);
604 } else {
605 scg.ilGen.Emit (errorAt, OpCodes.Castclass, typeof (LSL_List));
606 }
607 }
608 private static void TypeCastObject2Rotation (IScriptCodeGen scg, Token errorAt)
609 {
610 scg.ilGen.Emit (errorAt, OpCodes.Call, objectToRotationMethodInfo);
611 }
612 private static void TypeCastObject2Vector (IScriptCodeGen scg, Token errorAt)
613 {
614 scg.ilGen.Emit (errorAt, OpCodes.Call, objectToVectorMethodInfo);
615 }
616 private static void TypeCastRotation2Bool (IScriptCodeGen scg, Token errorAt)
617 {
618 scg.ilGen.Emit (errorAt, OpCodes.Call, rotationToBoolMethodInfo);
619 }
620 private static void TypeCastRotation2Object (IScriptCodeGen scg, Token errorAt)
621 {
622 scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (LSL_Rotation));
623 }
624 private static void TypeCastString2Bool (IScriptCodeGen scg, Token errorAt)
625 {
626 scg.ilGen.Emit (errorAt, OpCodes.Call, stringToBoolMethodInfo);
627 }
628 private static void TypeCastString2Object (IScriptCodeGen scg, Token errorAt)
629 {
630 }
631 private static void TypeCastString2Rotation (IScriptCodeGen scg, Token errorAt)
632 {
633 scg.ilGen.Emit (errorAt, OpCodes.Newobj, rotationConstrucorStringInfo);
634 }
635 private static void TypeCastString2Vector (IScriptCodeGen scg, Token errorAt)
636 {
637 scg.ilGen.Emit (errorAt, OpCodes.Newobj, vectorConstrucorStringInfo);
638 }
639 private static void TypeCastVector2Bool (IScriptCodeGen scg, Token errorAt)
640 {
641 scg.ilGen.Emit (errorAt, OpCodes.Call, vectorToBoolMethodInfo);
642 }
643 private static void TypeCastVector2List (IScriptCodeGen scg, Token errorAt)
644 {
645 scg.ilGen.Emit (errorAt, OpCodes.Call, vectorToListMethodInfo);
646 }
647 private static void TypeCastVector2Object (IScriptCodeGen scg, Token errorAt)
648 {
649 scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (LSL_Vector));
650 }
651 private static void TypeCastBool2List (IScriptCodeGen scg, Token errorAt)
652 {
653 scg.ilGen.Emit (errorAt, OpCodes.Call, boolToListMethodInfo);
654 }
655 private static void TypeCastBool2String (IScriptCodeGen scg, Token errorAt)
656 {
657 scg.ilGen.Emit (errorAt, OpCodes.Call, boolToStringMethodInfo);
658 }
659 private static void TypeCastFloat2List (IScriptCodeGen scg, Token errorAt)
660 {
661 scg.ilGen.Emit (errorAt, OpCodes.Call, floatToListMethodInfo);
662 }
663 private static void TypeCastFloat2String (IScriptCodeGen scg, Token errorAt)
664 {
665 scg.ilGen.Emit (errorAt, OpCodes.Call, floatToStringMethodInfo);
666 }
667 private static void TypeCastInteger2List (IScriptCodeGen scg, Token errorAt)
668 {
669 scg.ilGen.Emit (errorAt, OpCodes.Call, integerToListMethodInfo);
670 }
671 private static void TypeCastInteger2String (IScriptCodeGen scg, Token errorAt)
672 {
673 scg.ilGen.Emit (errorAt, OpCodes.Call, intToStringMethodInfo);
674 }
675 private static void TypeCastList2String (IScriptCodeGen scg, Token errorAt)
676 {
677 scg.ilGen.Emit (errorAt, OpCodes.Call, listToStringMethodInfo);
678 }
679 private static void TypeCastObject2String (IScriptCodeGen scg, Token errorAt)
680 {
681 scg.ilGen.Emit (errorAt, OpCodes.Call, objectToStringMethodInfo);
682 }
683 private static void TypeCastRotation2List (IScriptCodeGen scg, Token errorAt)
684 {
685 scg.ilGen.Emit (errorAt, OpCodes.Call, rotationToListMethodInfo);
686 }
687 private static void TypeCastRotation2String (IScriptCodeGen scg, Token errorAt)
688 {
689 scg.ilGen.Emit (errorAt, OpCodes.Call, rotationToStringMethodInfo);
690 }
691 private static void TypeCastString2Float (IScriptCodeGen scg, Token errorAt)
692 {
693 scg.ilGen.Emit (errorAt, OpCodes.Newobj, floatConstructorStringInfo);
694 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslFloatValueFieldInfo);
695 }
696 private static void TypeCastString2Integer (IScriptCodeGen scg, Token errorAt)
697 {
698 scg.ilGen.Emit (errorAt, OpCodes.Newobj, integerConstructorStringInfo);
699 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslIntegerValueFieldInfo);
700 }
701 private static void TypeCastString2List (IScriptCodeGen scg, Token errorAt)
702 {
703 scg.ilGen.Emit (errorAt, OpCodes.Call, stringToListMethodInfo);
704 }
705 private static void TypeCastVector2String (IScriptCodeGen scg, Token errorAt)
706 {
707 scg.ilGen.Emit (errorAt, OpCodes.Call, vectorToStringMethodInfo);
708 }
709
710 /*
711 * Because the calls are funky, let the compiler handle them.
712 */
713 public static bool RotationToBool (LSL_Rotation x) { return !x.Equals (ScriptBaseClass.ZERO_ROTATION); }
714 public static bool StringToBool (string x) { return x.Length > 0; }
715 public static bool VectorToBool (LSL_Vector x) { return !x.Equals (ScriptBaseClass.ZERO_VECTOR); }
716 public static string BoolToString (bool x) { return x ? "1" : "0"; }
717 public static string CharToString (char x) { return x.ToString (); }
718 public static string FloatToString (double x) { return x.ToString ("0.000000"); }
719 public static string IntegerToString (int x) { return x.ToString (); }
720 public static bool KeyToBool (string x) { return (x != "") && (x != ScriptBaseClass.NULL_KEY); }
721 public static bool ListToBool (LSL_List x) { return x.Length != 0; }
722 public static string ListToString (LSL_List x) { return x.ToString (); }
723 public static string ObjectToString (object x) { return (x == null) ? null : x.ToString (); }
724 public static string RotationToString (LSL_Rotation x) { return x.ToString (); }
725 public static string VectorToString (LSL_Vector x) { return x.ToString (); }
726 public static LSL_List BoolToList (bool b) { return new LSL_List (new object[] { new LSL_Integer (b ? 1 : 0) }); }
727 public static LSL_List CharToList (char c) { return new LSL_List (new object[] { new LSL_Integer (c) }); }
728 public static LSL_List ExcToList (Exception e) { return new LSL_List (new object[] { e }); }
729 public static LSL_List VectorToList (LSL_Vector v) { return new LSL_List (new object[] { v }); }
730 public static LSL_List FloatToList (double f) { return new LSL_List (new object[] { new LSL_Float (f) }); }
731 public static LSL_List IntegerToList (int i) { return new LSL_List (new object[] { new LSL_Integer (i) }); }
732 public static LSL_List RotationToList (LSL_Rotation r) { return new LSL_List (new object[] { r }); }
733 public static LSL_List StringToList (string s) { return new LSL_List (new object[] { new LSL_String (s) }); }
734
735 public static double ObjectToFloat (object x)
736 {
737 if (x is LSL_String) return double.Parse (((LSL_String)x).m_string);
738 if (x is string) return double.Parse ((string)x);
739 if (x is LSL_Float) return (double)(LSL_Float)x;
740 if (x is LSL_Integer) return (double)(int)(LSL_Integer)x;
741 if (x is int) return (double)(int)x;
742 return (double)x;
743 }
744
745 public static int ObjectToInteger (object x)
746 {
747 if (x is LSL_String) return int.Parse (((LSL_String)x).m_string);
748 if (x is string) return int.Parse ((string)x);
749 if (x is LSL_Integer) return (int)(LSL_Integer)x;
750 return (int)x;
751 }
752
753 public static LSL_List ObjectToList (object x)
754 {
755 return (LSL_List)x;
756 }
757
758 public static LSL_Rotation ObjectToRotation (object x)
759 {
760 if (x is LSL_String) return new LSL_Rotation (((LSL_String)x).m_string);
761 if (x is string) return new LSL_Rotation ((string)x);
762 return (LSL_Rotation)x;
763 }
764
765 public static LSL_Vector ObjectToVector (object x)
766 {
767 if (x is LSL_String) return new LSL_Vector (((LSL_String)x).m_string);
768 if (x is string) return new LSL_Vector ((string)x);
769 return (LSL_Vector)x;
770 }
771
772 public static string ExceptionToString (Exception x, XMRInstAbstract inst)
773 {
774 return XMRInstAbstract.xmrExceptionTypeName (x) + ": " + XMRInstAbstract.xmrExceptionMessage (x) +
775 "\n" + inst.xmrExceptionStackTrace (x);
776 }
777
778 /*
779 * These are used by event handler entrypoints to remove any LSL wrapping
780 * from the argument list and return the unboxed/unwrapped value.
781 */
782 public static double EHArgUnwrapFloat (object x)
783 {
784 if (x is LSL_Float) return (double)(LSL_Float)x;
785 return (double)x;
786 }
787
788 public static int EHArgUnwrapInteger (object x)
789 {
790 if (x is LSL_Integer) return (int)(LSL_Integer)x;
791 return (int)x;
792 }
793
794 public static LSL_Rotation EHArgUnwrapRotation (object x)
795 {
796 if (x is OpenMetaverse.Quaternion) {
797 OpenMetaverse.Quaternion q = (OpenMetaverse.Quaternion)x;
798 return new LSL_Rotation(q.X, q.Y, q.Z, q.W);
799 }
800 return (LSL_Rotation)x;
801 }
802
803 public static string EHArgUnwrapString (object x)
804 {
805 if (x is LSL_Key) return (string)(LSL_Key)x;
806 if (x is LSL_String) return (string)(LSL_String)x;
807 return (string)x;
808 }
809
810 public static LSL_Vector EHArgUnwrapVector (object x)
811 {
812 if (x is OpenMetaverse.Vector3) {
813 OpenMetaverse.Vector3 v = (OpenMetaverse.Vector3)x;
814 return new LSL_Vector(v.X, v.Y, v.Z);
815 }
816 return (LSL_Vector)x;
817 }
818 }
819}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptVarDict.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptVarDict.cs
new file mode 100644
index 0000000..67e1c34
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptVarDict.cs
@@ -0,0 +1,371 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31
32/**
33 * @brief Collection of variable/function/method definitions
34 */
35
36namespace OpenSim.Region.ScriptEngine.XMREngine
37{
38 public class VarDict : IEnumerable {
39 public VarDict outerVarDict; // next outer VarDict to search
40 public TokenDeclSDTypeClass thisClass; // this VarDict is for members of thisClass
41
42 private struct ArgTypes {
43 public TokenType[] argTypes;
44
45 public bool CanBeCalledBy (TokenType[] calledBy)
46 {
47 if ((argTypes == null) && (calledBy == null)) return true;
48 if ((argTypes == null) || (calledBy == null)) return false;
49 if (argTypes.Length != calledBy.Length) return false;
50 for (int i = argTypes.Length; -- i >= 0;) {
51 if (!TypeCast.IsAssignableFrom (argTypes[i], calledBy[i])) return false;
52 }
53 return true;
54 }
55
56 public override bool Equals (Object that)
57 {
58 if (that == null) return false;
59 if (that.GetType () != typeof (ArgTypes)) return false;
60 TokenType[] at = this.argTypes;
61 TokenType[] bt = ((ArgTypes)that).argTypes;
62 if ((at == null) && (bt == null)) return true;
63 if ((at == null) || (bt == null)) return false;
64 if (at.Length != bt.Length) return false;
65 for (int i = at.Length; -- i >= 0;) {
66 if (at[i].ToString () != bt[i].ToString ()) return false;
67 }
68 return true;
69 }
70
71 public override int GetHashCode ()
72 {
73 TokenType[] at = this.argTypes;
74 if (at == null) return -1;
75 int hc = 0;
76 for (int i = at.Length; -- i >= 0;) {
77 int c = (hc < 0) ? 1 : 0;
78 hc = hc * 2 + c;
79 hc ^= at[i].ToString ().GetHashCode ();
80 }
81 return hc;
82 }
83 }
84
85 private struct TDVEntry {
86 public int count;
87 public TokenDeclVar var;
88 }
89
90 private bool isFrozen = false;
91 private bool locals;
92 private Dictionary<string, Dictionary<ArgTypes, TDVEntry>> master = new Dictionary<string, Dictionary<ArgTypes, TDVEntry>> ();
93 private int count = 0;
94 private VarDict frozenLocals = null;
95
96 /**
97 * @brief Constructor.
98 * @param locals = false: cannot be frozen, allows forward references
99 * true: can be frozen, thus forbidding forward references
100 */
101 public VarDict (bool locals)
102 {
103 this.locals = locals;
104 }
105
106 /**
107 * @brief Add new variable to the dictionary.
108 */
109 public bool AddEntry (TokenDeclVar var)
110 {
111 if (isFrozen) {
112 throw new Exception ("var dict is frozen");
113 }
114
115 /*
116 * Make sure we have a sub-dictionary based on the bare name (ie, no signature)
117 */
118 Dictionary<ArgTypes, TDVEntry> typedic;
119 if (!master.TryGetValue (var.name.val, out typedic)) {
120 typedic = new Dictionary<ArgTypes, TDVEntry> ();
121 master.Add (var.name.val, typedic);
122 }
123
124 /*
125 * See if there is an entry in the sub-dictionary that matches the argument signature.
126 * Note that fields have null argument lists.
127 * Methods always have a non-null argument list, even if only 0 entries long.
128 */
129 ArgTypes types;
130 types.argTypes = (var.argDecl == null) ? null : KeyTypesToStringTypes (var.argDecl.types);
131 if (typedic.ContainsKey (types)) return false;
132
133 /*
134 * It is unique, add to its name-specific sub-dictionary.
135 */
136 TDVEntry entry;
137 entry.count = ++ count;
138 entry.var = var;
139 typedic.Add (types, entry);
140 return true;
141 }
142
143 public int Count { get { return count; } }
144
145 /**
146 * @brief If this is not a local variable frame, just return the frame as is.
147 * If this is a local variable frame, return a version that is frozen,
148 * ie, one that does not contain any future additions.
149 */
150 public VarDict FreezeLocals ()
151 {
152 /*
153 * If not local var frame, return original frame as is.
154 * This will allow forward references as the future additions
155 * will be seen by lookups done in this dictionary.
156 */
157 if (!locals) return this;
158
159 /*
160 * If local var frame, return a copy frozen at this point.
161 * This disallows forward referenes as those future additions
162 * will not be seen by lookups done in the frozen dictionary.
163 */
164 if ((frozenLocals == null) || (frozenLocals.count != this.count)) {
165
166 /*
167 * Make a copy of the current var dictionary frame.
168 * We copy a reference to the dictionary, and though it may
169 * contain additions made after this point, those additions
170 * will have a count .gt. frozen count and will be ignored.
171 */
172 frozenLocals = new VarDict (true);
173
174 frozenLocals.outerVarDict = this.outerVarDict;
175 frozenLocals.thisClass = this.thisClass;
176 frozenLocals.master = this.master;
177 frozenLocals.count = this.count;
178 frozenLocals.frozenLocals = frozenLocals;
179
180 /*
181 * Mark it as being frozen.
182 * - assert fail if any attempt is made to add to it
183 * - ignore any additions to the dictionary with greater count
184 */
185 frozenLocals.isFrozen = true;
186 }
187 return frozenLocals;
188 }
189
190 /**
191 * @brief Find all functions/variables that are callable
192 * @param name = name of function/variable to look for
193 * @param argTypes = the argument types the function is being called with
194 * null to look for a variable
195 * @returns null: no matching function/variable found
196 * else: list of matching functions/variables
197 * for variables, always of length 1
198 */
199 private List<TokenDeclVar> found = new List<TokenDeclVar> ();
200 public TokenDeclVar[] FindCallables (string name, TokenType[] argTypes)
201 {
202 argTypes = KeyTypesToStringTypes (argTypes);
203 TokenDeclVar var = FindExact (name, argTypes);
204 if (var != null) return new TokenDeclVar[] { var };
205
206 Dictionary<ArgTypes, TDVEntry> typedic;
207 if (!master.TryGetValue (name, out typedic)) return null;
208
209 found.Clear ();
210 foreach (KeyValuePair<ArgTypes, TDVEntry> kvp in typedic) {
211 if ((kvp.Value.count <= this.count) && kvp.Key.CanBeCalledBy (argTypes)) {
212 found.Add (kvp.Value.var);
213 }
214 }
215 return (found.Count > 0) ? found.ToArray () : null;
216 }
217
218 /**
219 * @brief Find exact matching function/variable
220 * @param name = name of function to look for
221 * @param argTypes = argument types the function was declared with
222 * null to look for a variable
223 * @returns null: no matching function/variable found
224 * else: the matching function/variable
225 */
226 public TokenDeclVar FindExact (string name, TokenType[] argTypes)
227 {
228 /*
229 * Look for list of stuff that matches the given name.
230 */
231 Dictionary<ArgTypes, TDVEntry> typedic;
232 if (!master.TryGetValue (name, out typedic)) return null;
233
234 /*
235 * Loop through all fields/methods declared by that name, regardless of arg signature.
236 */
237 foreach (TDVEntry entry in typedic.Values) {
238 if (entry.count > this.count) continue;
239 TokenDeclVar var = entry.var;
240
241 /*
242 * Get argument types of declaration.
243 * fields are always null
244 * methods are always non-null, though may be zero-length
245 */
246 TokenType[] declArgs = (var.argDecl == null) ? null : var.argDecl.types;
247
248 /*
249 * Convert any key args to string args.
250 */
251 declArgs = KeyTypesToStringTypes (declArgs);
252
253 /*
254 * If both are null, they are signature-less (ie, both are fields), and so match.
255 */
256 if ((declArgs == null) && (argTypes == null)) return var;
257
258 /*
259 * If calling a delegate, it is a match, regardless of delegate arg types.
260 * If it turns out the arg types do not match, the compiler will give an error
261 * trying to cast the arguments to the delegate arg types.
262 * We don't allow overloading same field name with different delegate types.
263 */
264 if ((declArgs == null) && (argTypes != null)) {
265 TokenType fieldType = var.type;
266 if (fieldType is TokenTypeSDTypeDelegate) return var;
267 }
268
269 /*
270 * If not both null, no match, keep looking.
271 */
272 if ((declArgs == null) || (argTypes == null)) continue;
273
274 /*
275 * Both not null, match argument types to make sure we have correct overload.
276 */
277 int i = declArgs.Length;
278 if (i != argTypes.Length) continue;
279 while (-- i >= 0) {
280 string da = declArgs[i].ToString ();
281 string ga = argTypes[i].ToString ();
282 if (da == "key") da = "string";
283 if (ga == "key") ga = "string";
284 if (da != ga) break;
285 }
286 if (i < 0) return var;
287 }
288
289 /*
290 * No match.
291 */
292 return null;
293 }
294
295 /**
296 * @brief Replace any TokenTypeKey elements with TokenTypeStr so that
297 * it doesn't matter if functions are declared with key or string,
298 * they will accept either.
299 * @param argTypes = argument types as declared in source code
300 * @returns argTypes with any key replaced by string
301 */
302 private static TokenType[] KeyTypesToStringTypes (TokenType[] argTypes)
303 {
304 if (argTypes != null) {
305 int i;
306 int nats = argTypes.Length;
307 for (i = nats; -- i >= 0;) {
308 if (argTypes[i] is TokenTypeKey) break;
309 }
310 if (i >= 0) {
311 TokenType[] at = new TokenType[nats];
312 for (i = nats; -- i >= 0;) {
313 at[i] = argTypes[i];
314 if (argTypes[i] is TokenTypeKey) {
315 at[i] = new TokenTypeStr (argTypes[i]);
316 }
317 }
318 return at;
319 }
320 }
321 return argTypes;
322 }
323
324 // foreach goes through all the TokenDeclVars that were added
325
326 // IEnumerable
327 public IEnumerator GetEnumerator ()
328 {
329 return new VarDictEnumerator (this.master, this.count);
330 }
331
332 private class VarDictEnumerator : IEnumerator {
333 private IEnumerator masterEnum;
334 private IEnumerator typedicEnum;
335 private int count;
336
337 public VarDictEnumerator (Dictionary<string, Dictionary<ArgTypes, TDVEntry>> master, int count)
338 {
339 masterEnum = master.Values.GetEnumerator ();
340 this.count = count;
341 }
342
343 // IEnumerator
344 public void Reset ()
345 {
346 masterEnum.Reset ();
347 typedicEnum = null;
348 }
349
350 // IEnumerator
351 public bool MoveNext ()
352 {
353 while (true) {
354 if (typedicEnum != null) {
355 while (typedicEnum.MoveNext ()) {
356 if (((TDVEntry)typedicEnum.Current).count <= this.count) return true;
357 }
358 typedicEnum = null;
359 }
360 if (!masterEnum.MoveNext ()) return false;
361 Dictionary<ArgTypes, TDVEntry> ctd;
362 ctd = (Dictionary<ArgTypes, TDVEntry>)masterEnum.Current;
363 typedicEnum = ctd.Values.GetEnumerator ();
364 }
365 }
366
367 // IEnumerator
368 public object Current { get { return ((TDVEntry)typedicEnum.Current).var; } }
369 }
370 }
371}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRWebRequest.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRWebRequest.cs
new file mode 100644
index 0000000..3579332
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRWebRequest.cs
@@ -0,0 +1,269 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/**
29 * @brief Perform web request
30 */
31
32using System;
33using System.IO;
34using System.Net;
35using System.Text;
36
37namespace OpenSim.Region.ScriptEngine.XMREngine {
38 public class MMRWebRequest {
39 public static bool allowFileURL = false;
40
41 public static Stream MakeRequest (string verb, string requestUrl, string obj, int timeoutms)
42 {
43 /*
44 * Pick apart the given URL and make sure we support it.
45 * For file:// URLs, just return a read-only stream of the file.
46 */
47 Uri uri = new Uri (requestUrl);
48 string supported = "http and https";
49 if (allowFileURL && (verb == "GET")) {
50 supported = "file, http and https";
51 if (uri.Scheme == "file") {
52 return File.OpenRead (requestUrl.Substring (7));
53 }
54 }
55 bool https = uri.Scheme == "https";
56 if (!https && (uri.Scheme != "http")) {
57 throw new WebException ("only support " + supported + ", not " + uri.Scheme);
58 }
59 string host = uri.Host;
60 int port = uri.Port;
61 if (port < 0) port = https ? 443 : 80;
62 string path = uri.AbsolutePath;
63
64 /*
65 * Connect to the web server.
66 */
67 System.Net.Sockets.TcpClient tcpconnection = new System.Net.Sockets.TcpClient (host, port);
68 if (timeoutms > 0) {
69 tcpconnection.SendTimeout = timeoutms;
70 tcpconnection.ReceiveTimeout = timeoutms;
71 }
72
73 try {
74
75 /*
76 * Get TCP stream to/from web server.
77 * If HTTPS, wrap stream with SSL encryption.
78 */
79 Stream tcpstream = tcpconnection.GetStream ();
80 if (https) {
81 System.Net.Security.SslStream sslstream = new System.Net.Security.SslStream (tcpstream, false);
82 sslstream.AuthenticateAsClient (host);
83 tcpstream = sslstream;
84 }
85
86 /*
87 * Write request header to the web server.
88 * There might be some POST data as well to write to web server.
89 */
90 WriteStream (tcpstream, verb + " " + path + " HTTP/1.1\r\n");
91 WriteStream (tcpstream, "Host: " + host + "\r\n");
92 if (obj != null) {
93 byte[] bytes = Encoding.UTF8.GetBytes (obj);
94
95 WriteStream (tcpstream, "Content-Length: " + bytes.Length + "\r\n");
96 WriteStream (tcpstream, "Content-Type: application/x-www-form-urlencoded\r\n");
97 WriteStream (tcpstream, "\r\n");
98 tcpstream.Write (bytes, 0, bytes.Length);
99 } else {
100 WriteStream (tcpstream, "\r\n");
101 }
102 tcpstream.Flush ();
103
104 /*
105 * Check for successful reply status line.
106 */
107 string headerline = ReadStreamLine (tcpstream).Trim ();
108 if (headerline != "HTTP/1.1 200 OK") throw new WebException ("status line " + headerline);
109
110 /*
111 * Scan through header lines.
112 * The only ones we care about are Content-Length and Transfer-Encoding.
113 */
114 bool chunked = false;
115 int contentlength = -1;
116 while ((headerline = ReadStreamLine (tcpstream).Trim ().ToLowerInvariant ()) != "") {
117 if (headerline.StartsWith ("content-length:")) {
118 contentlength = int.Parse (headerline.Substring (15));
119 }
120 if (headerline.StartsWith ("transfer-encoding:") && (headerline.Substring (18).Trim () == "chunked")) {
121 chunked = true;
122 }
123 }
124
125 /*
126 * Read response byte array as a series of chunks.
127 */
128 if (chunked) {
129 return new ChunkedStreamReader (tcpstream);
130 }
131
132 /*
133 * Read response byte array with the exact length given by Content-Length.
134 */
135 if (contentlength >= 0) {
136 return new LengthStreamReader (tcpstream, contentlength);
137 }
138
139 /*
140 * Don't know how it is being transferred.
141 */
142 throw new WebException ("header missing content-length or transfer-encoding: chunked");
143 } catch {
144 tcpconnection.Close ();
145 throw;
146 }
147 }
148
149 /**
150 * @brief Write the string out as ASCII bytes.
151 */
152 private static void WriteStream (Stream stream, string line)
153 {
154 byte[] bytes = Encoding.ASCII.GetBytes (line);
155 stream.Write (bytes, 0, bytes.Length);
156 }
157
158 /**
159 * @brief Read the next text line from a stream.
160 * @returns string with \r\n trimmed off
161 */
162 private static string ReadStreamLine (Stream stream)
163 {
164 StringBuilder sb = new StringBuilder ();
165 while (true) {
166 int b = stream.ReadByte ();
167 if (b < 0) break;
168 if (b == '\n') break;
169 if (b == '\r') continue;
170 sb.Append ((char)b);
171 }
172 return sb.ToString ();
173 }
174
175 private class ChunkedStreamReader : Stream {
176 private int chunklen;
177 private Stream tcpstream;
178
179 public ChunkedStreamReader (Stream tcpstream)
180 {
181 this.tcpstream = tcpstream;
182 }
183
184 public override bool CanRead { get { return true; } }
185 public override bool CanSeek { get { return false; } }
186 public override bool CanTimeout { get { return false; } }
187 public override bool CanWrite { get { return false; } }
188 public override long Length { get { return 0; } }
189 public override long Position { get { return 0; } set { } }
190 public override void Flush () { }
191 public override long Seek (long offset, SeekOrigin origin) { return 0; }
192 public override void SetLength (long length) { }
193 public override void Write (byte[] buffer, int offset, int length) { }
194
195 public override int Read (byte[] buffer, int offset, int length)
196 {
197 if (length <= 0) return 0;
198
199 if (chunklen == 0) {
200 chunklen = int.Parse (ReadStreamLine (tcpstream), System.Globalization.NumberStyles.HexNumber);
201 if (chunklen < 0) throw new WebException ("negative chunk length");
202 if (chunklen == 0) chunklen = -1;
203 }
204 if (chunklen < 0) return 0;
205
206 int maxread = (length < chunklen) ? length : chunklen;
207 int lenread = tcpstream.Read (buffer, offset, maxread);
208 chunklen -= lenread;
209 if (chunklen == 0) {
210 int b = tcpstream.ReadByte ();
211 if (b == '\r') b = tcpstream.ReadByte ();
212 if (b != '\n') throw new WebException ("chunk not followed by \\r\\n");
213 }
214 return lenread;
215 }
216
217 public override void Close ()
218 {
219 chunklen = -1;
220 if (tcpstream != null) {
221 tcpstream.Close ();
222 tcpstream = null;
223 }
224 }
225 }
226
227 private class LengthStreamReader : Stream {
228 private int contentlength;
229 private Stream tcpstream;
230
231 public LengthStreamReader (Stream tcpstream, int contentlength)
232 {
233 this.tcpstream = tcpstream;
234 this.contentlength = contentlength;
235 }
236
237 public override bool CanRead { get { return true; } }
238 public override bool CanSeek { get { return false; } }
239 public override bool CanTimeout { get { return false; } }
240 public override bool CanWrite { get { return false; } }
241 public override long Length { get { return 0; } }
242 public override long Position { get { return 0; } set { } }
243 public override void Flush () { }
244 public override long Seek (long offset, SeekOrigin origin) { return 0; }
245 public override void SetLength (long length) { }
246 public override void Write (byte[] buffer, int offset, int length) { }
247
248 public override int Read (byte[] buffer, int offset, int length)
249 {
250 if (length <= 0) return 0;
251 if (contentlength <= 0) return 0;
252
253 int maxread = (length < contentlength) ? length : contentlength;
254 int lenread = tcpstream.Read (buffer, offset, maxread);
255 contentlength -= lenread;
256 return lenread;
257 }
258
259 public override void Close ()
260 {
261 contentlength = -1;
262 if (tcpstream != null) {
263 tcpstream.Close ();
264 tcpstream = null;
265 }
266 }
267 }
268 }
269}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MonoTaskletsDummy.cs b/OpenSim/Region/ScriptEngine/XMREngine/MonoTaskletsDummy.cs
new file mode 100644
index 0000000..98910ae
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MonoTaskletsDummy.cs
@@ -0,0 +1,55 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28// Used to build a dummy Mono.Tasklets.dll file when running on Windows
29// Will also work if running with mono, it will just not allow use of
30// the "con" and "mmr" thread models, only "sys" will work.
31
32using System;
33
34namespace Mono.Tasklets {
35 public class Continuation : IDisposable
36 {
37 public Continuation ()
38 {
39 throw new NotSupportedException ("'con' thread model requires mono");
40 }
41 public void Dispose ()
42 { }
43
44 public void Mark ()
45 { }
46
47 public int Store (int state)
48 {
49 return 0;
50 }
51
52 public void Restore (int state)
53 { }
54 }
55}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs
new file mode 100644
index 0000000..36d95d3
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs
@@ -0,0 +1,534 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Text;
32
33using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
34using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
35using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
36using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
37using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
38using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
39using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
40
41// This class exists in the main app domain
42//
43namespace OpenSim.Region.ScriptEngine.XMREngine
44{
45 /**
46 * @brief Array objects.
47 */
48 public class XMR_Array {
49 private const int EMPTYHEAP = 64;
50 private const int ENTRYHEAP = 24;
51
52 private bool enumrValid; // true: enumr set to return array[arrayValid]
53 // false: array[0..arrayValid-1] is all there is
54 private SortedDictionary<object, object> dnary;
55 private SortedDictionary<object, object>.Enumerator enumr;
56 // enumerator used to fill 'array' past arrayValid to end of dictionary
57 private int arrayValid; // number of elements in 'array' that have been filled in
58 private KeyValuePair<object, object>[] array; // list of kvp's that have been returned by ForEach() since last modification
59 private XMRInstAbstract inst; // script instance debited with heap use
60 private int heapUse; // current heap use debit amount
61
62 public static TokenTypeSDTypeDelegate countDelegate = new TokenTypeSDTypeDelegate (new TokenTypeInt (null), new TokenType[0]);
63 public static TokenTypeSDTypeDelegate clearDelegate = new TokenTypeSDTypeDelegate (new TokenTypeVoid (null), new TokenType[0]);
64 public static TokenTypeSDTypeDelegate indexDelegate = new TokenTypeSDTypeDelegate (new TokenTypeObject (null), new TokenType[] { new TokenTypeInt (null) });
65 public static TokenTypeSDTypeDelegate valueDelegate = new TokenTypeSDTypeDelegate (new TokenTypeObject (null), new TokenType[] { new TokenTypeInt (null) });
66
67 public XMR_Array (XMRInstAbstract inst)
68 {
69 this.inst = inst;
70 dnary = new SortedDictionary<object, object> (XMRArrayKeyComparer.singleton);
71 heapUse = inst.UpdateHeapUse (0, EMPTYHEAP);
72 }
73
74 ~XMR_Array ()
75 {
76 heapUse = inst.UpdateHeapUse (heapUse, 0);
77 }
78
79 public static TokenType GetRValType (TokenName name)
80 {
81 if (name.val == "count") return new TokenTypeInt (name);
82 if (name.val == "clear") return clearDelegate;
83 if (name.val == "index") return indexDelegate;
84 if (name.val == "value") return valueDelegate;
85 return new TokenTypeVoid (name);
86 }
87
88 /**
89 * @brief Handle 'array[index]' syntax to get or set an element of the dictionary.
90 * Get returns null if element not defined, script sees type 'undef'.
91 * Setting an element to null removes it.
92 */
93 public object GetByKey(object key)
94 {
95 object val;
96 key = FixKey (key);
97 if (!dnary.TryGetValue (key, out val)) val = null;
98 return val;
99 }
100
101 public void SetByKey(object key, object value)
102 {
103 key = FixKey (key);
104
105 /*
106 * Update heap use throwing an exception on failure
107 * before making any changes to the array.
108 */
109 int keysize = HeapTrackerObject.Size (key);
110 int newheapuse = heapUse;
111 object oldval;
112 if (dnary.TryGetValue (key, out oldval)) {
113 newheapuse -= keysize + HeapTrackerObject.Size (oldval);
114 }
115 if (value != null) {
116 newheapuse += keysize + HeapTrackerObject.Size (value);
117 }
118 heapUse = inst.UpdateHeapUse (heapUse, newheapuse);
119
120 /*
121 * Save new value in array, replacing one of same key if there.
122 * null means remove the value, ie, script did array[key] = undef.
123 */
124 if (value != null) {
125 dnary[key] = value;
126 } else {
127 dnary.Remove (key);
128
129 /*
130 * Shrink the enumeration array, but always leave at least one element.
131 */
132 if ((array != null) && (dnary.Count < array.Length / 2)) {
133 Array.Resize<KeyValuePair<object, object>> (ref array, array.Length / 2);
134 }
135 }
136
137 /*
138 * The enumeration array is invalid because the dictionary has been modified.
139 * Next time a ForEach() call happens, it will repopulate 'array' as elements are retrieved.
140 */
141 arrayValid = 0;
142 }
143
144 /**
145 * @brief Converts an 'object' type to array, key, list, string, but disallows null,
146 * as our language doesn't allow types other than 'object' to be null.
147 * Value types (float, rotation, etc) don't need explicit check for null as
148 * the C# runtime can't convert a null to a value type, and throws an exception.
149 * But for any reference type (array, key, etc) we must manually check for null.
150 */
151 public static XMR_Array Obj2Array (object obj)
152 {
153 if (obj == null) throw new NullReferenceException ();
154 return (XMR_Array)obj;
155 }
156 public static LSL_Key Obj2Key (object obj)
157 {
158 if (obj == null) throw new NullReferenceException ();
159 return (LSL_Key)obj;
160 }
161 public static LSL_List Obj2List (object obj)
162 {
163 if (obj == null) throw new NullReferenceException ();
164 return (LSL_List)obj;
165 }
166 public static LSL_String Obj2String (object obj)
167 {
168 if (obj == null) throw new NullReferenceException ();
169 return obj.ToString ();
170 }
171
172 /**
173 * @brief remove all elements from the array.
174 * sets everything to its 'just constructed' state.
175 */
176 public void __pub_clear ()
177 {
178 heapUse = inst.UpdateHeapUse (heapUse, EMPTYHEAP);
179 dnary.Clear ();
180 enumrValid = false;
181 arrayValid = 0;
182 array = null;
183 }
184
185 /**
186 * @brief return number of elements in the array.
187 */
188 public int __pub_count ()
189 {
190 return dnary.Count;
191 }
192
193 /**
194 * @brief Retrieve index (key) of an arbitrary element.
195 * @param number = number of the element (0 based)
196 * @returns null: array doesn't have that many elements
197 * else: index (key) for that element
198 */
199 public object __pub_index (int number)
200 {
201 return ForEach (number) ? UnfixKey (array[number].Key) : null;
202 }
203
204 /**
205 * @brief Retrieve value of an arbitrary element.
206 * @param number = number of the element (0 based)
207 * @returns null: array doesn't have that many elements
208 * else: value for that element
209 */
210 public object __pub_value (int number)
211 {
212 return ForEach (number) ? array[number].Value : null;
213 }
214
215 /**
216 * @brief Called in each iteration of a 'foreach' statement.
217 * @param number = index of element to retrieve (0 = first one)
218 * @returns false: element does not exist
219 * true: element exists
220 */
221 private bool ForEach (int number)
222 {
223 /*
224 * If we don't have any array, we can't have ever done
225 * any calls here before, so allocate an array big enough
226 * and set everything else to the beginning.
227 */
228 if (array == null) {
229 array = new KeyValuePair<object, object>[dnary.Count];
230 arrayValid = 0;
231 }
232
233 /*
234 * If dictionary modified since last enumeration, get a new enumerator.
235 */
236 if (arrayValid == 0) {
237 enumr = dnary.GetEnumerator ();
238 enumrValid = true;
239 }
240
241 /*
242 * Make sure we have filled the array up enough for requested element.
243 */
244 while ((arrayValid <= number) && enumrValid && enumr.MoveNext ()) {
245 if (arrayValid >= array.Length) {
246 Array.Resize<KeyValuePair<object, object>> (ref array, dnary.Count);
247 }
248 array[arrayValid++] = enumr.Current;
249 }
250
251 /*
252 * If we don't have that many elements, return end-of-array status.
253 */
254 return number < arrayValid;
255 }
256
257 /**
258 * @brief Transmit array out in such a way that it can be reconstructed,
259 * including any in-progress ForEach() enumerations.
260 */
261 public delegate void SendArrayObjDelegate (object graph);
262 public void SendArrayObj (SendArrayObjDelegate sendObj)
263 {
264 /*
265 * Set the count then the elements themselves.
266 * UnfixKey() because sendObj doesn't handle XMRArrayListKeys.
267 */
268 sendObj (dnary.Count);
269 foreach (KeyValuePair<object, object> kvp in dnary) {
270 sendObj (UnfixKey (kvp.Key));
271 sendObj (kvp.Value);
272 }
273 }
274
275 /**
276 * @brief Receive array in. Any previous contents are erased.
277 * Set up such that any enumeration in progress will resume
278 * at the exact spot and in the exact same order as they
279 * were in on the sending side.
280 */
281 public delegate object RecvArrayObjDelegate ();
282 public void RecvArrayObj (RecvArrayObjDelegate recvObj)
283 {
284 heapUse = inst.UpdateHeapUse (heapUse, EMPTYHEAP);
285
286 /*
287 * Cause any enumeration to refill the array from the sorted dictionary.
288 * Since it is a sorted dictionary, any enumerations will be in the same
289 * order as on the sending side.
290 */
291 arrayValid = 0;
292 enumrValid = false;
293
294 /*
295 * Fill dictionary.
296 */
297 dnary.Clear ();
298 int count = (int)recvObj ();
299 while (-- count >= 0) {
300 object key = FixKey (recvObj ());
301 object val = recvObj ();
302 int htuse = HeapTrackerObject.Size (key) + HeapTrackerObject.Size (val);
303 heapUse = inst.UpdateHeapUse (heapUse, heapUse + htuse);
304 dnary.Add (key, val);
305 }
306 }
307
308 /**
309 * We want our index values to be of consistent type, otherwise we get things like (LSL_Integer)1 != (int)1.
310 * So strip off any LSL-ness from the types.
311 * We also deep-strip any given lists used as keys (multi-dimensional arrays).
312 */
313 public static object FixKey (object key)
314 {
315 if (key is LSL_Integer) return (int)(LSL_Integer)key;
316 if (key is LSL_Float) return (double)(LSL_Float)key;
317 if (key is LSL_Key) return (string)(LSL_Key)key;
318 if (key is LSL_String) return (string)(LSL_String)key;
319 if (key is LSL_List) {
320 object[] data = ((LSL_List)key).Data;
321 if (data.Length == 1) return FixKey (data[0]);
322 return new XMRArrayListKey ((LSL_List)key);
323 }
324 return key; // int, double, string, LSL_Vector, LSL_Rotation, etc are ok as is
325 }
326
327 /**
328 * @brief When returning a key, such as for array.index(), we want to return the original
329 * LSL_List, not the sanitized one, as the script compiler expects an LSL_List.
330 * Any other sanitized types can remain as is (int, string, etc).
331 */
332 private static object UnfixKey (object key)
333 {
334 if (key is XMRArrayListKey) key = ((XMRArrayListKey)key).GetOriginal ();
335 return key;
336 }
337 }
338
339 public class XMRArrayKeyComparer : IComparer<object> {
340
341 public static XMRArrayKeyComparer singleton = new XMRArrayKeyComparer ();
342
343 /**
344 * @brief Compare two keys
345 */
346 public int Compare (object x, object y) // IComparer<object>
347 {
348 /*
349 * Use short type name (eg, String, Int32, XMRArrayListKey) as most significant part of key.
350 */
351 string xtn = x.GetType ().Name;
352 string ytn = y.GetType ().Name;
353 int ctn = String.CompareOrdinal (xtn, ytn);
354 if (ctn != 0) return ctn;
355
356 ComparerDelegate cd;
357 if (!comparers.TryGetValue (xtn, out cd)) {
358 throw new Exception ("unsupported key type " + xtn);
359 }
360 return cd (x, y);
361 }
362
363 private delegate int ComparerDelegate (object a, object b);
364
365 private static Dictionary<string, ComparerDelegate> comparers = BuildComparers ();
366
367 private static Dictionary<string, ComparerDelegate> BuildComparers ()
368 {
369 Dictionary<string, ComparerDelegate> cmps = new Dictionary<string, ComparerDelegate> ();
370 cmps.Add (typeof (double).Name, MyFloatComparer);
371 cmps.Add (typeof (int).Name, MyIntComparer);
372 cmps.Add (typeof (XMRArrayListKey).Name, MyListKeyComparer);
373 cmps.Add (typeof (LSL_Rotation).Name, MyRotationComparer);
374 cmps.Add (typeof (string).Name, MyStringComparer);
375 cmps.Add (typeof (LSL_Vector).Name, MyVectorComparer);
376 return cmps;
377 }
378
379 private static int MyFloatComparer (object a, object b)
380 {
381 double af = (double)a;
382 double bf = (double)b;
383 if (af < bf) return -1;
384 if (af > bf) return 1;
385 return 0;
386 }
387 private static int MyIntComparer (object a, object b)
388 {
389 return (int)a - (int)b;
390 }
391 private static int MyListKeyComparer (object a, object b)
392 {
393 XMRArrayListKey alk = (XMRArrayListKey)a;
394 XMRArrayListKey blk = (XMRArrayListKey)b;
395 return XMRArrayListKey.Compare (alk, blk);
396 }
397 private static int MyRotationComparer (object a, object b)
398 {
399 LSL_Rotation ar = (LSL_Rotation)a;
400 LSL_Rotation br = (LSL_Rotation)b;
401 if (ar.x < br.x) return -1;
402 if (ar.x > br.x) return 1;
403 if (ar.y < br.y) return -1;
404 if (ar.y > br.y) return 1;
405 if (ar.z < br.z) return -1;
406 if (ar.z > br.z) return 1;
407 if (ar.s < br.s) return -1;
408 if (ar.s > br.s) return 1;
409 return 0;
410 }
411 private static int MyStringComparer (object a, object b)
412 {
413 return String.CompareOrdinal ((string)a, (string)b);
414 }
415 private static int MyVectorComparer (object a, object b)
416 {
417 LSL_Vector av = (LSL_Vector)a;
418 LSL_Vector bv = (LSL_Vector)b;
419 if (av.x < bv.x) return -1;
420 if (av.x > bv.x) return 1;
421 if (av.y < bv.y) return -1;
422 if (av.y > bv.y) return 1;
423 if (av.z < bv.z) return -1;
424 if (av.z > bv.z) return 1;
425 return 0;
426 }
427 }
428
429 /**
430 * @brief Lists used as keys must be sanitized first.
431 * List gets converted to an object[] and each element is converted from LSL_ types to system types where possible.
432 * And we also need an equality operator that compares the values of all elements of the list, not just the lengths.
433 * Note that just like LSL_Lists, we consider these objects to be immutable, so they can be directly used as keys in
434 * the dictionary as they don't ever change.
435 */
436 public class XMRArrayListKey {
437 private LSL_List original;
438 private object[] cleaned;
439 private int length;
440 private int hashCode;
441
442 /**
443 * @brief Construct a sanitized object[] from a list.
444 * Also save the original list in case we need it later.
445 */
446 public XMRArrayListKey (LSL_List key)
447 {
448 original = key;
449 object[] given = key.Data;
450 int len = given.Length;
451 length = len;
452 cleaned = new object[len];
453 int hc = len;
454 for (int i = 0; i < len; i ++) {
455 object v = XMR_Array.FixKey (given[i]);
456 hc += hc + ((hc < 0) ? 1 : 0);
457 hc ^= v.GetHashCode ();
458 cleaned[i] = v;
459 }
460 hashCode = hc;
461 }
462
463 /**
464 * @brief Get heap tracking size.
465 */
466 public int Size {
467 get {
468 return original.Size;
469 }
470 }
471
472 /**
473 * @brief See if the given object is an XMRArrayListKey and every value is equal to our own.
474 */
475 public override bool Equals (object o)
476 {
477 if (!(o is XMRArrayListKey)) return false;
478 XMRArrayListKey a = (XMRArrayListKey)o;
479 int len = a.length;
480 if (len != length) return false;
481 if (a.hashCode != hashCode) return false;
482 for (int i = 0; i < len; i ++) {
483 if (!cleaned[i].Equals (a.cleaned[i])) return false;
484 }
485 return true;
486 }
487
488 /**
489 * @brief Get an hash code.
490 */
491 public override int GetHashCode ()
492 {
493 return hashCode;
494 }
495
496 /**
497 * @brief Compare for key sorting.
498 */
499 public static int Compare (XMRArrayListKey x, XMRArrayListKey y)
500 {
501 int j = x.length - y.length;
502 if (j == 0) {
503 for (int i = 0; i < x.length; i ++) {
504 object xo = x.cleaned[i];
505 object yo = y.cleaned[i];
506 j = XMRArrayKeyComparer.singleton.Compare (xo, yo);
507 if (j != 0) break;
508 }
509 }
510 return j;
511 }
512
513 /**
514 * @brief Get the original LSL_List we were built from.
515 */
516 public LSL_List GetOriginal ()
517 {
518 return original;
519 }
520
521 /**
522 * @brief Debugging
523 */
524 public override string ToString ()
525 {
526 StringBuilder sb = new StringBuilder ();
527 for (int i = 0; i < length; i ++) {
528 if (i > 0) sb.Append (',');
529 sb.Append (cleaned[i].ToString ());
530 }
531 return sb.ToString ();
532 }
533 }
534}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMREngXmrTestLs.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMREngXmrTestLs.cs
new file mode 100644
index 0000000..266c5aa
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMREngXmrTestLs.cs
@@ -0,0 +1,491 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using log4net;
29using System;
30using System.Collections.Generic;
31using System.IO;
32using System.Reflection;
33using System.Text;
34using System.Threading;
35
36using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
37using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
38using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
39using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
40using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
41using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
42using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
43
44namespace OpenSim.Region.ScriptEngine.XMREngine
45{
46 public partial class XMREngine {
47
48 private void XmrTestLs (string[] args, int indx)
49 {
50 bool flagFull = false;
51 bool flagQueues = false;
52 bool flagTopCPU = false;
53 int maxScripts = 0x7FFFFFFF;
54 int numScripts = 0;
55 string outName = null;
56 XMRInstance[] instances;
57
58 /*
59 * Decode command line options.
60 */
61 for (int i = indx; i < args.Length; i ++) {
62 if (args[i] == "-full") {
63 flagFull = true;
64 continue;
65 }
66 if (args[i] == "-help") {
67 m_log.Info ("[XMREngine]: xmr ls -full -max=<number> -out=<filename> -queues -topcpu");
68 return;
69 }
70 if (args[i].StartsWith("-max=")) {
71 try {
72 maxScripts = Convert.ToInt32(args[i].Substring(5));
73 } catch (Exception e) {
74 m_log.Error("[XMREngine]: bad max " + args[i].Substring(5) + ": " + e.Message);
75 return;
76 }
77 continue;
78 }
79 if (args[i].StartsWith("-out=")) {
80 outName = args[i].Substring(5);
81 continue;
82 }
83 if (args[i] == "-queues") {
84 flagQueues = true;
85 continue;
86 }
87 if (args[i] == "-topcpu") {
88 flagTopCPU = true;
89 continue;
90 }
91 if (args[i][0] == '-') {
92 m_log.Error("[XMREngine]: unknown option " + args[i] + ", try 'xmr ls -help'");
93 return;
94 }
95 }
96
97 TextWriter outFile = null;
98 if (outName != null) {
99 try {
100 outFile = File.CreateText(outName);
101 } catch (Exception e) {
102 m_log.Error("[XMREngine]: error creating " + outName + ": " + e.Message);
103 return;
104 }
105 } else {
106 outFile = new LogInfoTextWriter(m_log);
107 }
108
109 try {
110 for (int i = 0; i < numThreadScriptWorkers; i ++) {
111 XMRScriptThread th = m_ScriptThreads[i];
112 outFile.WriteLine("Script thread ID: " + th.m_ScriptThreadTID);
113 long execTime = th.m_ScriptExecTime;
114 if (execTime < 0) {
115 execTime += (long)(DateTime.UtcNow - DateTime.MinValue).TotalMilliseconds;
116 }
117 outFile.WriteLine(" execution time: " + execTime + " mS");
118 outFile.WriteLine(" last ran at: " + th.m_LastRanAt.ToString());
119 XMRInstance rins = th.m_RunInstance;
120 if (rins != null) {
121 outFile.WriteLine(" running: " + rins.ItemID.ToString() + " " + rins.m_DescName);
122 if (flagFull) {
123 outFile.WriteLine (rins.RunTestLs (true));
124 }
125 }
126 }
127
128 /*
129 * Scan instance list to find those that match selection criteria.
130 */
131 if (!Monitor.TryEnter(m_InstancesDict, 100)) {
132 m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
133 return;
134 }
135 try
136 {
137 instances = new XMRInstance[m_InstancesDict.Count];
138 foreach (XMRInstance ins in m_InstancesDict.Values)
139 {
140 if (InstanceMatchesArgs(ins, args, indx)) {
141 instances[numScripts++] = ins;
142 }
143 }
144 } finally {
145 Monitor.Exit(m_InstancesDict);
146 }
147
148 /*
149 * Maybe sort by descending CPU time.
150 */
151 if (flagTopCPU) {
152 Array.Sort<XMRInstance>(instances, CompareInstancesByCPUTime);
153 }
154
155 /*
156 * Print the entries.
157 */
158 if (!flagFull) {
159 outFile.WriteLine(" ItemID" +
160 " CPU(ms)" +
161 " NumEvents" +
162 " Status " +
163 " World Position " +
164 " <Part>:<Item>");
165 }
166 for (int i = 0; (i < numScripts) && (i < maxScripts); i ++) {
167 outFile.WriteLine(instances[i].RunTestLs(flagFull));
168 }
169
170 /*
171 * Print number of scripts that match selection criteria,
172 * even if we were told to print fewer.
173 */
174 outFile.WriteLine("total of {0} script(s)", numScripts);
175
176 /*
177 * If -queues given, print out queue contents too.
178 */
179 if (flagQueues) {
180 LsQueue(outFile, "start", m_StartQueue, args, indx);
181 LsQueue(outFile, "sleep", m_SleepQueue, args, indx);
182 LsQueue(outFile, "yield", m_YieldQueue, args, indx);
183 }
184 } finally {
185 outFile.Close();
186 }
187 }
188
189 private void XmrTestPev (string[] args, int indx)
190 {
191 bool flagAll = false;
192 int numScripts = 0;
193 XMRInstance[] instances;
194
195 /*
196 * Decode command line options.
197 */
198 int i, j;
199 List<string> selargs = new List<string> (args.Length);
200 MethodInfo[] eventmethods = typeof (IEventHandlers).GetMethods ();
201 MethodInfo eventmethod;
202 for (i = indx; i < args.Length; i ++) {
203 string arg = args[i];
204 if (arg == "-all") {
205 flagAll = true;
206 continue;
207 }
208 if (arg == "-help") {
209 m_log.Info ("[XMREngine]: xmr pev -all | <part-of-script-name> <event-name> <params...>");
210 return;
211 }
212 if (arg[0] == '-') {
213 m_log.Error ("[XMREngine]: unknown option " + arg + ", try 'xmr pev -help'");
214 return;
215 }
216 for (j = 0; j < eventmethods.Length; j ++) {
217 eventmethod = eventmethods[j];
218 if (eventmethod.Name == arg) goto gotevent;
219 }
220 selargs.Add (arg);
221 }
222 m_log.Error ("[XMREngine]: missing <event-name> <params...>, try 'xmr pev -help'");
223 return;
224 gotevent:
225 string eventname = eventmethod.Name;
226 StringBuilder sourcesb = new StringBuilder ();
227 while (++ i < args.Length) {
228 sourcesb.Append (' ');
229 sourcesb.Append (args[i]);
230 }
231 string sourcest = sourcesb.ToString ();
232 string sourcehash;
233 youveanerror = false;
234 Token t = TokenBegin.Construct ("", null, ErrorMsg, sourcest, out sourcehash);
235 if (youveanerror) return;
236 ParameterInfo[] paraminfos = eventmethod.GetParameters ();
237 object[] paramvalues = new object[paraminfos.Length];
238 i = 0;
239 while (!((t = t.nextToken) is TokenEnd)) {
240 if (i >= paramvalues.Length) {
241 ErrorMsg (t, "extra parameter(s)");
242 return;
243 }
244 paramvalues[i] = ParseParamValue (ref t);
245 if (paramvalues[i] == null) return;
246 i ++;
247 }
248 OpenSim.Region.ScriptEngine.Shared.EventParams eps =
249 new OpenSim.Region.ScriptEngine.Shared.EventParams (eventname, paramvalues, zeroDetectParams);
250
251 /*
252 * Scan instance list to find those that match selection criteria.
253 */
254 if (!Monitor.TryEnter(m_InstancesDict, 100)) {
255 m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
256 return;
257 }
258
259 try {
260 instances = new XMRInstance[m_InstancesDict.Count];
261 foreach (XMRInstance ins in m_InstancesDict.Values) {
262 if (flagAll || InstanceMatchesArgs (ins, selargs.ToArray (), 0)) {
263 instances[numScripts++] = ins;
264 }
265 }
266 } finally {
267 Monitor.Exit(m_InstancesDict);
268 }
269
270 /*
271 * Post event to the matching instances.
272 */
273 for (i = 0; i < numScripts; i ++) {
274 XMRInstance inst = instances[i];
275 m_log.Info ("[XMREngine]: post " + eventname + " to " + inst.m_DescName);
276 inst.PostEvent (eps);
277 }
278 }
279
280 private object ParseParamValue (ref Token token)
281 {
282 if (token is TokenFloat) {
283 return new LSL_Float (((TokenFloat)token).val);
284 }
285 if (token is TokenInt) {
286 return new LSL_Integer (((TokenInt)token).val);
287 }
288 if (token is TokenStr) {
289 return new LSL_String (((TokenStr)token).val);
290 }
291 if (token is TokenKwCmpLT) {
292 List<double> valuelist = new List<double> ();
293 while (!((token = token.nextToken) is TokenKwCmpGT)) {
294 if (!(token is TokenKwComma)) {
295 object value = ParseParamValue (ref token);
296 if (value == null) return null;
297 if (value is int) value = (double)(int)value;
298 if (!(value is double)) {
299 ErrorMsg (token, "must be float or integer constant");
300 return null;
301 }
302 valuelist.Add ((double)value);
303 } else if (token.prevToken is TokenKwComma) {
304 ErrorMsg (token, "missing constant");
305 return null;
306 }
307 }
308 double[] values = valuelist.ToArray ();
309 switch (values.Length) {
310 case 3: {
311 return new LSL_Vector (values[0], values[1], values[2]);
312 }
313 case 4: {
314 return new LSL_Rotation (values[0], values[1], values[2], values[3]);
315 }
316 default: {
317 ErrorMsg (token, "not rotation or vector");
318 return null;
319 }
320 }
321 }
322 if (token is TokenKwBrkOpen) {
323 List<object> valuelist = new List<object> ();
324 while (!((token = token.nextToken) is TokenKwBrkClose)) {
325 if (!(token is TokenKwComma)) {
326 object value = ParseParamValue (ref token);
327 if (value == null) return null;
328 valuelist.Add (value);
329 } else if (token.prevToken is TokenKwComma) {
330 ErrorMsg (token, "missing constant");
331 return null;
332 }
333 }
334 return new LSL_List (valuelist.ToArray ());
335 }
336 if (token is TokenName) {
337 FieldInfo field = typeof (OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass).GetField (((TokenName)token).val);
338 if ((field != null) && field.IsPublic && (field.IsLiteral || (field.IsStatic && field.IsInitOnly))) {
339 return field.GetValue (null);
340 }
341 }
342 ErrorMsg (token, "invalid constant");
343 return null;
344 }
345
346 private bool youveanerror;
347 private void ErrorMsg (Token token, string message)
348 {
349 youveanerror = true;
350 m_log.Info ("[XMREngine]: " + token.posn + " " + message);
351 }
352
353 private void XmrTestReset (string[] args, int indx)
354 {
355 bool flagAll = false;
356 int numScripts = 0;
357 XMRInstance[] instances;
358
359 if (args.Length <= indx) {
360 m_log.Error("[XMREngine]: must specify part of script name or -all for all scripts");
361 return;
362 }
363
364 /*
365 * Decode command line options.
366 */
367 for (int i = indx; i < args.Length; i ++) {
368 if (args[i] == "-all") {
369 flagAll = true;
370 continue;
371 }
372 if (args[i] == "-help") {
373 m_log.Info ("[XMREngine]: xmr reset -all | <part-of-script-name>");
374 return;
375 }
376 if (args[i][0] == '-') {
377 m_log.Error ("[XMREngine]: unknown option " + args[i] + ", try 'xmr reset -help'");
378 return;
379 }
380 }
381
382 /*
383 * Scan instance list to find those that match selection criteria.
384 */
385 if (!Monitor.TryEnter(m_InstancesDict, 100)) {
386 m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
387 return;
388 }
389
390 try {
391 instances = new XMRInstance[m_InstancesDict.Count];
392 foreach (XMRInstance ins in m_InstancesDict.Values) {
393 if (flagAll || InstanceMatchesArgs (ins, args, indx)) {
394 instances[numScripts++] = ins;
395 }
396 }
397 } finally {
398 Monitor.Exit(m_InstancesDict);
399 }
400
401 /*
402 * Reset the instances as if someone clicked their "Reset" button.
403 */
404 for (int i = 0; i < numScripts; i ++) {
405 XMRInstance inst = instances[i];
406 m_log.Info ("[XMREngine]: resetting " + inst.m_DescName);
407 inst.Reset();
408 }
409 }
410
411 private static int CompareInstancesByCPUTime(XMRInstance a, XMRInstance b)
412 {
413 if (a == null) {
414 return (b == null) ? 0 : 1;
415 }
416 if (b == null) {
417 return -1;
418 }
419 if (b.m_CPUTime < a.m_CPUTime) return -1;
420 if (b.m_CPUTime > a.m_CPUTime) return 1;
421 return 0;
422 }
423
424 private void LsQueue(TextWriter outFile, string name, XMRInstQueue queue, string[] args, int indx)
425 {
426 outFile.WriteLine("Queue " + name + ":");
427 lock (queue) {
428 for (XMRInstance inst = queue.PeekHead(); inst != null; inst = inst.m_NextInst) {
429 try {
430
431 /*
432 * Try to print instance name.
433 */
434 if (InstanceMatchesArgs(inst, args, indx)) {
435 outFile.WriteLine(" " + inst.ItemID.ToString() + " " + inst.m_DescName);
436 }
437 } catch (Exception e) {
438
439 /*
440 * Sometimes there are instances in the queue that are disposed.
441 */
442 outFile.WriteLine(" " + inst.ItemID.ToString() + " " + inst.m_DescName + ": " + e.Message);
443 }
444 }
445 }
446 }
447
448 private bool InstanceMatchesArgs(XMRInstance ins, string[] args, int indx)
449 {
450 bool hadSomethingToCompare = false;
451
452 for (int i = indx; i < args.Length; i ++)
453 {
454 if (args[i][0] != '-') {
455 hadSomethingToCompare = true;
456 if (ins.m_DescName.Contains(args[i])) return true;
457 if (ins.ItemID.ToString().Contains(args[i])) return true;
458 if (ins.AssetID.ToString().Contains(args[i])) return true;
459 }
460 }
461 return !hadSomethingToCompare;
462 }
463 }
464
465 /**
466 * @brief Make m_log.Info look like a text writer.
467 */
468 public class LogInfoTextWriter : TextWriter {
469 private StringBuilder sb = new StringBuilder();
470 private ILog m_log;
471 public LogInfoTextWriter (ILog m_log)
472 {
473 this.m_log = m_log;
474 }
475 public override void Write (char c)
476 {
477 if (c == '\n') {
478 m_log.Info("[XMREngine]: " + sb.ToString());
479 sb.Remove(0, sb.Length);
480 } else {
481 sb.Append(c);
482 }
483 }
484 public override void Close () { }
485 public override Encoding Encoding {
486 get {
487 return Encoding.UTF8;
488 }
489 }
490 }
491}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMREngine.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMREngine.cs
new file mode 100644
index 0000000..24d49f8
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMREngine.cs
@@ -0,0 +1,1979 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using log4net;
29using Mono.Addins;
30using Nini.Config;
31using OpenSim.Framework;
32using OpenSim.Framework.Console;
33using OpenSim.Framework.Monitoring;
34using OpenSim.Region.ClientStack.Linden;
35using OpenSim.Region.Framework.Interfaces;
36using OpenSim.Region.Framework.Scenes;
37using OpenSim.Region.ScriptEngine.Interfaces;
38using OpenSim.Region.ScriptEngine.Shared;
39using OpenSim.Region.ScriptEngine.Shared.Api;
40using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
41using OpenMetaverse;
42using System;
43using System.Collections;
44using System.Collections.Generic;
45using System.IO;
46using System.Reflection;
47using System.Reflection.Emit;
48using System.Text;
49using System.Threading;
50using System.Timers;
51using System.Xml;
52
53using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
54using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
55using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
56using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
57using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
58using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
59using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
60
61[assembly: Addin("XMREngine", OpenSim.VersionInfo.VersionNumber)]
62[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
63
64namespace OpenSim.Region.ScriptEngine.XMREngine
65{
66 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "XMREngine")]
67 public partial class XMREngine : INonSharedRegionModule, IScriptEngine,
68 IScriptModule
69 {
70 public static readonly DetectParams[] zeroDetectParams = new DetectParams[0];
71 private static ArrayList noScriptErrors = new ArrayList();
72 public static readonly ILog m_log =
73 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
74 private static readonly string[] scriptReferencedAssemblies = new string[0];
75
76 private bool m_LateInit;
77 private bool m_TraceCalls;
78 public bool m_Verbose;
79 public bool m_ScriptDebug;
80 public Scene m_Scene;
81 private IConfigSource m_ConfigSource;
82 private IConfig m_Config;
83 private string m_ScriptBasePath;
84 private bool m_Enabled = false;
85 public bool m_StartProcessing = false;
86 public bool m_UseSourceHashCode = false;
87 public ConstructorInfo uThreadCtor;
88 private Dictionary<UUID, ArrayList> m_ScriptErrors =
89 new Dictionary<UUID, ArrayList>();
90 private Dictionary<UUID, List<UUID>> m_ObjectItemList =
91 new Dictionary<UUID, List<UUID>>();
92 private Dictionary<UUID, XMRInstance[]> m_ObjectInstArray =
93 new Dictionary<UUID, XMRInstance[]>();
94 public Dictionary<string,FieldInfo> m_XMRInstanceApiCtxFieldInfos =
95 new Dictionary<string,FieldInfo> ();
96 private int m_StackSize;
97 private int m_HeapSize;
98 private XMRScriptThread[] m_ScriptThreads;
99 private Thread m_SleepThread = null;
100 private Thread m_SliceThread = null;
101 private bool m_Exiting = false;
102
103 private int m_MaintenanceInterval = 10;
104 private System.Timers.Timer m_MaintenanceTimer;
105 public int numThreadScriptWorkers;
106
107 private object m_FrameUpdateLock = new object ();
108 private event ThreadStart m_FrameUpdateList = null;
109
110 /*
111 * Various instance lists:
112 * m_InstancesDict = all known instances
113 * find an instance given its itemID
114 * m_StartQueue = instances that have just had event queued to them
115 * m_YieldQueue = instances that are ready to run right now
116 * m_SleepQueue = instances that have m_SleepUntil valid
117 * sorted by ascending m_SleepUntil
118 */
119 private Dictionary<UUID, XMRInstance> m_InstancesDict =
120 new Dictionary<UUID, XMRInstance>();
121 public Queue<ThreadStart> m_ThunkQueue = new Queue<ThreadStart> ();
122 public XMRInstQueue m_StartQueue = new XMRInstQueue();
123 public XMRInstQueue m_YieldQueue = new XMRInstQueue();
124 public XMRInstQueue m_SleepQueue = new XMRInstQueue();
125 private string m_LockedDict = "nobody";
126
127 public XMREngine()
128 {
129 string envar;
130
131 envar = Environment.GetEnvironmentVariable ("XMREngineTraceCalls");
132 m_TraceCalls = (envar != null) && ((envar[0] & 1) != 0);
133 m_log.Info ("[XMREngine]: m_TraceCalls=" + m_TraceCalls);
134
135 envar = Environment.GetEnvironmentVariable ("XMREngineVerbose");
136 m_Verbose = (envar != null) && ((envar[0] & 1) != 0);
137 m_log.Info ("[XMREngine]: m_Verbose=" + m_Verbose);
138
139 envar = Environment.GetEnvironmentVariable ("XMREngineScriptDebug");
140 m_ScriptDebug = (envar != null) && ((envar[0] & 1) != 0);
141 m_log.Info ("[XMREngine]: m_ScriptDebug=" + m_ScriptDebug);
142 }
143
144 public string Name
145 {
146 get { return "XMREngine"; }
147 }
148
149 public Type ReplaceableInterface
150 {
151 get { return null; }
152 }
153
154 public string ScriptEnginePath
155 {
156 get { return m_ScriptBasePath; }
157 }
158
159 public string ScriptClassName
160 {
161 get { return "XMREngineScript"; }
162 }
163
164 public string ScriptBaseClassName
165 {
166 get { return typeof (XMRInstance).FullName; }
167 }
168
169 public ParameterInfo[] ScriptBaseClassParameters
170 {
171 get { return typeof(XMRInstance).GetConstructor (new Type[] { typeof (WaitHandle) }).GetParameters (); }
172 }
173
174 public string[] ScriptReferencedAssemblies
175 {
176 get { return scriptReferencedAssemblies; }
177 }
178
179 public void Initialise(IConfigSource config)
180 {
181 TraceCalls("[XMREngine]: Initialize entry");
182 m_ConfigSource = config;
183
184 ////foreach (IConfig icfg in config.Configs) {
185 //// m_log.Debug("[XMREngine]: Initialise: configs[" + icfg.Name + "]");
186 //// foreach (string key in icfg.GetKeys ()) {
187 //// m_log.Debug("[XMREngine]: Initialise: " + key + "=" + icfg.GetExpanded (key));
188 //// }
189 ////}
190
191 m_Enabled = false;
192 m_Config = config.Configs["XMREngine"];
193 if (m_Config == null) {
194 m_log.Info("[XMREngine]: no config, assuming disabled");
195 return;
196 }
197 m_Enabled = m_Config.GetBoolean("Enabled", false);
198 m_log.InfoFormat("[XMREngine]: config enabled={0}", m_Enabled);
199 if (!m_Enabled) {
200 return;
201 }
202
203 string uThreadModel = "sys"; // will work anywhere
204 uThreadModel = m_Config.GetString ("UThreadModel", uThreadModel);
205
206 Type uThreadType = null;
207 switch (uThreadModel.ToLower ()) {
208
209 // mono continuations - memcpy()s the stack
210 case "con": {
211 uThreadType = typeof (ScriptUThread_Con);
212 break;
213 }
214
215 // patched mono microthreads - switches stack pointer
216 case "mmr": {
217 Exception e = ScriptUThread_MMR.LoadMono ();
218 if (e != null) {
219 m_log.Error ("[XMREngine]: mmr thread model not available\n", e);
220 m_Enabled = false;
221 return;
222 }
223 uThreadType = typeof (ScriptUThread_MMR);
224 break;
225 }
226
227 // system threads - works on mono and windows
228 case "sys": {
229 uThreadType = typeof (ScriptUThread_Sys);
230 break;
231 }
232
233 // who knows what
234 default: {
235 m_log.Error ("[XMREngine]: unknown thread model " + uThreadModel);
236 m_Enabled = false;
237 return;
238 }
239 }
240
241 uThreadCtor = uThreadType.GetConstructor (new Type[] { typeof (XMRInstance) });
242 m_log.Info ("[XMREngine]: using thread model " + uThreadModel);
243
244 m_UseSourceHashCode = m_Config.GetBoolean ("UseSourceHashCode", false);
245 numThreadScriptWorkers = m_Config.GetInt ("NumThreadScriptWorkers", 1);
246 m_ScriptThreads = new XMRScriptThread[numThreadScriptWorkers];
247
248 for (int i = 0; i < numThreadScriptWorkers; i ++) {
249 m_ScriptThreads[i] = new XMRScriptThread(this);
250 }
251
252 m_SleepThread = StartMyThread (RunSleepThread, "xmrengine sleep", ThreadPriority.Normal);
253 m_SliceThread = StartMyThread (RunSliceThread, "xmrengine slice", ThreadPriority.Normal);
254
255 /*
256 * Verify that our ScriptEventCode's match OpenSim's scriptEvent's.
257 */
258 bool err = false;
259 for (int i = 0; i < 32; i ++) {
260 string mycode = "undefined";
261 string oscode = "undefined";
262 try {
263 mycode = ((ScriptEventCode)i).ToString();
264 Convert.ToInt32(mycode);
265 mycode = "undefined";
266 } catch {
267 }
268 try {
269 oscode = ((OpenSim.Region.Framework.Scenes.scriptEvents)(1 << i)).ToString();
270 Convert.ToInt32(oscode);
271 oscode = "undefined";
272 } catch {
273 }
274 if (mycode != oscode) {
275 m_log.ErrorFormat("[XMREngine]: {0} mycode={1}, oscode={2}", i, mycode, oscode);
276 err = true;
277 }
278 }
279 if (err) {
280 m_Enabled = false;
281 return;
282 }
283
284 m_StackSize = m_Config.GetInt("ScriptStackSize", 2048) << 10;
285 m_HeapSize = m_Config.GetInt("ScriptHeapSize", 1024) << 10;
286
287 m_log.InfoFormat("[XMREngine]: Enabled, {0}.{1} Meg (0x{2}) stacks",
288 (m_StackSize >> 20).ToString (),
289 (((m_StackSize % 0x100000) * 1000)
290 >> 20).ToString ("D3"),
291 m_StackSize.ToString ("X"));
292
293 m_log.InfoFormat("[XMREngine]: ... {0}.{1} Meg (0x{2}) heaps",
294 (m_HeapSize >> 20).ToString (),
295 (((m_HeapSize % 0x100000) * 1000)
296 >> 20).ToString ("D3"),
297 m_HeapSize.ToString ("X"));
298
299 m_MaintenanceInterval = m_Config.GetInt("MaintenanceInterval", 10);
300
301 if (m_MaintenanceInterval > 0)
302 {
303 m_MaintenanceTimer = new System.Timers.Timer(m_MaintenanceInterval * 60000);
304 m_MaintenanceTimer.Elapsed += DoMaintenance;
305 m_MaintenanceTimer.Start();
306 }
307
308 MainConsole.Instance.Commands.AddCommand("xmr", false,
309 "xmr",
310 "xmr [...|help|...] ...",
311 "Run xmr script engine commands",
312 RunTest);
313
314 TraceCalls("[XMREngine]: Initialize successful");
315 }
316
317 public void AddRegion(Scene scene)
318 {
319 if (!m_Enabled)
320 return;
321
322 TraceCalls("[XMREngine]: XMREngine.AddRegion({0})", scene.RegionInfo.RegionName);
323
324 m_Scene = scene;
325
326 m_Scene.RegisterModuleInterface<IScriptModule>(this);
327
328 m_ScriptBasePath = m_Config.GetString ("ScriptBasePath", "ScriptData");
329 m_ScriptBasePath = Path.Combine (m_ScriptBasePath, scene.RegionInfo.RegionID.ToString());
330
331 Directory.CreateDirectory(m_ScriptBasePath);
332
333 m_Scene.EventManager.OnRezScript += OnRezScript;
334
335 m_Scene.StackModuleInterface<IScriptModule>(this);
336 }
337
338 private void OneTimeLateInitialization ()
339 {
340 /*
341 * Build list of defined APIs and their 'this' types and define a field in XMRInstanceSuperType.
342 */
343 ApiManager am = new ApiManager ();
344 Dictionary<string,Type> apiCtxTypes = new Dictionary<string,Type> ();
345 foreach (string api in am.GetApis ()) {
346 m_log.Debug ("[XMREngine]: adding api " + api);
347 IScriptApi scriptApi = am.CreateApi (api);
348 Type apiCtxType = scriptApi.GetType ();
349 if (api == "LSL") apiCtxType = typeof (XMRLSL_Api);
350 apiCtxTypes[api] = apiCtxType;
351 }
352
353 if (ScriptCodeGen.xmrInstSuperType == null) // Only create type once!
354 {
355 /*
356 * Start creating type XMRInstanceSuperType that contains a field
357 * m_ApiManager_<APINAME> that points to the per-instance context
358 * struct for that API, ie, the 'this' value passed to all methods
359 * in that API. It is in essence:
360 *
361 * public class XMRInstanceSuperType : XMRInstance {
362 * public XMRLSL_Api m_ApiManager_LSL; // 'this' value for all ll...() functions
363 * public MOD_Api m_ApiManager_MOD; // 'this' value for all mod...() functions
364 * public OSSL_Api m_ApiManager_OSSL; // 'this' value for all os...() functions
365 * ....
366 * }
367 */
368 AssemblyName assemblyName = new AssemblyName ();
369 assemblyName.Name = "XMRInstanceSuperAssembly";
370 AssemblyBuilder assemblyBuilder = Thread.GetDomain ().DefineDynamicAssembly (assemblyName, AssemblyBuilderAccess.Run);
371 ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule ("XMRInstanceSuperModule");
372 TypeBuilder typeBuilder = moduleBuilder.DefineType ("XMRInstanceSuperType", TypeAttributes.Public | TypeAttributes.Class);
373 typeBuilder.SetParent (typeof (XMRInstance));
374
375 foreach (string apiname in apiCtxTypes.Keys)
376 {
377 string fieldName = "m_ApiManager_" + apiname;
378 typeBuilder.DefineField (fieldName, apiCtxTypes[apiname], FieldAttributes.Public);
379 }
380
381 /*
382 * Finalize definition of XMRInstanceSuperType.
383 * Give the compiler a short name to reference it by,
384 * otherwise it will try to use the AssemblyQualifiedName
385 * and fail miserably.
386 */
387 ScriptCodeGen.xmrInstSuperType = typeBuilder.CreateType ();
388 ScriptObjWriter.DefineInternalType ("xmrsuper", ScriptCodeGen.xmrInstSuperType);
389 }
390
391 /*
392 * Tell the compiler about all the constants and methods for each API.
393 * We also tell the compiler how to get the per-instance context for each API
394 * by reading the corresponding m_ApiManager_<APINAME> field of XMRInstanceSuperType.
395 */
396 foreach (KeyValuePair<string,Type> kvp in apiCtxTypes) {
397
398 // get API name and the corresponding per-instance context type
399 string api = kvp.Key;
400 Type apiCtxType = kvp.Value;
401
402 // give script compiler an abbreviated name for the API context type
403 ScriptObjWriter.DefineInternalType ("apimanager_" + api, apiCtxType);
404
405 // this field tells the compiled code where the per-instance API context object is
406 // eg, for the OSSL API, it is in ((XMRInstanceSuperType)inst).m_ApiManager_OSSL
407 string fieldName = "m_ApiManager_" + api;
408 FieldInfo fieldInfo = ScriptCodeGen.xmrInstSuperType.GetField (fieldName);
409 m_XMRInstanceApiCtxFieldInfos[api] = fieldInfo;
410
411 // now tell the compiler about the constants and methods for the API
412 ScriptConst.AddInterfaceConstants (null, apiCtxType.GetFields ());
413 TokenDeclInline.AddInterfaceMethods (null, apiCtxType.GetMethods (), fieldInfo);
414 }
415
416 /*
417 * Add sim-specific APIs to the compiler.
418 */
419 IScriptModuleComms comms = m_Scene.RequestModuleInterface<IScriptModuleComms> ();
420 if (comms != null) {
421
422 /*
423 * Add methods to list of built-in functions.
424 */
425 Delegate[] methods = comms.GetScriptInvocationList ();
426 foreach (Delegate m in methods) {
427 MethodInfo mi = m.Method;
428 try {
429 CommsCallCodeGen cccg = new CommsCallCodeGen (mi, comms, m_XMRInstanceApiCtxFieldInfos["MOD"]);
430 Verbose ("[XMREngine]: added comms function " + cccg.fullName);
431 } catch (Exception e) {
432 m_log.Error ("[XMREngine]: failed to add comms function " + mi.Name);
433 m_log.Error ("[XMREngine]: - " + e.ToString ());
434 }
435 }
436
437 /*
438 * Add constants to list of built-in constants.
439 */
440 Dictionary<string,object> consts = comms.GetConstants ();
441 foreach (KeyValuePair<string,object> kvp in consts) {
442 try {
443 ScriptConst sc = ScriptConst.AddConstant (kvp.Key, kvp.Value);
444 Verbose ("[XMREngine]: added comms constant " + sc.name);
445 } catch (Exception e) {
446 m_log.Error ("[XMREngine]: failed to add comms constant " + kvp.Key);
447 m_log.Error ("[XMREngine]: - " + e.Message);
448 }
449 }
450 } else {
451 Verbose ("[XMREngine]: comms not enabled");
452 }
453 }
454
455 /**
456 * @brief Generate code for the calls to the comms functions.
457 * It is a tRUlY EvIL interface.
458 * To call the function we must call an XMRInstanceSuperType.m_ApiManager_MOD.modInvoker?()
459 * method passing it the name of the function as a string and the script
460 * argument list wrapped up in an object[] array. The modInvoker?() methods
461 * do some sick type conversions (with corresponding mallocs) so we can't
462 * call the methods directly.
463 */
464 private class CommsCallCodeGen : TokenDeclInline {
465 private static Type[] modInvokerArgTypes = new Type[] { typeof (string), typeof (object[]) };
466 public static FieldInfo xmrInstModApiCtxField;
467
468 private MethodInfo modInvokerMeth;
469 private string methName;
470
471 /**
472 * @brief Constructor
473 * @param mi = method to make available to scripts
474 * mi.Name = name that is used by scripts
475 * mi.GetParameters() = parameter list as defined by module
476 * includes the 'UUID host','UUID script' parameters that script does not see
477 * allowed types for script-visible parameters are as follows:
478 * Single -> float
479 * Int32 -> integer
480 * OpenMetaverse.UUID -> key
481 * Object[] -> list
482 * OpenMetaverse.Quaternion -> rotation
483 * String -> string
484 * OpenMetaverse.Vector3 -> vector
485 * mi.ReturnType = return type as defined by module
486 * types are same as allowed for parameters
487 * @param comms = comms module the method came from
488 * @param apictxfi = what field in XMRInstanceSuperType the 'this' value is for this method
489 */
490 public CommsCallCodeGen (MethodInfo mi, IScriptModuleComms comms, FieldInfo apictxfi)
491 : base (null, false, NameArgSig (mi), RetType (mi))
492 {
493 methName = mi.Name;
494 string modInvokerName = comms.LookupModInvocation (methName);
495 if (modInvokerName == null) throw new Exception ("cannot find comms method " + methName);
496 modInvokerMeth = typeof (MOD_Api).GetMethod (modInvokerName, modInvokerArgTypes);
497 xmrInstModApiCtxField = apictxfi;
498 }
499
500 // script-visible name(argtype,...) signature string
501 private static string NameArgSig (MethodInfo mi)
502 {
503 StringBuilder sb = new StringBuilder ();
504 sb.Append (mi.Name);
505 sb.Append ('(');
506 ParameterInfo[] mps = mi.GetParameters ();
507 for (int i = 2; i < mps.Length; i ++) {
508 ParameterInfo pi = mps[i];
509 if (i > 2) sb.Append (',');
510 sb.Append (ParamType (pi.ParameterType));
511 }
512 sb.Append (')');
513 return sb.ToString ();
514 }
515
516 // script-visible return type
517 // note that although we support void, the comms stuff does not
518 private static TokenType RetType (MethodInfo mi)
519 {
520 Type rt = mi.ReturnType;
521 if (rt == typeof (float)) return new TokenTypeFloat (null);
522 if (rt == typeof (int)) return new TokenTypeInt (null);
523 if (rt == typeof (object[])) return new TokenTypeList (null);
524 if (rt == typeof (OpenMetaverse.UUID)) return new TokenTypeKey (null);
525 if (rt == typeof (OpenMetaverse.Quaternion)) return new TokenTypeRot (null);
526 if (rt == typeof (string)) return new TokenTypeStr (null);
527 if (rt == typeof (OpenMetaverse.Vector3)) return new TokenTypeVec (null);
528 if (rt == null || rt == typeof (void)) return new TokenTypeVoid (null);
529 throw new Exception ("unsupported return type " + rt.Name);
530 }
531
532 // script-visible parameter type
533 private static string ParamType (Type t)
534 {
535 if (t == typeof (float)) return "float";
536 if (t == typeof (int)) return "integer";
537 if (t == typeof (OpenMetaverse.UUID)) return "key";
538 if (t == typeof (object[])) return "list";
539 if (t == typeof (OpenMetaverse.Quaternion)) return "rotation";
540 if (t == typeof (string)) return "string";
541 if (t == typeof (OpenMetaverse.Vector3)) return "vector";
542 throw new Exception ("unsupported parameter type " + t.Name);
543 }
544
545 /**
546 * @brief Called by the compiler to generate a call to the comms function.
547 * @param scg = which script is being compiled
548 * @param errorAt = where in the source code the call is being made (for error messages)
549 * @param result = a temp location to put the return value in if any
550 * @param args = array of script-visible arguments being passed to the function
551 */
552 public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
553 {
554 /*
555 * Set up 'this' pointer for modInvoker?() = value from ApiManager.CreateApi("MOD").
556 */
557 scg.PushXMRInst ();
558 scg.ilGen.Emit (errorAt, OpCodes.Castclass, xmrInstModApiCtxField.DeclaringType);
559 scg.ilGen.Emit (errorAt, OpCodes.Ldfld, xmrInstModApiCtxField);
560
561 /*
562 * Set up 'fname' argument to modInvoker?() = name of the function to be called.
563 */
564 scg.ilGen.Emit (errorAt, OpCodes.Ldstr, methName);
565
566 /*
567 * Set up 'parms' argument to modInvoker?() = object[] of the script-visible parameters,
568 * in their LSL-wrapped form. Of course, the modInvoker?() method will malloc yet another
569 * object[] and type-convert these parameters one-by-one with another round of unwrapping
570 * and wrapping.
571 * Types allowed in this object[]:
572 * LSL_Float, LSL_Integer, LSL_Key, LSL_List, LSL_Rotation, LSL_String, LSL_Vector
573 */
574 int nargs = args.Length;
575 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, nargs);
576 scg.ilGen.Emit (errorAt, OpCodes.Newarr, typeof (object));
577
578 for (int i = 0; i < nargs; i ++) {
579 scg.ilGen.Emit (errorAt, OpCodes.Dup);
580 scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, i);
581
582 // get location and type of argument
583
584 CompValu arg = args[i];
585 TokenType argtype = arg.type;
586
587 // if already in a form acceptable to modInvoker?(),
588 // just push it to the stack and convert to object
589 // by boxing it if necessary
590
591 // but if something like a double, int, string, etc
592 // push to stack converting to the LSL-wrapped type
593 // then convert to object by boxing if necessary
594
595 Type boxit = null;
596 if (argtype is TokenTypeLSLFloat) {
597 args[i].PushVal (scg, errorAt);
598 boxit = typeof (LSL_Float);
599 } else if (argtype is TokenTypeLSLInt) {
600 args[i].PushVal (scg, errorAt);
601 boxit = typeof (LSL_Integer);
602 } else if (argtype is TokenTypeLSLKey) {
603 args[i].PushVal (scg, errorAt);
604 boxit = typeof (LSL_Key);
605 } else if (argtype is TokenTypeList) {
606 args[i].PushVal (scg, errorAt);
607 boxit = typeof (LSL_List);
608 } else if (argtype is TokenTypeRot) {
609 args[i].PushVal (scg, errorAt);
610 boxit = typeof (LSL_Rotation);
611 } else if (argtype is TokenTypeLSLString) {
612 args[i].PushVal (scg, errorAt);
613 boxit = typeof (LSL_String);
614 } else if (argtype is TokenTypeVec) {
615 args[i].PushVal (scg, errorAt);
616 boxit = typeof (LSL_Vector);
617 } else if (argtype is TokenTypeFloat) {
618 args[i].PushVal (scg, errorAt, new TokenTypeLSLFloat (argtype));
619 boxit = typeof (LSL_Float);
620 } else if (argtype is TokenTypeInt) {
621 args[i].PushVal (scg, errorAt, new TokenTypeLSLInt (argtype));
622 boxit = typeof (LSL_Integer);
623 } else if (argtype is TokenTypeKey) {
624 args[i].PushVal (scg, errorAt, new TokenTypeLSLKey (argtype));
625 boxit = typeof (LSL_Key);
626 } else if (argtype is TokenTypeStr) {
627 args[i].PushVal (scg, errorAt, new TokenTypeLSLString (argtype));
628 boxit = typeof (LSL_String);
629 } else {
630 throw new Exception ("unsupported arg type " + argtype.GetType ().Name);
631 }
632 if (boxit.IsValueType) {
633 scg.ilGen.Emit (errorAt, OpCodes.Box, boxit);
634 }
635
636 // pop the object into the object[]
637
638 scg.ilGen.Emit (errorAt, OpCodes.Stelem, typeof (object));
639 }
640
641 /*
642 * Call the modInvoker?() method.
643 * It leaves an LSL-wrapped type on the stack.
644 */
645 if (modInvokerMeth.IsVirtual) {
646 scg.ilGen.Emit (errorAt, OpCodes.Callvirt, modInvokerMeth);
647 } else {
648 scg.ilGen.Emit (errorAt, OpCodes.Call, modInvokerMeth);
649 }
650
651 /*
652 * The 3rd arg to Pop() is the type on the stack,
653 * ie, what modInvoker?() actually returns.
654 * The Pop() method will wrap/unwrap as needed.
655 */
656 Type retSysType = modInvokerMeth.ReturnType;
657 if (retSysType == null) retSysType = typeof (void);
658 TokenType retTokType = TokenType.FromSysType (errorAt, retSysType);
659 result.Pop (scg, errorAt, retTokType);
660 }
661 }
662
663 /**
664 * @brief Called late in shutdown procedure,
665 * after the 'Shutting down..." message.
666 */
667 public void RemoveRegion(Scene scene)
668 {
669 if (!m_Enabled)
670 return;
671
672 TraceCalls("[XMREngine]: XMREngine.RemoveRegion({0})", scene.RegionInfo.RegionName);
673
674 /*
675 * Write script states out to .state files so it will be
676 * available when the region is restarted.
677 */
678 DoMaintenance(null, null);
679
680 /*
681 * Stop executing script threads and wait for final
682 * one to finish (ie, script gets to CheckRun() call).
683 */
684 m_Exiting = true;
685 for (int i = 0; i < numThreadScriptWorkers; i ++) {
686 XMRScriptThread scriptThread = m_ScriptThreads[i];
687 if (scriptThread != null) {
688 scriptThread.Terminate();
689 m_ScriptThreads[i] = null;
690 }
691 }
692 if (m_SleepThread != null) {
693 lock (m_SleepQueue) {
694 Monitor.PulseAll (m_SleepQueue);
695 }
696 m_SleepThread.Join();
697 m_SleepThread = null;
698 }
699 if (m_SliceThread != null) {
700 m_SliceThread.Join();
701 m_SliceThread = null;
702 }
703
704 m_Scene.EventManager.OnFrame -= OnFrame;
705 m_Scene.EventManager.OnRezScript -= OnRezScript;
706 m_Scene.EventManager.OnRemoveScript -= OnRemoveScript;
707 m_Scene.EventManager.OnScriptReset -= OnScriptReset;
708 m_Scene.EventManager.OnStartScript -= OnStartScript;
709 m_Scene.EventManager.OnStopScript -= OnStopScript;
710 m_Scene.EventManager.OnGetScriptRunning -= OnGetScriptRunning;
711 m_Scene.EventManager.OnShutdown -= OnShutdown;
712
713 m_Enabled = false;
714 m_Scene = null;
715 }
716
717 public void RegionLoaded(Scene scene)
718 {
719 if (!m_Enabled)
720 return;
721
722 TraceCalls("[XMREngine]: XMREngine.RegionLoaded({0})", scene.RegionInfo.RegionName);
723
724 m_Scene.EventManager.OnFrame += OnFrame;
725 m_Scene.EventManager.OnRemoveScript += OnRemoveScript;
726 m_Scene.EventManager.OnScriptReset += OnScriptReset;
727 m_Scene.EventManager.OnStartScript += OnStartScript;
728 m_Scene.EventManager.OnStopScript += OnStopScript;
729 m_Scene.EventManager.OnGetScriptRunning += OnGetScriptRunning;
730 m_Scene.EventManager.OnShutdown += OnShutdown;
731
732 InitEvents();
733 }
734
735 public void StartProcessing()
736 {
737 m_log.Debug ("[XMREngine]: StartProcessing entry");
738 m_Scene.EventManager.TriggerEmptyScriptCompileQueue (0, "");
739 m_StartProcessing = true;
740 for (int i = 0; i < numThreadScriptWorkers; i ++) {
741 XMRScriptThread.WakeUpOne();
742 }
743 m_log.Debug ("[XMREngine]: StartProcessing return");
744 }
745
746 public void Close()
747 {
748 TraceCalls("[XMREngine]: XMREngine.Close()");
749 }
750
751 private void RunTest (string module, string[] args)
752 {
753 if (args.Length < 2) {
754 m_log.Info ("[XMREngine]: missing command, try 'xmr help'");
755 return;
756 }
757
758 switch (args[1]) {
759 case "cvv": {
760 switch (args.Length) {
761 case 2: {
762 m_log.InfoFormat ("[XMREngine]: compiled version value = {0}",
763 ScriptCodeGen.COMPILED_VERSION_VALUE);
764 break;
765 }
766 case 3: {
767 try {
768 ScriptCodeGen.COMPILED_VERSION_VALUE = Convert.ToInt32 (args[2]);
769 } catch {
770 m_log.Error ("[XMREngine]: bad/missing version number");
771 }
772 break;
773 }
774 default: {
775 m_log.Error ("[XMREngine]: xmr cvv [<new_compiled_version_value>]");
776 break;
777 }
778 }
779 break;
780 }
781 case "echo": {
782 for (int i = 0; i < args.Length; i ++) {
783 m_log.Info ("[XMREngine]: echo[" + i + "]=<" + args[i] + ">");
784 }
785 break;
786 }
787 case "gc": {
788 GC.Collect();
789 break;
790 }
791 case "help":
792 case "?": {
793 m_log.Info ("[XMREngine]: xmr cvv [<newvalue>] - show/set compiled version value");
794 m_log.Info ("[XMREngine]: xmr gc");
795 m_log.Info ("[XMREngine]: xmr ls [-help ...]");
796 m_log.Info ("[XMREngine]: xmr mvv [<newvalue>] - show/set migration version value");
797 m_log.Info ("[XMREngine]: xmr pev [-help ...] - post event");
798 m_log.Info ("[XMREngine]: xmr reset [-help ...]");
799 m_log.Info ("[XMREngine]: xmr resume - resume script processing");
800 m_log.Info ("[XMREngine]: xmr suspend - suspend script processing");
801 m_log.Info ("[XMREngine]: xmr tracecalls [yes | no]");
802 m_log.Info ("[XMREngine]: xmr verbose [yes | no]");
803 break;
804 }
805 case "ls": {
806 XmrTestLs (args, 2);
807 break;
808 }
809 case "mvv": {
810 switch (args.Length) {
811 case 2: {
812 m_log.InfoFormat ("[XMREngine]: migration version value = {0}",
813 XMRInstance.migrationVersion);
814 break;
815 }
816 case 3: {
817 try {
818 int mvv = Convert.ToInt32 (args[2]);
819 if ((mvv < 0) || (mvv > 255)) throw new Exception ("out of range");
820 XMRInstance.migrationVersion = (byte) mvv;
821 } catch (Exception e) {
822 m_log.Error ("[XMREngine]: bad/missing version number (" + e.Message + ")");
823 }
824 break;
825 }
826 default: {
827 m_log.Error ("[XMREngine]: xmr mvv [<new_migration_version_value>]");
828 break;
829 }
830 }
831 break;
832 }
833 case "pev": {
834 XmrTestPev (args, 2);
835 break;
836 }
837 case "reset": {
838 XmrTestReset (args, 2);
839 break;
840 }
841 case "resume": {
842 m_log.Info ("[XMREngine]: resuming scripts");
843 for (int i = 0; i < numThreadScriptWorkers; i ++) {
844 m_ScriptThreads[i].ResumeThread();
845 }
846 break;
847 }
848 case "suspend": {
849 m_log.Info ("[XMREngine]: suspending scripts");
850 for (int i = 0; i < numThreadScriptWorkers; i ++) {
851 m_ScriptThreads[i].SuspendThread();
852 }
853 break;
854 }
855 case "tracecalls": {
856 if (args.Length > 2) {
857 m_TraceCalls = (args[2][0] & 1) != 0;
858 }
859 m_log.Info ("[XMREngine]: tracecalls " + (m_TraceCalls ? "yes" : "no"));
860 break;
861 }
862 case "verbose": {
863 if (args.Length > 2) {
864 m_Verbose = (args[2][0] & 1) != 0;
865 }
866 m_log.Info ("[XMREngine]: verbose " + (m_Verbose ? "yes" : "no"));
867 break;
868 }
869 default: {
870 m_log.Error ("[XMREngine]: unknown command " + args[1] + ", try 'xmr help'");
871 break;
872 }
873 }
874 }
875
876 // Not required when not using IScriptInstance
877 //
878 public IScriptWorkItem QueueEventHandler(object parms)
879 {
880 return null;
881 }
882
883 public Scene World
884 {
885 get { return m_Scene; }
886 }
887
888 public IScriptModule ScriptModule
889 {
890 get { return this; }
891 }
892
893 public void SaveAllState()
894 {
895 m_log.Error("[XMREngine]: XMREngine.SaveAllState() called!!");
896 }
897
898 public event ScriptRemoved OnScriptRemoved;
899 public event ObjectRemoved OnObjectRemoved;
900
901 // Events targeted at a specific script
902 // ... like listen() for an llListen() call
903 //
904 public bool PostScriptEvent(UUID itemID, EventParams parms)
905 {
906 XMRInstance instance = GetInstance (itemID);
907 if (instance == null) return false;
908
909 TraceCalls("[XMREngine]: XMREngine.PostScriptEvent({0},{1})", itemID.ToString(), parms.EventName);
910
911 instance.PostEvent(parms);
912 return true;
913 }
914
915 // Events targeted at all scripts in the given prim.
916 // localID = which prim
917 // parms = event to post
918 //
919 public bool PostObjectEvent (uint localID, EventParams parms)
920 {
921 SceneObjectPart part = m_Scene.GetSceneObjectPart(localID);
922
923 if (part == null)
924 return false;
925
926 TraceCalls("[XMREngine]: XMREngine.PostObjectEvent({0},{1})", localID.ToString(), parms.EventName);
927
928 /*
929 * In SecondLife, attach events go to all scripts of all prims
930 * in a linked object. So here we duplicate that functionality,
931 * as all we ever get is a single attach event for the whole
932 * object.
933 */
934 if (parms.EventName == "attach") {
935 bool posted = false;
936 foreach (SceneObjectPart primpart in part.ParentGroup.Parts) {
937 posted |= PostPrimEvent (primpart, parms);
938 }
939 return posted;
940 }
941
942 /*
943 * Other events go to just the scripts in that prim.
944 */
945 return PostPrimEvent (part, parms);
946 }
947
948 private bool PostPrimEvent (SceneObjectPart part, EventParams parms)
949 {
950 UUID partUUID = part.UUID;
951
952 /*
953 * Get list of script instances running in the object.
954 */
955 XMRInstance[] objInstArray;
956 lock (m_InstancesDict) {
957 if (!m_ObjectInstArray.TryGetValue (partUUID, out objInstArray)) {
958 return false;
959 }
960 if (objInstArray == null) {
961 objInstArray = RebuildObjectInstArray (partUUID);
962 m_ObjectInstArray[partUUID] = objInstArray;
963 }
964 }
965
966 /*
967 * Post event to all script instances in the object.
968 */
969 if (objInstArray.Length <= 0) return false;
970 foreach (XMRInstance inst in objInstArray) {
971 inst.PostEvent (parms);
972 }
973 return true;
974 }
975
976 public DetectParams GetDetectParams(UUID itemID, int number)
977 {
978 XMRInstance instance = GetInstance (itemID);
979 if (instance == null) return null;
980 return instance.GetDetectParams(number);
981 }
982
983 public void SetMinEventDelay(UUID itemID, double delay)
984 {
985 }
986
987 public int GetStartParameter(UUID itemID)
988 {
989 XMRInstance instance = GetInstance (itemID);
990 if (instance == null) return 0;
991 return instance.StartParam;
992 }
993
994 // This is the "set running" method
995 //
996 public void SetScriptState(UUID itemID, bool state, bool self)
997 {
998 SetScriptState (itemID, state);
999 }
1000 public void SetScriptState(UUID itemID, bool state)
1001 {
1002 XMRInstance instance = GetInstance (itemID);
1003 if (instance != null) {
1004 instance.Running = state;
1005 }
1006 }
1007
1008 // Control display of the "running" checkbox
1009 //
1010 public bool GetScriptState(UUID itemID)
1011 {
1012 XMRInstance instance = GetInstance (itemID);
1013 if (instance == null) return false;
1014 return instance.Running;
1015 }
1016
1017 public void SetState(UUID itemID, string newState)
1018 {
1019 TraceCalls("[XMREngine]: XMREngine.SetState({0},{1})", itemID.ToString(), newState);
1020 }
1021
1022 public void ApiResetScript(UUID itemID)
1023 {
1024 XMRInstance instance = GetInstance (itemID);
1025 if (instance != null) {
1026 instance.ApiReset();
1027 }
1028 }
1029
1030 public void ResetScript(UUID itemID)
1031 {
1032 XMRInstance instance = GetInstance (itemID);
1033 if (instance != null) {
1034 IUrlModule urlModule = m_Scene.RequestModuleInterface<IUrlModule>();
1035 if (urlModule != null)
1036 urlModule.ScriptRemoved(itemID);
1037
1038 instance.Reset();
1039 }
1040 }
1041
1042 public IConfig Config
1043 {
1044 get { return m_Config; }
1045 }
1046
1047 public IConfigSource ConfigSource
1048 {
1049 get { return m_ConfigSource; }
1050 }
1051
1052 public string ScriptEngineName
1053 {
1054 get { return "XMREngine"; }
1055 }
1056
1057 public IScriptApi GetApi(UUID itemID, string name)
1058 {
1059 FieldInfo fi;
1060 if (!m_XMRInstanceApiCtxFieldInfos.TryGetValue (name, out fi)) return null;
1061 XMRInstance inst = GetInstance (itemID);
1062 if (inst == null) return null;
1063 return (IScriptApi)fi.GetValue (inst);
1064 }
1065
1066 /**
1067 * @brief Get script's current state as an XML string
1068 * - called by "Take", "Take Copy" and when object deleted (ie, moved to Trash)
1069 * This includes the .state file
1070 */
1071 public string GetXMLState(UUID itemID)
1072 {
1073 XMRInstance instance = GetInstance (itemID);
1074 if (instance == null) return String.Empty;
1075
1076 TraceCalls("[XMREngine]: XMREngine.GetXMLState({0})", itemID.ToString());
1077
1078 if (!instance.m_HasRun) return String.Empty;
1079
1080 XmlDocument doc = new XmlDocument();
1081
1082 /*
1083 * Set up <State Engine="XMREngine" UUID="itemID" Asset="assetID"> tag.
1084 */
1085 XmlElement stateN = doc.CreateElement("", "State", "");
1086 doc.AppendChild(stateN);
1087
1088 XmlAttribute engineA = doc.CreateAttribute("", "Engine", "");
1089 engineA.Value = ScriptEngineName;
1090 stateN.Attributes.Append(engineA);
1091
1092 XmlAttribute uuidA = doc.CreateAttribute("", "UUID", "");
1093 uuidA.Value = itemID.ToString();
1094 stateN.Attributes.Append(uuidA);
1095
1096 XmlAttribute assetA = doc.CreateAttribute("", "Asset", "");
1097 string assetID = instance.AssetID.ToString();
1098 assetA.Value = assetID;
1099 stateN.Attributes.Append(assetA);
1100
1101 /*
1102 * Get <ScriptState>...</ScriptState> item that hold's script's state.
1103 * This suspends the script if necessary then takes a snapshot.
1104 */
1105 XmlElement scriptStateN = instance.GetExecutionState(doc);
1106 stateN.AppendChild(scriptStateN);
1107
1108 return doc.OuterXml;
1109 }
1110
1111 // Set script's current state from an XML string
1112 // - called just before a script is instantiated
1113 // So we write the .state file so the .state file will be seen when
1114 // the script is instantiated.
1115 public bool SetXMLState(UUID itemID, string xml)
1116 {
1117 XmlDocument doc = new XmlDocument();
1118
1119 try
1120 {
1121 doc.LoadXml(xml);
1122 }
1123 catch
1124 {
1125 return false;
1126 }
1127 TraceCalls("[XMREngine]: XMREngine.SetXMLState({0})", itemID.ToString());
1128
1129 // Make sure <State Engine="XMREngine"> so we know it is in our
1130 // format.
1131 XmlElement stateN = (XmlElement)doc.SelectSingleNode("State");
1132 if (stateN == null)
1133 return false;
1134
1135 if (stateN.GetAttribute("Engine") != ScriptEngineName)
1136 return false;
1137
1138 // <ScriptState>...</ScriptState> contains contents of .state file.
1139 XmlElement scriptStateN = (XmlElement)stateN.SelectSingleNode("ScriptState");
1140 if (scriptStateN == null) {
1141 return false;
1142 }
1143 string sen = stateN.GetAttribute("Engine");
1144 if ((sen == null) || (sen != ScriptEngineName)) {
1145 return false;
1146 }
1147
1148 XmlAttribute assetA = doc.CreateAttribute("", "Asset", "");
1149 assetA.Value = stateN.GetAttribute("Asset");
1150 scriptStateN.Attributes.Append(assetA);
1151
1152 // Write out the .state file with the <ScriptState ...>...</ScriptState> XML text
1153 string statePath = XMRInstance.GetStateFileName(m_ScriptBasePath, itemID);
1154 FileStream ss = File.Create(statePath);
1155 StreamWriter sw = new StreamWriter(ss);
1156 sw.Write(scriptStateN.OuterXml);
1157 sw.Close();
1158 ss.Close();
1159
1160 return true;
1161 }
1162
1163 public bool PostScriptEvent(UUID itemID, string name, Object[] p)
1164 {
1165 if (!m_Enabled)
1166 return false;
1167
1168 TraceCalls("[XMREngine]: XMREngine.PostScriptEvent({0},{1})", itemID.ToString(), name);
1169
1170 return PostScriptEvent(itemID, new EventParams(name, p, zeroDetectParams));
1171 }
1172
1173 public bool PostObjectEvent(UUID itemID, string name, Object[] p)
1174 {
1175 if (!m_Enabled)
1176 return false;
1177
1178 TraceCalls("[XMREngine]: XMREngine.PostObjectEvent({0},{1})", itemID.ToString(), name);
1179
1180 SceneObjectPart part = m_Scene.GetSceneObjectPart(itemID);
1181 if (part == null)
1182 return false;
1183
1184 return PostObjectEvent(part.LocalId, new EventParams(name, p, zeroDetectParams));
1185 }
1186
1187 // about the 3523rd entrypoint for a script to put itself to sleep
1188 public void SleepScript(UUID itemID, int delay)
1189 {
1190 XMRInstance instance = GetInstance (itemID);
1191 if (instance != null) {
1192 instance.Sleep (delay);
1193 }
1194 }
1195
1196 // Get a script instance loaded, compiling it if necessary
1197 //
1198 // localID = the object as a whole, may contain many scripts
1199 // itemID = this instance of the script in this object
1200 // script = script source code
1201 // startParam = value passed to 'on_rez' event handler
1202 // postOnRez = true to post an 'on_rez' event to script on load
1203 // defEngine = default script engine
1204 // stateSource = post this event to script on load
1205
1206 public void OnRezScript(uint localID, UUID itemID, string script,
1207 int startParam, bool postOnRez, string defEngine, int stateSource)
1208 {
1209 SceneObjectPart part = m_Scene.GetSceneObjectPart(localID);
1210 TaskInventoryItem item = part.Inventory.GetInventoryItem(itemID);
1211
1212 if (!m_LateInit) {
1213 m_LateInit = true;
1214 OneTimeLateInitialization ();
1215 }
1216
1217 TraceCalls("[XMREngine]: OnRezScript(...,{0},...)", itemID.ToString());
1218
1219 /*
1220 * Assume script uses the default engine, whatever that is.
1221 */
1222 string engineName = defEngine;
1223
1224 /*
1225 * Very first line might contain "//" scriptengine ":".
1226 */
1227 string firstline = "";
1228 if (script.StartsWith("//")) {
1229 int lineEnd = script.IndexOf('\n');
1230 if (lineEnd > 1) firstline = script.Substring(0, lineEnd).Trim();
1231 int colon = firstline.IndexOf(':');
1232 if (colon >= 2) {
1233 engineName = firstline.Substring(2, colon - 2).Trim();
1234 if (engineName == "") engineName = defEngine;
1235 }
1236 }
1237
1238 /*
1239 * Make sure the default or requested engine is us.
1240 */
1241 if (engineName != ScriptEngineName) {
1242
1243 /*
1244 * Not us, if requested engine exists, silently ignore script and let
1245 * requested engine handle it.
1246 */
1247 IScriptModule[] engines = m_Scene.RequestModuleInterfaces<IScriptModule> ();
1248 foreach (IScriptModule eng in engines) {
1249 if (eng.ScriptEngineName == engineName) {
1250 return;
1251 }
1252 }
1253
1254 /*
1255 * Requested engine not defined, warn on console.
1256 * Then we try to handle it if we're the default engine, else we ignore it.
1257 */
1258 m_log.Warn ("[XMREngine]: " + itemID.ToString() + " requests undefined/disabled engine " + engineName);
1259 m_log.Info ("[XMREngine]: - " + part.GetWorldPosition ());
1260 m_log.Info ("[XMREngine]: first line: " + firstline);
1261 if (defEngine != ScriptEngineName) {
1262 m_log.Info ("[XMREngine]: leaving it to the default script engine (" + defEngine + ") to process it");
1263 return;
1264 }
1265 m_log.Info ("[XMREngine]: will attempt to processing it anyway as default script engine");
1266 }
1267
1268 /*
1269 * Put on object/instance lists.
1270 */
1271 XMRInstance instance = (XMRInstance)Activator.CreateInstance (ScriptCodeGen.xmrInstSuperType);
1272 instance.m_LocalID = localID;
1273 instance.m_ItemID = itemID;
1274 instance.m_SourceCode = script;
1275 instance.m_StartParam = startParam;
1276 instance.m_PostOnRez = postOnRez;
1277 instance.m_StateSource = (StateSource)stateSource;
1278 instance.m_Part = part;
1279 instance.m_PartUUID = part.UUID;
1280 instance.m_Item = item;
1281 instance.m_DescName = part.Name + ":" + item.Name;
1282 instance.m_IState = XMRInstState.CONSTRUCT;
1283
1284 lock (m_InstancesDict) {
1285 m_LockedDict = "RegisterInstance";
1286
1287 // Insert on internal list of all scripts being handled by this engine instance.
1288 m_InstancesDict[instance.m_ItemID] = instance;
1289
1290 // Insert on internal list of all scripts being handled by this engine instance
1291 // that are part of the object.
1292 List<UUID> itemIDList;
1293 if (!m_ObjectItemList.TryGetValue(instance.m_PartUUID, out itemIDList)) {
1294 itemIDList = new List<UUID>();
1295 m_ObjectItemList[instance.m_PartUUID] = itemIDList;
1296 }
1297 if (!itemIDList.Contains(instance.m_ItemID)) {
1298 itemIDList.Add(instance.m_ItemID);
1299 m_ObjectInstArray[instance.m_PartUUID] = null;
1300 }
1301
1302 m_LockedDict = "~RegisterInstance";
1303 }
1304
1305 /*
1306 * Compile and load it.
1307 */
1308 lock (m_ScriptErrors) {
1309 m_ScriptErrors.Remove (instance.m_ItemID);
1310 }
1311 LoadThreadWork (instance);
1312 }
1313
1314 /**
1315 * @brief This routine instantiates one script.
1316 */
1317 private void LoadThreadWork (XMRInstance instance)
1318 {
1319 /*
1320 * Compile and load the script in memory.
1321 */
1322 ArrayList errors = new ArrayList();
1323 Exception initerr = null;
1324 try {
1325 instance.Initialize(this, m_ScriptBasePath, m_StackSize, m_HeapSize, errors);
1326 } catch (Exception e1) {
1327 initerr = e1;
1328 }
1329 if ((initerr != null) && !instance.m_ForceRecomp) {
1330 UUID itemID = instance.m_ItemID;
1331 Verbose ("[XMREngine]: {0}/{2} first load failed ({1}), retrying after recompile",
1332 itemID.ToString(), initerr.Message, instance.m_Item.AssetID.ToString());
1333 Verbose ("[XMREngine]:\n{0}", initerr.ToString ());
1334 initerr = null;
1335 errors = new ArrayList();
1336 instance.m_ForceRecomp = true;
1337 try {
1338 instance.Initialize(this, m_ScriptBasePath, m_StackSize, m_HeapSize, errors);
1339 } catch (Exception e2) {
1340 initerr = e2;
1341 }
1342 }
1343 if (initerr != null) {
1344 UUID itemID = instance.m_ItemID;
1345 Verbose ("[XMREngine]: Error starting script {0}/{2}: {1}",
1346 itemID.ToString(), initerr.Message, instance.m_Item.AssetID.ToString());
1347 if (initerr.Message != "compilation errors") {
1348 Verbose ("[XMREngine]: - " + instance.m_Part.GetWorldPosition () + " " + instance.m_DescName);
1349 Verbose ("[XMREngine]: exception:\n{0}", initerr.ToString());
1350 }
1351
1352 OnRemoveScript (0, itemID);
1353
1354 /*
1355 * Post errors where GetScriptErrors() can see them.
1356 */
1357 if (errors.Count == 0) {
1358 errors.Add(initerr.Message);
1359 } else {
1360 foreach (Object err in errors) {
1361 if (m_ScriptDebug)
1362 m_log.DebugFormat ("[XMREngine]: {0}", err.ToString());
1363 }
1364 }
1365 lock (m_ScriptErrors) {
1366 m_ScriptErrors[instance.m_ItemID] = errors;
1367 }
1368
1369 return;
1370 }
1371
1372 /*
1373 * Tell GetScriptErrors() that we have finished compiling/loading
1374 * successfully (by posting a 0 element array).
1375 */
1376 lock (m_ScriptErrors) {
1377 if (instance.m_IState != XMRInstState.CONSTRUCT) throw new Exception("bad state");
1378 m_ScriptErrors[instance.m_ItemID] = noScriptErrors;
1379 }
1380
1381 /*
1382 * Transition from CONSTRUCT->ONSTARTQ and give to RunScriptThread().
1383 * Put it on the start queue so it will run any queued event handlers,
1384 * such as state_entry() or on_rez(). If there aren't any queued, it
1385 * will just go to idle state when RunOne() tries to dequeue an event.
1386 */
1387 lock (instance.m_QueueLock) {
1388 if (instance.m_IState != XMRInstState.CONSTRUCT) throw new Exception("bad state");
1389 instance.m_IState = XMRInstState.ONSTARTQ;
1390 if (!instance.m_Running) {
1391 instance.EmptyEventQueues ();
1392 }
1393 }
1394 QueueToStart(instance);
1395 }
1396
1397 public void OnRemoveScript(uint localID, UUID itemID)
1398 {
1399 TraceCalls("[XMREngine]: OnRemoveScript(...,{0})", itemID.ToString());
1400
1401 /*
1402 * Remove from our list of known scripts.
1403 * After this, no more events can queue because we won't be
1404 * able to translate the itemID to an XMRInstance pointer.
1405 */
1406 XMRInstance instance = null;
1407 lock (m_InstancesDict)
1408 {
1409 m_LockedDict = "OnRemoveScript:" + itemID.ToString();
1410
1411 /*
1412 * Tell the instance to free off everything it can.
1413 */
1414 if (!m_InstancesDict.TryGetValue(itemID, out instance))
1415 {
1416 m_LockedDict = "~OnRemoveScript";
1417 return;
1418 }
1419
1420 /*
1421 * Tell it to stop executing anything.
1422 */
1423 instance.suspendOnCheckRunHold = true;
1424
1425 /*
1426 * Remove it from our list of known script instances
1427 * mostly so no more events can queue to it.
1428 */
1429 m_InstancesDict.Remove(itemID);
1430
1431 List<UUID> itemIDList;
1432 if (m_ObjectItemList.TryGetValue (instance.m_PartUUID, out itemIDList)) {
1433 itemIDList.Remove(itemID);
1434 if (itemIDList.Count == 0) {
1435 m_ObjectItemList.Remove(instance.m_PartUUID);
1436 m_ObjectInstArray.Remove(instance.m_PartUUID);
1437 } else {
1438 m_ObjectInstArray[instance.m_PartUUID] = null;
1439 }
1440 }
1441
1442 /*
1443 * Delete the .state file as any needed contents were fetched with GetXMLState()
1444 * and stored on the database server.
1445 */
1446 string stateFileName = XMRInstance.GetStateFileName(m_ScriptBasePath, itemID);
1447 File.Delete(stateFileName);
1448
1449 ScriptRemoved handlerScriptRemoved = OnScriptRemoved;
1450 if (handlerScriptRemoved != null) {
1451 handlerScriptRemoved(itemID);
1452 }
1453
1454 m_LockedDict = "~~OnRemoveScript";
1455 }
1456
1457 /*
1458 * Free off its stack and fun things like that.
1459 * If it is running, abort it.
1460 */
1461 instance.Dispose ();
1462 }
1463
1464 public void OnScriptReset(uint localID, UUID itemID)
1465 {
1466 TraceCalls("[XMREngine]: XMREngine.OnScriptReset({0},{1})", localID.ToString(), itemID.ToString());
1467 ResetScript(itemID);
1468 }
1469
1470 public void OnStartScript(uint localID, UUID itemID)
1471 {
1472 XMRInstance instance = GetInstance (itemID);
1473 if (instance != null) {
1474 instance.Running = true;
1475 }
1476 }
1477
1478 public void OnStopScript(uint localID, UUID itemID)
1479 {
1480 XMRInstance instance = GetInstance (itemID);
1481 if (instance != null) {
1482 instance.Running = false;
1483 }
1484 }
1485
1486 public void OnGetScriptRunning(IClientAPI controllingClient,
1487 UUID objectID, UUID itemID)
1488 {
1489 XMRInstance instance = GetInstance (itemID);
1490 if (instance != null) {
1491 TraceCalls("[XMREngine]: XMREngine.OnGetScriptRunning({0},{1})", objectID.ToString(), itemID.ToString());
1492
1493 IEventQueue eq = World.RequestModuleInterface<IEventQueue>();
1494 if (eq == null) {
1495 controllingClient.SendScriptRunningReply(objectID, itemID,
1496 instance.Running);
1497 } else {
1498 eq.Enqueue(EventQueueHelper.ScriptRunningReplyEvent(objectID,
1499 itemID, instance.Running, true),
1500 controllingClient.AgentId);
1501 }
1502 }
1503 }
1504
1505 public bool HasScript(UUID itemID, out bool running)
1506 {
1507 XMRInstance instance = GetInstance (itemID);
1508 if (instance == null) {
1509 running = true;
1510 return false;
1511 }
1512 running = instance.Running;
1513 return true;
1514 }
1515
1516 /**
1517 * @brief Called once per frame update to see if scripts have
1518 * any such work to do.
1519 */
1520 private void OnFrame ()
1521 {
1522 if (m_FrameUpdateList != null) {
1523 ThreadStart frameupdates;
1524 lock (m_FrameUpdateLock) {
1525 frameupdates = m_FrameUpdateList;
1526 m_FrameUpdateList = null;
1527 }
1528 frameupdates ();
1529 }
1530 }
1531
1532 /**
1533 * @brief Add a one-shot delegate to list of things to do
1534 * synchronized with frame updates.
1535 */
1536 public void AddOnFrameUpdate (ThreadStart thunk)
1537 {
1538 lock (m_FrameUpdateLock) {
1539 m_FrameUpdateList += thunk;
1540 }
1541 }
1542
1543 /**
1544 * @brief Gets called early as part of shutdown,
1545 * right after "Persisting changed objects" message.
1546 */
1547 public void OnShutdown()
1548 {
1549 TraceCalls("[XMREngine]: XMREngine.OnShutdown()");
1550 }
1551
1552 /**
1553 * @brief Queue an instance to the StartQueue so it will run.
1554 * This queue is used for instances that have just had
1555 * an event queued to them when they were previously
1556 * idle. It must only be called by the thread that
1557 * transitioned the thread to XMRInstState.ONSTARTQ so
1558 * we don't get two threads trying to queue the same
1559 * instance to the m_StartQueue at the same time.
1560 */
1561 public void QueueToStart(XMRInstance inst)
1562 {
1563 lock (m_StartQueue) {
1564 if (inst.m_IState != XMRInstState.ONSTARTQ) throw new Exception("bad state");
1565 m_StartQueue.InsertTail(inst);
1566 }
1567 XMRScriptThread.WakeUpOne();
1568 }
1569
1570 /**
1571 * @brief A script may be sleeping, in which case we wake it.
1572 */
1573 public void WakeFromSleep(XMRInstance inst)
1574 {
1575 /*
1576 * Remove from sleep queue unless someone else already woke it.
1577 */
1578 lock (m_SleepQueue) {
1579 if (inst.m_IState != XMRInstState.ONSLEEPQ) {
1580 return;
1581 }
1582 m_SleepQueue.Remove(inst);
1583 inst.m_IState = XMRInstState.REMDFROMSLPQ;
1584 }
1585
1586 /*
1587 * Put on end of list of scripts that are ready to run.
1588 */
1589 lock (m_YieldQueue) {
1590 inst.m_IState = XMRInstState.ONYIELDQ;
1591 m_YieldQueue.InsertTail(inst);
1592 }
1593
1594 /*
1595 * Make sure the OS thread is running so it will see the script.
1596 */
1597 XMRScriptThread.WakeUpOne();
1598 }
1599
1600 /**
1601 * @brief An instance has just finished running for now,
1602 * figure out what to do with it next.
1603 * @param inst = instance in question, not on any queue at the moment
1604 * @param newIState = its new state
1605 * @returns with instance inserted onto proper queue (if any)
1606 */
1607 public void HandleNewIState(XMRInstance inst, XMRInstState newIState)
1608 {
1609 /*
1610 * RunOne() should have left the instance in RUNNING state.
1611 */
1612 if (inst.m_IState != XMRInstState.RUNNING) throw new Exception("bad state");
1613
1614 /*
1615 * Now see what RunOne() wants us to do with the instance next.
1616 */
1617 switch (newIState) {
1618
1619 /*
1620 * Instance has set m_SleepUntil to when it wants to sleep until.
1621 * So insert instance in sleep queue by ascending wake time.
1622 * Then wake the timer thread if this is the new first entry
1623 * so it will reset its timer.
1624 */
1625 case XMRInstState.ONSLEEPQ: {
1626 lock (m_SleepQueue) {
1627 XMRInstance after;
1628
1629 inst.m_IState = XMRInstState.ONSLEEPQ;
1630 for (after = m_SleepQueue.PeekHead(); after != null; after = after.m_NextInst) {
1631 if (after.m_SleepUntil > inst.m_SleepUntil) break;
1632 }
1633 m_SleepQueue.InsertBefore(inst, after);
1634 if (m_SleepQueue.PeekHead() == inst) {
1635 Monitor.Pulse (m_SleepQueue);
1636 }
1637 }
1638 break;
1639 }
1640
1641 /*
1642 * Instance just took a long time to run and got wacked by the
1643 * slicer. So put on end of yield queue to let someone else
1644 * run. If there is no one else, it will run again right away.
1645 */
1646 case XMRInstState.ONYIELDQ: {
1647 lock (m_YieldQueue) {
1648 inst.m_IState = XMRInstState.ONYIELDQ;
1649 m_YieldQueue.InsertTail(inst);
1650 }
1651 break;
1652 }
1653
1654 /*
1655 * Instance finished executing an event handler. So if there is
1656 * another event queued for it, put it on the start queue so it
1657 * will process the new event. Otherwise, mark it idle and the
1658 * next event to queue to it will start it up.
1659 */
1660 case XMRInstState.FINISHED: {
1661 Monitor.Enter(inst.m_QueueLock);
1662 if (!inst.m_Suspended && (inst.m_EventQueue.Count > 0)) {
1663 Monitor.Exit(inst.m_QueueLock);
1664 lock (m_StartQueue) {
1665 inst.m_IState = XMRInstState.ONSTARTQ;
1666 m_StartQueue.InsertTail (inst);
1667 }
1668 } else {
1669 inst.m_IState = XMRInstState.IDLE;
1670 Monitor.Exit(inst.m_QueueLock);
1671 }
1672 break;
1673 }
1674
1675 /*
1676 * Its m_SuspendCount > 0.
1677 * Don't put it on any queue and it won't run.
1678 * Since it's not IDLE, even queuing an event won't start it.
1679 */
1680 case XMRInstState.SUSPENDED: {
1681 inst.m_IState = XMRInstState.SUSPENDED;
1682 break;
1683 }
1684
1685 /*
1686 * It has been disposed of.
1687 * Just set the new state and all refs should theoretically drop off
1688 * as the instance is no longer in any list.
1689 */
1690 case XMRInstState.DISPOSED: {
1691 inst.m_IState = XMRInstState.DISPOSED;
1692 break;
1693 }
1694
1695 /*
1696 * RunOne returned something bad.
1697 */
1698 default: throw new Exception("bad new state");
1699 }
1700 }
1701
1702 /**
1703 * @brief Thread that moves instances from the Sleep queue to the Yield queue.
1704 */
1705 private void RunSleepThread()
1706 {
1707 double deltaTS;
1708 int deltaMS;
1709 XMRInstance inst;
1710
1711 while (true) {
1712 lock (m_SleepQueue) {
1713
1714 /*
1715 * Wait here until there is a script on the timer queue that has expired.
1716 */
1717 while (true) {
1718 UpdateMyThread ();
1719 if (m_Exiting) {
1720 MyThreadExiting ();
1721 return;
1722 }
1723 inst = m_SleepQueue.PeekHead();
1724 if (inst == null) {
1725 Monitor.Wait (m_SleepQueue, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
1726 continue;
1727 }
1728 if (inst.m_IState != XMRInstState.ONSLEEPQ) throw new Exception("bad state");
1729 deltaTS = (inst.m_SleepUntil - DateTime.UtcNow).TotalMilliseconds;
1730 if (deltaTS <= 0.0) break;
1731 deltaMS = Int32.MaxValue;
1732 if (deltaTS < Int32.MaxValue) deltaMS = (int)deltaTS;
1733 if (deltaMS > Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2) {
1734 deltaMS = Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2;
1735 }
1736 Monitor.Wait (m_SleepQueue, deltaMS);
1737 }
1738
1739 /*
1740 * Remove the expired entry from the timer queue.
1741 */
1742 m_SleepQueue.RemoveHead();
1743 inst.m_IState = XMRInstState.REMDFROMSLPQ;
1744 }
1745
1746 /*
1747 * Post the script to the yield queue so it will run and wake a script thread to run it.
1748 */
1749 lock (m_YieldQueue) {
1750 inst.m_IState = XMRInstState.ONYIELDQ;
1751 m_YieldQueue.InsertTail(inst);
1752 }
1753 XMRScriptThread.WakeUpOne ();
1754 }
1755 }
1756
1757 /**
1758 * @brief Thread that runs a time slicer.
1759 */
1760 private void RunSliceThread()
1761 {
1762 int ms = m_Config.GetInt ("TimeSlice", 50);
1763 while (!m_Exiting) {
1764 UpdateMyThread ();
1765
1766 /*
1767 * Let script run for a little bit.
1768 */
1769 System.Threading.Thread.Sleep (ms);
1770
1771 /*
1772 * If some script is running, flag it to suspend
1773 * next time it calls CheckRun().
1774 */
1775 for (int i = 0; i < numThreadScriptWorkers; i ++) {
1776 XMRScriptThread st = m_ScriptThreads[i];
1777 if (st != null) st.TimeSlice();
1778 }
1779 }
1780 MyThreadExiting ();
1781 }
1782
1783 public void Suspend(UUID itemID, int ms)
1784 {
1785 XMRInstance instance = GetInstance (itemID);
1786 if (instance != null) {
1787 instance.Sleep(ms);
1788 }
1789 }
1790
1791 public void Die(UUID itemID)
1792 {
1793 XMRInstance instance = GetInstance (itemID);
1794 if (instance != null) {
1795 TraceCalls("[XMREngine]: XMREngine.Die({0})", itemID.ToString());
1796 instance.Die();
1797 }
1798 }
1799
1800 /**
1801 * @brief Get specific script instance for which OnRezScript()
1802 * has been called for an XMREngine script, and that
1803 * OnRemoveScript() has not been called since.
1804 * @param itemID = as passed to OnRezScript() identifying a specific script instance
1805 * @returns null: not one of our scripts (maybe XEngine etc)
1806 * else: points to the script instance
1807 */
1808 public XMRInstance GetInstance(UUID itemID)
1809 {
1810 XMRInstance instance;
1811 lock (m_InstancesDict) {
1812 if (!m_InstancesDict.TryGetValue(itemID, out instance)) {
1813 instance = null;
1814 }
1815 }
1816 return instance;
1817 }
1818
1819 // Called occasionally to write script state to .state file so the
1820 // script will restart from its last known state if the region crashes
1821 // and gets restarted.
1822 private void DoMaintenance(object source, ElapsedEventArgs e)
1823 {
1824 XMRInstance[] instanceArray;
1825
1826 lock (m_InstancesDict) {
1827 instanceArray = System.Linq.Enumerable.ToArray(m_InstancesDict.Values);
1828 }
1829 foreach (XMRInstance ins in instanceArray)
1830 {
1831 // Don't save attachments
1832 if (ins.m_Part.ParentGroup.IsAttachment)
1833 continue;
1834 ins.GetExecutionState(new XmlDocument());
1835 }
1836 }
1837
1838 /**
1839 * @brief Retrieve errors generated by a previous call to OnRezScript().
1840 * We are guaranteed this routine will not be called before the
1841 * corresponding OnRezScript() has returned. It blocks until the
1842 * compile has completed.
1843 */
1844 public ArrayList GetScriptErrors(UUID itemID)
1845 {
1846 ArrayList errors;
1847
1848 lock (m_ScriptErrors) {
1849 while (!m_ScriptErrors.TryGetValue (itemID, out errors)) {
1850 Monitor.Wait (m_ScriptErrors);
1851 }
1852 m_ScriptErrors.Remove (itemID);
1853 }
1854 return errors;
1855 }
1856
1857 /**
1858 * @brief Return a list of all script execution times.
1859 */
1860 public Dictionary<uint, float> GetObjectScriptsExecutionTimes ()
1861 {
1862 Dictionary<uint, float> topScripts = new Dictionary<uint, float> ();
1863 lock (m_InstancesDict) {
1864 foreach (XMRInstance instance in m_InstancesDict.Values) {
1865 uint rootLocalID = instance.m_Part.ParentGroup.LocalId;
1866 float oldTotal;
1867 if (!topScripts.TryGetValue (rootLocalID, out oldTotal)) {
1868 oldTotal = 0;
1869 }
1870 topScripts[rootLocalID] = (float)instance.m_CPUTime + oldTotal;
1871 }
1872 }
1873 return topScripts;
1874 }
1875
1876 /**
1877 * @brief A float the value is a representative execution time in
1878 * milliseconds of all scripts in the link set.
1879 * @param itemIDs = list of scripts in the link set
1880 * @returns milliseconds for all those scripts
1881 */
1882 public float GetScriptExecutionTime (List<UUID> itemIDs)
1883 {
1884 if ((itemIDs == null) || (itemIDs.Count == 0)) {
1885 return 0;
1886 }
1887 float time = 0;
1888 foreach (UUID itemID in itemIDs) {
1889 XMRInstance instance = GetInstance (itemID);
1890 if ((instance != null) && instance.Running) {
1891 time += (float) instance.m_CPUTime;
1892 }
1893 }
1894 return time;
1895 }
1896
1897 /**
1898 * @brief Block script from dequeuing events.
1899 */
1900 public void SuspendScript(UUID itemID)
1901 {
1902 XMRInstance instance = GetInstance (itemID);
1903 if (instance != null) {
1904 TraceCalls("[XMREngine]: XMREngine.SuspendScript({0})", itemID.ToString());
1905 instance.SuspendIt();
1906 }
1907 }
1908
1909 /**
1910 * @brief Allow script to dequeue events.
1911 */
1912 public void ResumeScript(UUID itemID)
1913 {
1914 XMRInstance instance = GetInstance (itemID);
1915 if (instance != null) {
1916 TraceCalls("[XMREngine]: XMREngine.ResumeScript({0})", itemID.ToString());
1917 instance.ResumeIt();
1918 } else {
1919 // probably an XEngine script
1920 }
1921 }
1922
1923 /**
1924 * @brief Rebuild m_ObjectInstArray[partUUID] from m_ObjectItemList[partUUID]
1925 * @param partUUID = which object in scene to rebuild for
1926 */
1927 private XMRInstance[] RebuildObjectInstArray (UUID partUUID)
1928 {
1929 List<UUID> itemIDList = m_ObjectItemList[partUUID];
1930 int n = 0;
1931 foreach (UUID itemID in itemIDList) {
1932 if (m_InstancesDict.ContainsKey (itemID)) n ++;
1933 }
1934 XMRInstance[] a = new XMRInstance[n];
1935 n = 0;
1936 foreach (UUID itemID in itemIDList) {
1937 if (m_InstancesDict.TryGetValue (itemID, out a[n])) n ++;
1938 }
1939 m_ObjectInstArray[partUUID] = a;
1940 return a;
1941 }
1942
1943 public void TraceCalls (string format, params object[] args)
1944 {
1945 if (m_TraceCalls) m_log.DebugFormat (format, args);
1946 }
1947 public void Verbose (string format, params object[] args)
1948 {
1949 if (m_Verbose) m_log.DebugFormat (format, args);
1950 }
1951
1952 /**
1953 * @brief Manage our threads.
1954 */
1955 public static Thread StartMyThread (ThreadStart start, string name, ThreadPriority priority)
1956 {
1957 m_log.Debug ("[XMREngine]: starting thread " + name);
1958 Thread thread = new Thread (start);
1959 thread.Name = name;
1960 thread.Priority = priority;
1961 thread.Start ();
1962
1963 Watchdog.ThreadWatchdogInfo info = new Watchdog.ThreadWatchdogInfo (thread, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS, name);
1964 Watchdog.AddThread (info, name, true);
1965
1966 return thread;
1967 }
1968
1969 public static void UpdateMyThread ()
1970 {
1971 Watchdog.UpdateThread ();
1972 }
1973
1974 public static void MyThreadExiting ()
1975 {
1976 Watchdog.RemoveThread (true);
1977 }
1978 }
1979}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMREvents.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMREvents.cs
new file mode 100644
index 0000000..f6c2d73
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMREvents.cs
@@ -0,0 +1,369 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32using OpenMetaverse;
33using OpenSim.Framework;
34using OpenSim.Region.Framework.Scenes;
35using OpenSim.Region.Framework.Interfaces;
36using OpenSim.Region.ScriptEngine.Shared;
37using OpenSim.Region.ScriptEngine.Interfaces;
38using log4net;
39
40using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
41using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
42using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
43using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
44using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
45using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
46using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
47
48namespace OpenSim.Region.ScriptEngine.XMREngine
49{
50 /// <summary>
51 /// Prepares events so they can be directly executed upon a script by EventQueueManager, then queues it.
52 /// </summary>
53 public partial class XMREngine
54 {
55 public static readonly object[] zeroObjectArray = new object[0];
56 public static readonly object[] oneObjectArrayOne = new object[1] { 1 };
57
58 private void InitEvents()
59 {
60 m_log.Info("[XMREngine] Hooking up to server events");
61 this.World.EventManager.OnAttach += attach;
62 this.World.EventManager.OnObjectGrab += touch_start;
63 this.World.EventManager.OnObjectGrabbing += touch;
64 this.World.EventManager.OnObjectDeGrab += touch_end;
65 this.World.EventManager.OnScriptChangedEvent += changed;
66 this.World.EventManager.OnScriptAtTargetEvent += at_target;
67 this.World.EventManager.OnScriptNotAtTargetEvent += not_at_target;
68 this.World.EventManager.OnScriptAtRotTargetEvent += at_rot_target;
69 this.World.EventManager.OnScriptNotAtRotTargetEvent += not_at_rot_target;
70 this.World.EventManager.OnScriptMovingStartEvent += moving_start;
71 this.World.EventManager.OnScriptMovingEndEvent += moving_end;
72 this.World.EventManager.OnScriptControlEvent += control;
73 this.World.EventManager.OnScriptColliderStart += collision_start;
74 this.World.EventManager.OnScriptColliding += collision;
75 this.World.EventManager.OnScriptCollidingEnd += collision_end;
76 this.World.EventManager.OnScriptLandColliderStart += land_collision_start;
77 this.World.EventManager.OnScriptLandColliding += land_collision;
78 this.World.EventManager.OnScriptLandColliderEnd += land_collision_end;
79 IMoneyModule money=this.World.RequestModuleInterface<IMoneyModule>();
80 if (money != null)
81 {
82 money.OnObjectPaid+=HandleObjectPaid;
83 }
84 }
85
86 /// <summary>
87 /// When an object gets paid by an avatar and generates the paid event,
88 /// this will pipe it to the script engine
89 /// </summary>
90 /// <param name="objectID">Object ID that got paid</param>
91 /// <param name="agentID">Agent Id that did the paying</param>
92 /// <param name="amount">Amount paid</param>
93 private void HandleObjectPaid(UUID objectID, UUID agentID,
94 int amount)
95 {
96 // Add to queue for all scripts in ObjectID object
97 DetectParams[] det = new DetectParams[1];
98 det[0] = new DetectParams();
99 det[0].Key = agentID;
100 det[0].Populate(this.World);
101
102 // Since this is an event from a shared module, all scenes will
103 // get it. But only one has the object in question. The others
104 // just ignore it.
105 //
106 SceneObjectPart part =
107 this.World.GetSceneObjectPart(objectID);
108
109 if (part == null)
110 return;
111
112 if ((part.ScriptEvents & scriptEvents.money) == 0)
113 part = part.ParentGroup.RootPart;
114
115 Verbose ("Paid: " + objectID + " from " + agentID + ", amount " + amount);
116
117 if (part != null)
118 {
119 money(part.LocalId, agentID, amount, det);
120 }
121 }
122
123 /// <summary>
124 /// Handles piping the proper stuff to The script engine for touching
125 /// Including DetectedParams
126 /// </summary>
127 /// <param name="localID"></param>
128 /// <param name="originalID"></param>
129 /// <param name="offsetPos"></param>
130 /// <param name="remoteClient"></param>
131 /// <param name="surfaceArgs"></param>
132 public void touch_start(uint localID, uint originalID, Vector3 offsetPos,
133 IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs)
134 {
135 touches(localID, originalID, offsetPos, remoteClient, surfaceArgs, "touch_start");
136 }
137
138 public void touch(uint localID, uint originalID, Vector3 offsetPos,
139 IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs)
140 {
141 touches(localID, originalID, offsetPos, remoteClient, surfaceArgs, "touch");
142 }
143
144 private static Vector3 zeroVec3 = new Vector3(0,0,0);
145 public void touch_end(uint localID, uint originalID, IClientAPI remoteClient,
146 SurfaceTouchEventArgs surfaceArgs)
147 {
148 touches(localID, originalID, zeroVec3, remoteClient, surfaceArgs, "touch_end");
149 }
150
151 private void touches(uint localID, uint originalID, Vector3 offsetPos,
152 IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs, string eventname)
153 {
154 SceneObjectPart part;
155 if (originalID == 0) {
156 part = this.World.GetSceneObjectPart(localID);
157 if (part == null) return;
158 } else {
159 part = this.World.GetSceneObjectPart(originalID);
160 }
161
162 DetectParams det = new DetectParams();
163 det.Key = remoteClient.AgentId;
164 det.Populate(this.World);
165 det.OffsetPos = new LSL_Vector(offsetPos.X,
166 offsetPos.Y,
167 offsetPos.Z);
168 det.LinkNum = part.LinkNum;
169
170 if (surfaceArgs != null) {
171 det.SurfaceTouchArgs = surfaceArgs;
172 }
173
174 // Add to queue for all scripts in ObjectID object
175 this.PostObjectEvent(localID, new EventParams(
176 eventname, oneObjectArrayOne,
177 new DetectParams[] { det }));
178 }
179
180 public void changed(uint localID, uint change)
181 {
182 int ch = (int)change;
183 // Add to queue for all scripts in localID, Object pass change.
184 this.PostObjectEvent(localID, new EventParams(
185 "changed",new object[] { ch },
186 zeroDetectParams));
187 }
188
189 // state_entry: not processed here
190 // state_exit: not processed here
191
192 public void money(uint localID, UUID agentID, int amount, DetectParams[] det)
193 {
194 this.PostObjectEvent(localID, new EventParams(
195 "money", new object[] {
196 agentID.ToString(),
197 amount },
198 det));
199 }
200
201 public void collision_start(uint localID, ColliderArgs col)
202 {
203 collisions(localID, col, "collision_start");
204 }
205
206 public void collision(uint localID, ColliderArgs col)
207 {
208 collisions(localID, col, "collision");
209 }
210
211 public void collision_end(uint localID, ColliderArgs col)
212 {
213 collisions(localID, col, "collision_end");
214 }
215
216 private void collisions(uint localID, ColliderArgs col, string eventname)
217 {
218 int dc = col.Colliders.Count;
219 if (dc > 0) {
220 DetectParams[] det = new DetectParams[dc];
221 int i = 0;
222 foreach (DetectedObject detobj in col.Colliders) {
223 DetectParams d = new DetectParams();
224 det[i++] = d;
225
226 d.Key = detobj.keyUUID;
227 d.Populate (this.World);
228
229 /* not done by XEngine...
230 d.Position = detobj.posVector;
231 d.Rotation = detobj.rotQuat;
232 d.Velocity = detobj.velVector;
233 ... */
234 }
235
236 this.PostObjectEvent(localID, new EventParams(
237 eventname,
238 new Object[] { dc },
239 det));
240 }
241 }
242
243 public void land_collision_start(uint localID, ColliderArgs col)
244 {
245 land_collisions(localID, col, "land_collision_start");
246 }
247
248 public void land_collision(uint localID, ColliderArgs col)
249 {
250 land_collisions(localID, col, "land_collision");
251 }
252
253 public void land_collision_end(uint localID, ColliderArgs col)
254 {
255 land_collisions(localID, col, "land_collision_end");
256 }
257
258 private void land_collisions(uint localID, ColliderArgs col, string eventname)
259 {
260 foreach (DetectedObject detobj in col.Colliders) {
261 LSL_Vector vec = new LSL_Vector(detobj.posVector.X,
262 detobj.posVector.Y,
263 detobj.posVector.Z);
264 EventParams eps = new EventParams(eventname,
265 new Object[] { vec },
266 zeroDetectParams);
267 this.PostObjectEvent(localID, eps);
268 }
269 }
270
271 // timer: not handled here
272 // listen: not handled here
273
274 public void control(UUID itemID, UUID agentID, uint held, uint change)
275 {
276 this.PostScriptEvent(itemID, new EventParams(
277 "control",new object[] {
278 agentID.ToString(),
279 (int)held,
280 (int)change},
281 zeroDetectParams));
282 }
283
284 public void email(uint localID, UUID itemID, string timeSent,
285 string address, string subject, string message, int numLeft)
286 {
287 this.PostObjectEvent(localID, new EventParams(
288 "email",new object[] {
289 timeSent,
290 address,
291 subject,
292 message,
293 numLeft},
294 zeroDetectParams));
295 }
296
297 public void at_target(uint localID, uint handle, Vector3 targetpos,
298 Vector3 atpos)
299 {
300 this.PostObjectEvent(localID, new EventParams(
301 "at_target", new object[] {
302 (int)handle,
303 new LSL_Vector(targetpos.X,targetpos.Y,targetpos.Z),
304 new LSL_Vector(atpos.X,atpos.Y,atpos.Z) },
305 zeroDetectParams));
306 }
307
308 public void not_at_target(uint localID)
309 {
310 this.PostObjectEvent(localID, new EventParams(
311 "not_at_target",zeroObjectArray,
312 zeroDetectParams));
313 }
314
315 public void at_rot_target(uint localID, uint handle, OpenMetaverse.Quaternion targetrot, OpenMetaverse.Quaternion atrot)
316 {
317 this.PostObjectEvent(
318 localID,
319 new EventParams(
320 "at_rot_target",
321 new object[] {
322 new LSL_Integer(handle),
323 new LSL_Rotation(targetrot.X, targetrot.Y, targetrot.Z, targetrot.W),
324 new LSL_Rotation(atrot.X, atrot.Y, atrot.Z, atrot.W)
325 },
326 zeroDetectParams
327 )
328 );
329 }
330
331 public void not_at_rot_target(uint localID)
332 {
333 this.PostObjectEvent(localID, new EventParams(
334 "not_at_rot_target",zeroObjectArray,
335 zeroDetectParams));
336 }
337
338 // run_time_permissions: not handled here
339
340 public void attach(uint localID, UUID itemID, UUID avatar)
341 {
342 this.PostObjectEvent(localID, new EventParams(
343 "attach",new object[] {
344 avatar.ToString() },
345 zeroDetectParams));
346 }
347
348 // dataserver: not handled here
349 // link_message: not handled here
350
351 public void moving_start(uint localID)
352 {
353 this.PostObjectEvent(localID, new EventParams(
354 "moving_start",zeroObjectArray,
355 zeroDetectParams));
356 }
357
358 public void moving_end(uint localID)
359 {
360 this.PostObjectEvent(localID, new EventParams(
361 "moving_end",zeroObjectArray,
362 zeroDetectParams));
363 }
364
365 // object_rez: not handled here
366 // remote_data: not handled here
367 // http_response: not handled here
368 }
369}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRHeapTracker.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRHeapTracker.cs
new file mode 100644
index 0000000..c906f21
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRHeapTracker.cs
@@ -0,0 +1,172 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using OpenSim.Region.ScriptEngine.XMREngine;
30using System;
31using System.Collections.Generic;
32using System.IO;
33using System.Reflection;
34using System.Reflection.Emit;
35
36using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
37using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
38using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
39using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
40using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
41using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
42using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
43
44namespace OpenSim.Region.ScriptEngine.XMREngine
45{
46 public class HeapTrackerBase {
47 private int usage;
48 private XMRInstAbstract instance;
49
50 public HeapTrackerBase (XMRInstAbstract inst)
51 {
52 if (inst == null) throw new ArgumentNullException ("inst");
53 instance = inst;
54 }
55
56 ~HeapTrackerBase ()
57 {
58 usage = instance.UpdateHeapUse (usage, 0);
59 }
60
61 protected void NewUse (int newuse)
62 {
63 usage = instance.UpdateHeapUse (usage, newuse);
64 }
65 }
66
67 public class HeapTrackerList : HeapTrackerBase {
68 private LSL_List value;
69
70 public HeapTrackerList (XMRInstAbstract inst) : base (inst) { }
71
72 public void Pop (LSL_List lis)
73 {
74 NewUse (Size (lis));
75 value = lis;
76 }
77
78 public LSL_List Push ()
79 {
80 return value;
81 }
82
83 public static int Size (LSL_List lis)
84 {
85 return (!typeof (LSL_List).IsValueType && (lis == null)) ? 0 : lis.Size;
86 }
87 }
88
89 public class HeapTrackerObject : HeapTrackerBase {
90 public const int HT_CHAR = 2;
91 public const int HT_DELE = 8;
92 public const int HT_DOUB = 8;
93 public const int HT_SING = 4;
94 public const int HT_SFLT = 4;
95 public const int HT_INT = 4;
96 public const int HT_VEC = HT_DOUB * 3;
97 public const int HT_ROT = HT_DOUB * 4;
98
99 private object value;
100
101 public HeapTrackerObject (XMRInstAbstract inst) : base (inst) { }
102
103 public void Pop (object obj)
104 {
105 NewUse (Size (obj));
106 value = obj;
107 }
108
109 public object Push ()
110 {
111 return value;
112 }
113
114 public static int Size (object obj)
115 {
116 if (obj == null) return 0;
117
118 if (obj is char) return HT_CHAR;
119 if (obj is Delegate) return HT_DELE;
120 if (obj is double) return HT_DOUB;
121 if (obj is float) return HT_SING;
122 if (obj is int) return HT_INT;
123 if (obj is LSL_Float) return HT_SFLT;
124 if (obj is LSL_Integer) return HT_INT;
125 if (obj is LSL_List) return ((LSL_List)obj).Size;
126 if (obj is LSL_Rotation) return HT_ROT;
127 if (obj is LSL_String) return ((LSL_String)obj).m_string.Length * HT_CHAR;
128 if (obj is LSL_Vector) return HT_VEC;
129 if (obj is string) return ((string)obj).Length * HT_CHAR;
130 if (obj is XMR_Array) return 0;
131 if (obj is XMRArrayListKey) return ((XMRArrayListKey)obj).Size;
132 if (obj is XMRSDTypeClObj) return 0;
133
134 if (obj is Array) {
135 Array ar = (Array)obj;
136 int len = ar.Length;
137 if (len == 0) return 0;
138 Type et = ar.GetType ().GetElementType ();
139 if (et.IsValueType) return Size (ar.GetValue (0)) * len;
140 int size = 0;
141 for (int i = 0; i < len; i ++) {
142 size += Size (ar.GetValue (i));
143 }
144 return size;
145 }
146
147 throw new Exception ("unknown size of type " + obj.GetType ().Name);
148 }
149 }
150
151 public class HeapTrackerString : HeapTrackerBase {
152 private string value;
153
154 public HeapTrackerString (XMRInstAbstract inst) : base (inst) { }
155
156 public void Pop (string str)
157 {
158 NewUse (Size (str));
159 value = str;
160 }
161
162 public string Push ()
163 {
164 return value;
165 }
166
167 public static int Size (string str)
168 {
169 return (str == null) ? 0 : str.Length * HeapTrackerObject.HT_CHAR;
170 }
171 }
172}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstAbstract.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstAbstract.cs
new file mode 100644
index 0000000..802eac2
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstAbstract.cs
@@ -0,0 +1,2031 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using System;
30using System.Collections.Generic;
31using System.Globalization;
32using System.IO;
33using System.Reflection.Emit;
34using System.Runtime.Serialization;
35using System.Text;
36using System.Threading;
37
38using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
39using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
40using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
41using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
42using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
43using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
44using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
45
46namespace OpenSim.Region.ScriptEngine.XMREngine
47{
48 public class XMRInstArrays {
49 public XMR_Array[] iarArrays;
50 public char[] iarChars;
51 public double[] iarFloats;
52 public int[] iarIntegers;
53 public LSL_List[] iarLists;
54 public object[] iarObjects;
55 public LSL_Rotation[] iarRotations;
56 public string[] iarStrings;
57 public LSL_Vector[] iarVectors;
58 public XMRSDTypeClObj[] iarSDTClObjs;
59 public Delegate[][] iarSDTIntfObjs;
60
61 private XMRInstAbstract instance;
62 private int heapUse;
63
64 private static readonly XMR_Array[] noArrays = new XMR_Array[0];
65 private static readonly char[] noChars = new char[0];
66 private static readonly double[] noFloats = new double[0];
67 private static readonly int[] noIntegers = new int[0];
68 private static readonly LSL_List[] noLists = new LSL_List[0];
69 private static readonly object[] noObjects = new object[0];
70 private static readonly LSL_Rotation[] noRotations = new LSL_Rotation[0];
71 private static readonly string[] noStrings = new string[0];
72 private static readonly LSL_Vector[] noVectors = new LSL_Vector[0];
73 private static readonly XMRSDTypeClObj[] noSDTClObjs = new XMRSDTypeClObj[0];
74 private static readonly Delegate[][] noSDTIntfObjs = new Delegate[0][];
75
76 public XMRInstArrays (XMRInstAbstract inst)
77 {
78 instance = inst;
79 }
80
81 ~XMRInstArrays ()
82 {
83 heapUse = instance.UpdateHeapUse (heapUse, 0);
84 }
85
86 public void AllocVarArrays (XMRInstArSizes ars)
87 {
88 ClearOldArrays ();
89
90 heapUse = instance.UpdateHeapUse (heapUse,
91 ars.iasChars * HeapTrackerObject.HT_CHAR +
92 ars.iasFloats * HeapTrackerObject.HT_SFLT +
93 ars.iasIntegers * HeapTrackerObject.HT_INT +
94 ars.iasRotations * HeapTrackerObject.HT_ROT +
95 ars.iasVectors * HeapTrackerObject.HT_VEC +
96 ars.iasSDTIntfObjs * HeapTrackerObject.HT_DELE);
97
98 iarArrays = (ars.iasArrays > 0) ? new XMR_Array [ars.iasArrays] : noArrays;
99 iarChars = (ars.iasChars > 0) ? new char [ars.iasChars] : noChars;
100 iarFloats = (ars.iasFloats > 0) ? new double [ars.iasFloats] : noFloats;
101 iarIntegers = (ars.iasIntegers > 0) ? new int [ars.iasIntegers] : noIntegers;
102 iarLists = (ars.iasLists > 0) ? new LSL_List [ars.iasLists] : noLists;
103 iarObjects = (ars.iasObjects > 0) ? new object [ars.iasObjects] : noObjects;
104 iarRotations = (ars.iasRotations > 0) ? new LSL_Rotation [ars.iasRotations] : noRotations;
105 iarStrings = (ars.iasStrings > 0) ? new string [ars.iasStrings] : noStrings;
106 iarVectors = (ars.iasVectors > 0) ? new LSL_Vector [ars.iasVectors] : noVectors;
107 iarSDTClObjs = (ars.iasSDTClObjs > 0) ? new XMRSDTypeClObj[ars.iasSDTClObjs] : noSDTClObjs;
108 iarSDTIntfObjs = (ars.iasSDTIntfObjs > 0) ? new Delegate [ars.iasSDTIntfObjs][] : noSDTIntfObjs;
109 }
110
111 /**
112 * @brief Do not write directly to iarLists[index], rather use this method.
113 */
114 public void PopList (int index, LSL_List lis)
115 {
116 LSL_List old = iarLists[index];
117 int newheapuse = heapUse + HeapTrackerList.Size (lis) - HeapTrackerList.Size (old);
118 heapUse = instance.UpdateHeapUse (heapUse, newheapuse);
119 iarLists[index] = lis;
120 }
121
122 /**
123 * @brief Do not write directly to iarObjects[index], rather use this method.
124 */
125 public void PopObject (int index, object obj)
126 {
127 object old = iarObjects[index];
128 int newheapuse = heapUse + HeapTrackerObject.Size (obj) - HeapTrackerObject.Size (old);
129 heapUse = instance.UpdateHeapUse (heapUse, newheapuse);
130 iarObjects[index] = obj;
131 }
132
133 /**
134 * @brief Do not write directly to iarStrings[index], rather use this method.
135 */
136 public void PopString (int index, string str)
137 {
138 string old = iarStrings[index];
139 int newheapuse = heapUse + HeapTrackerString.Size (str) - HeapTrackerString.Size (old);
140 heapUse = instance.UpdateHeapUse (heapUse, newheapuse);
141 iarStrings[index] = str;
142 }
143
144 /**
145 * @brief Write all arrays out to a file.
146 */
147 public delegate void Sender (object value);
148 public void SendArrays (Sender sender)
149 {
150 sender (iarArrays);
151 sender (iarChars);
152 sender (iarFloats);
153 sender (iarIntegers);
154 sender (iarLists);
155 sender (iarObjects);
156 sender (iarRotations);
157 sender (iarStrings);
158 sender (iarVectors);
159 sender (iarSDTClObjs);
160 sender (iarSDTIntfObjs);
161 }
162
163 /**
164 * @brief Read all arrays in from a file.
165 */
166 public delegate object Recver ();
167 public void RecvArrays (Recver recver)
168 {
169 ClearOldArrays ();
170
171 iarArrays = (XMR_Array[]) recver ();
172 char[] chrs = (char[]) recver ();
173 double[] flts = (double[]) recver ();
174 int[] ints = (int[]) recver ();
175 LSL_List[] liss = (LSL_List[]) recver ();
176 object[] objs = (object[]) recver ();
177 LSL_Rotation[] rots = (LSL_Rotation[]) recver ();
178 string[] strs = (string[]) recver ();
179 LSL_Vector[] vecs = (LSL_Vector[]) recver ();
180 iarSDTClObjs = (XMRSDTypeClObj[]) recver ();
181 Delegate[][] dels = (Delegate[][]) recver ();
182
183 int newheapuse = heapUse;
184
185 // value types simply are the size of the value * number of values
186 newheapuse += chrs.Length * HeapTrackerObject.HT_CHAR;
187 newheapuse += flts.Length * HeapTrackerObject.HT_SFLT;
188 newheapuse += ints.Length * HeapTrackerObject.HT_INT;
189 newheapuse += rots.Length * HeapTrackerObject.HT_ROT;
190 newheapuse += vecs.Length * HeapTrackerObject.HT_VEC;
191 newheapuse += dels.Length * HeapTrackerObject.HT_DELE;
192
193 // lists, objects, strings are the sum of the size of each element
194 foreach (LSL_List lis in liss) {
195 newheapuse += HeapTrackerList.Size (lis);
196 }
197 foreach (object obj in objs) {
198 newheapuse += HeapTrackerObject.Size (obj);
199 }
200 foreach (string str in strs) {
201 newheapuse += HeapTrackerString.Size (str);
202 }
203
204 // others (XMR_Array, XMRSDTypeClObj) keep track of their own heap usage
205
206 // update script heap usage, throwing an exception before finalizing changes
207 heapUse = instance.UpdateHeapUse (heapUse, newheapuse);
208
209 iarChars = chrs;
210 iarFloats = flts;
211 iarIntegers = ints;
212 iarLists = liss;
213 iarObjects = objs;
214 iarRotations = rots;
215 iarStrings = strs;
216 iarVectors = vecs;
217 iarSDTIntfObjs = dels;
218 }
219
220 private void ClearOldArrays ()
221 {
222 int newheapuse = heapUse;
223
224 iarArrays = null;
225 if (iarChars != null) {
226 newheapuse -= iarChars.Length * HeapTrackerObject.HT_CHAR;
227 iarChars = null;
228 }
229 if (iarFloats != null) {
230 newheapuse -= iarFloats.Length * HeapTrackerObject.HT_SFLT;
231 iarFloats = null;
232 }
233 if (iarIntegers != null) {
234 newheapuse -= iarIntegers.Length * HeapTrackerObject.HT_INT;
235 iarIntegers = null;
236 }
237 if (iarLists != null) {
238 foreach (LSL_List lis in iarLists) {
239 newheapuse -= HeapTrackerList.Size (lis);
240 }
241 iarLists = null;
242 }
243 if (iarObjects != null) {
244 foreach (object obj in iarObjects) {
245 newheapuse -= HeapTrackerObject.Size (obj);
246 }
247 iarObjects = null;
248 }
249 if (iarRotations != null) {
250 newheapuse -= iarRotations.Length * HeapTrackerObject.HT_ROT;
251 iarRotations = null;
252 }
253 if (iarStrings != null) {
254 foreach (string str in iarStrings) {
255 newheapuse -= HeapTrackerString.Size (str);
256 }
257 iarStrings = null;
258 }
259 if (iarVectors != null) {
260 newheapuse -= iarVectors.Length * HeapTrackerObject.HT_VEC;
261 iarVectors = null;
262 }
263 iarSDTClObjs = null;
264 if (iarSDTIntfObjs != null) {
265 newheapuse -= iarSDTIntfObjs.Length * HeapTrackerObject.HT_DELE;
266 iarSDTIntfObjs = null;
267 }
268
269 heapUse = instance.UpdateHeapUse (heapUse, newheapuse);
270 }
271 }
272
273 public class XMRInstArSizes {
274 public int iasArrays;
275 public int iasChars;
276 public int iasFloats;
277 public int iasIntegers;
278 public int iasLists;
279 public int iasObjects;
280 public int iasRotations;
281 public int iasStrings;
282 public int iasVectors;
283 public int iasSDTClObjs;
284 public int iasSDTIntfObjs;
285
286 public void WriteAsmFile (TextWriter asmFileWriter, string label)
287 {
288 asmFileWriter.WriteLine (" {0}Arrays {1}", label, iasArrays);
289 asmFileWriter.WriteLine (" {0}Chars {1}", label, iasChars);
290 asmFileWriter.WriteLine (" {0}Floats {1}", label, iasFloats);
291 asmFileWriter.WriteLine (" {0}Integers {1}", label, iasIntegers);
292 asmFileWriter.WriteLine (" {0}Lists {1}", label, iasLists);
293 asmFileWriter.WriteLine (" {0}Objects {1}", label, iasObjects);
294 asmFileWriter.WriteLine (" {0}Rotations {1}", label, iasRotations);
295 asmFileWriter.WriteLine (" {0}Strings {1}", label, iasStrings);
296 asmFileWriter.WriteLine (" {0}Vectors {1}", label, iasVectors);
297 asmFileWriter.WriteLine (" {0}SDTClObjs {1}", label, iasSDTClObjs);
298 asmFileWriter.WriteLine (" {0}SDTIntfObjs {1}", label, iasSDTIntfObjs);
299 }
300 public void WriteToFile (BinaryWriter objFileWriter)
301 {
302 objFileWriter.Write (iasArrays);
303 objFileWriter.Write (iasChars);
304 objFileWriter.Write (iasFloats);
305 objFileWriter.Write (iasIntegers);
306 objFileWriter.Write (iasLists);
307 objFileWriter.Write (iasObjects);
308 objFileWriter.Write (iasRotations);
309 objFileWriter.Write (iasStrings);
310 objFileWriter.Write (iasVectors);
311 objFileWriter.Write (iasSDTClObjs);
312 objFileWriter.Write (iasSDTIntfObjs);
313 }
314 public void ReadFromFile (BinaryReader objFileReader)
315 {
316 iasArrays = objFileReader.ReadInt32 ();
317 iasChars = objFileReader.ReadInt32 ();
318 iasFloats = objFileReader.ReadInt32 ();
319 iasIntegers = objFileReader.ReadInt32 ();
320 iasLists = objFileReader.ReadInt32 ();
321 iasObjects = objFileReader.ReadInt32 ();
322 iasRotations = objFileReader.ReadInt32 ();
323 iasStrings = objFileReader.ReadInt32 ();
324 iasVectors = objFileReader.ReadInt32 ();
325 iasSDTClObjs = objFileReader.ReadInt32 ();
326 iasSDTIntfObjs = objFileReader.ReadInt32 ();
327 }
328 }
329
330 public class XMRStackFrame {
331 public XMRStackFrame nextSF;
332 public string funcName;
333 public int callNo;
334 public object[] objArray;
335 }
336
337 /*
338 * Contains only items required by the stand-alone compiler
339 * so the compiler doesn't need to pull in all of OpenSim.
340 *
341 * Inherit from ScriptBaseClass so we can be used as 'this'
342 * parameter for backend-API calls, eg llSay().
343 */
344 public abstract class XMRInstAbstract : ScriptBaseClass
345 {
346 public const int CallMode_NORMAL = 0; // when function is called, it proceeds normally
347 public const int CallMode_SAVE = 1; // StackSaveException() was thrown, push args/locals to stackFrames
348 public const int CallMode_RESTORE = 2; // when function is called, it pops state from stackFrames
349
350 public bool suspendOnCheckRunHold; // suspend script execution until explicitly set false
351 public bool suspendOnCheckRunTemp; // suspend script execution for single step only
352 public int stackLimit; // stack must have at least this many bytes free on entry to functions
353
354 public ScriptObjCode m_ObjCode; // script object code this instance was created from
355
356 public object[] ehArgs; // event handler argument array
357 public bool doGblInit = true; // default state_entry() needs to initialize global variables
358 public int stateCode = 0; // state the script is in (0 = 'default')
359 public int newStateCode = -1; // if >= 0, in the middle of exiting 'stateCode' and entering 'newStateCode'
360 public ScriptEventCode eventCode = ScriptEventCode.None;
361 // what event handler is executing (or None if not)
362
363 public int callMode = CallMode_NORMAL;
364 // to capture stack frames on stackFrames:
365 // set to CallMode_SAVE just before throwing StackSaveException()
366 // from within CheckRun() and cleared to CallMode_NORMAL when
367 // the exception is caught
368 // to restore stack frames from stackFrames:
369 // set to CallMode_RESTORE just before calling CallSEH() and
370 // cleared to CallMode_NORMAL by CheckRun()
371 public XMRStackFrame stackFrames; // stack frames being saved/restored
372
373 private static readonly char[] justacomma = { ',' };
374
375 /*
376 * These arrays hold the global variable values for the script instance.
377 * The array lengths are determined by the script compilation,
378 * and are found in ScriptObjCode.glblSizes.
379 */
380 public XMRInstArrays glblVars;
381
382 public XMRInstAbstract ()
383 {
384 glblVars = new XMRInstArrays (this);
385 }
386
387 /****************************************************************\
388 * Abstract function prototypes. *
389 * These functions require access to the OpenSim environment. *
390 \****************************************************************/
391
392 public abstract void CheckRunWork ();
393 public abstract void StateChange ();
394 public abstract int xmrStackLeft ();
395
396 [xmrMethodCallsCheckRunAttribute] // calls CheckRun()
397 [xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
398 public abstract LSL_List xmrEventDequeue (double timeout, int returnMask1, int returnMask2,
399 int backgroundMask1, int backgroundMask2);
400
401 [xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
402 public abstract void xmrEventEnqueue (LSL_List ev);
403
404 [xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
405 public abstract LSL_List xmrEventSaveDets ();
406
407 [xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
408 public abstract void xmrEventLoadDets (LSL_List dpList);
409
410 [xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
411 public abstract void xmrTrapRegionCrossing (int en);
412
413 [xmrMethodIsNoisyAttribute] // calls Stub<somethingorother>()
414 public abstract bool xmrSetObjRegPosRotAsync (LSL_Vector pos, LSL_Rotation rot, int options, int evcode, LSL_List evargs);
415
416 /************************************\
417 * Constants available to scripts *
418 \************************************/
419
420 public const int XMRSORPRA_FLYACROSS = 0x00000001;
421
422 /**************************************************\
423 * Functions what don't require runtime support *
424 * beyond what the compiler provides. *
425 \**************************************************/
426
427 protected int heapLimit;
428 private int heapUsed;
429
430 public virtual int UpdateHeapUse (int olduse, int newuse)
431 {
432 if (newuse <= olduse) {
433 Interlocked.Add (ref heapUsed, newuse - olduse);
434 } else {
435 int newtotal, oldtotal;
436 do {
437 oldtotal = Interlocked.Add (ref heapUsed, 0);
438 newtotal = oldtotal + newuse - olduse;
439 if (newtotal > heapLimit) {
440 System.GC.Collect ();
441 System.GC.WaitForPendingFinalizers ();
442 oldtotal = Interlocked.Add (ref heapUsed, 0);
443 newtotal = oldtotal + newuse - olduse;
444 if (newtotal > heapLimit) {
445 throw new OutOfHeapException (oldtotal, newtotal, heapLimit);
446 }
447 }
448 } while (Interlocked.CompareExchange (ref heapUsed, newtotal, oldtotal) != oldtotal);
449 }
450
451 return newuse;
452 }
453
454 public int xmrHeapLeft ()
455 {
456 return heapLimit - heapUsed;
457 }
458 public int xmrHeapUsed ()
459 {
460 return heapUsed;
461 }
462
463 /**
464 * @brief Call script's event handler function from the very beginning.
465 * @param instance.stateCode = which state the event is happening in
466 * @param instance.eventCode = which event is happening in that state
467 * @returns when event handler has completed or throws an exception
468 * with instance.eventCode = ScriptEventCode.None
469 */
470 public void CallSEH ()
471 {
472 ScriptEventHandler seh;
473
474 /*
475 * CallMode_NORMAL: run event handler from the beginning normally
476 * CallMode_RESTORE: restore event handler stack from stackFrames
477 */
478 callMode = (stackFrames == null) ? XMRInstAbstract.CallMode_NORMAL :
479 XMRInstAbstract.CallMode_RESTORE;
480
481 while (true) {
482 if (this.newStateCode < 0) {
483
484 /*
485 * Process event given by 'stateCode' and 'eventCode'.
486 * The event handler should call CheckRun() as often as convenient.
487 */
488 int newState = this.stateCode;
489 seh = this.m_ObjCode.scriptEventHandlerTable[newState,(int)this.eventCode];
490 if (seh != null) {
491 try {
492 seh (this);
493 } catch (ScriptChangeStateException scse) {
494 newState = scse.newState;
495 }
496 }
497 this.ehArgs = null; // we are done with them and no args for
498 // exit_state()/enter_state() anyway
499
500 /*
501 * The usual case is no state change.
502 * Even a 'state <samestate>;' statement has no effect except to exit out.
503 * It does not execute the state_exit() or state_entry() handlers.
504 * See http://wiki.secondlife.com/wiki/State
505 */
506 if (newState == this.stateCode) break;
507
508 /*
509 * Save new state in a more permanent location in case we
510 * get serialized out while in the state_exit() handler.
511 */
512 this.newStateCode = newState;
513 }
514
515 /*
516 * Call old state's state_exit() handler.
517 */
518 this.eventCode = ScriptEventCode.state_exit;
519 seh = this.m_ObjCode.scriptEventHandlerTable[this.stateCode,(int)ScriptEventCode.state_exit];
520 if (seh != null) {
521 try {
522 seh (this);
523 } catch (ScriptChangeStateException scse) {
524 this.newStateCode = scse.newState;
525 }
526 }
527
528 /*
529 * Switch over to the new state's state_entry() handler.
530 */
531 this.stateCode = this.newStateCode;
532 this.eventCode = ScriptEventCode.state_entry;
533 this.newStateCode = -1;
534
535 /*
536 * Now that the old state can't possibly start any more activity,
537 * cancel any listening handlers, etc, of the old state.
538 */
539 this.StateChange ();
540
541 /*
542 * Loop back to execute new state's state_entry() handler.
543 */
544 }
545
546 /*
547 * Event no longer being processed.
548 */
549 this.eventCode = ScriptEventCode.None;
550 }
551
552 /**
553 * @brief For compatibility with old code.
554 */
555 public void CheckRun (int line)
556 {
557 CheckRunStack ();
558 }
559
560 /**
561 * @brief Called at beginning of complex functions to see if they
562 * are nested too deep possibly in a recursive loop.
563 */
564 public void CheckRunStack ()
565 {
566 if (xmrStackLeft () < stackLimit) {
567 throw new OutOfStackException ();
568 }
569 CheckRunQuick ();
570 }
571
572 /**
573 * @brief Called in each iteration of a loop to see if running too long.
574 */
575 public void CheckRunQuick ()
576 {
577 if (suspendOnCheckRunHold || suspendOnCheckRunTemp) {
578 CheckRunWork ();
579 }
580 }
581
582 /**
583 * @brief Called during CallMode_SAVE to create a stackframe save object that saves
584 * local variables and calling point within the function.
585 * @param funcName = name of function whose frame is being saved
586 * @param callNo = call number (ie, return address) within function to restart at
587 * @param nSaves = number of variables the function will save
588 * @returns an object[nSaves] where function can save variables
589 */
590 public object[] CaptureStackFrame (string funcName, int callNo, int nSaves)
591 {
592 XMRStackFrame sf = new XMRStackFrame ();
593 sf.nextSF = stackFrames;
594 sf.funcName = funcName;
595 sf.callNo = callNo;
596 sf.objArray = new object[nSaves];
597 stackFrames = sf;
598 return sf.objArray;
599 }
600
601 /**
602 * @brief Called during CallMode_RESTORE to pop a stackframe object to restore
603 * local variables and calling point within the function.
604 * @param funcName = name of function whose frame is being restored
605 * @returns the object[nSaves] where function can retrieve variables
606 * callNo = as passed to CaptureStackFrame() indicating restart point
607 */
608 public object[] RestoreStackFrame (string funcName, out int callNo)
609 {
610 XMRStackFrame sf = stackFrames;
611 if (sf.funcName != funcName) {
612 throw new Exception ("frame mismatch " + sf.funcName + " vs " + funcName);
613 }
614 callNo = sf.callNo;
615 stackFrames = sf.nextSF;
616 return sf.objArray;
617 }
618
619 /**
620 * @brief Convert all LSL_Integers in a list to System.Int32s,
621 * as required by llParcelMediaQuery().
622 */
623 public static LSL_List FixLLParcelMediaQuery (LSL_List oldlist)
624 {
625 object[] oldarray = oldlist.Data;
626 int len = oldarray.Length;
627 object[] newarray = new object[len];
628 for (int i = 0; i < len; i ++) {
629 object obj = oldarray[i];
630 if (obj is LSL_Integer) obj = (int)(LSL_Integer)obj;
631 newarray[i] = obj;
632 }
633 return new LSL_List (newarray);
634 }
635
636 /**
637 * @brief Convert *SOME* LSL_Integers in a list to System.Int32s,
638 * as required by llParcelMediaCommandList().
639 */
640 public static LSL_List FixLLParcelMediaCommandList (LSL_List oldlist)
641 {
642 object[] oldarray = oldlist.Data;
643 int len = oldarray.Length;
644 object[] newarray = new object[len];
645 int verbatim = 0;
646 for (int i = 0; i < len; i ++) {
647 object obj = oldarray[i];
648 if (-- verbatim < 0) {
649 if (obj is LSL_Integer) obj = (int)(LSL_Integer)obj;
650 if (obj is int) {
651 switch ((int)obj) {
652 case ScriptBaseClass.PARCEL_MEDIA_COMMAND_AUTO_ALIGN: {
653 // leave next integer as LSL_Integer
654 verbatim = 1;
655 break;
656 }
657 case ScriptBaseClass.PARCEL_MEDIA_COMMAND_SIZE: {
658 // leave next two integers as LSL_Integer
659 verbatim = 2;
660 break;
661 }
662 }
663 }
664 }
665 newarray[i] = obj;
666 }
667 return new LSL_List (newarray);
668 }
669
670 public static int xmrHashCode (int i)
671 {
672 return i.GetHashCode ();
673 }
674 public static int xmrHashCode (double f)
675 {
676 return f.GetHashCode ();
677 }
678 public static int xmrHashCode (object o)
679 {
680 return o.GetHashCode ();
681 }
682 public static int xmrHashCode (string s)
683 {
684 return s.GetHashCode ();
685 }
686
687 public bool xmrSetObjRegPosRotAsync (LSL_Vector pos, LSL_Rotation rot, int evcode, LSL_List evargs)
688 {
689 return xmrSetObjRegPosRotAsync (pos, rot, 0, evcode, evargs);
690 }
691
692 public string xmrTypeName (object o)
693 {
694 /*
695 * Basic types return constant strings of the script-visible type name.
696 */
697 if (o is XMR_Array) return "array";
698 if (o is bool) return "bool";
699 if (o is char) return "char";
700 if (o is Exception) return "exception";
701 if (o is double) return "float";
702 if (o is float) return "float";
703 if (o is LSL_Float) return "float";
704 if (o is int) return "integer";
705 if (o is LSL_Integer) return "integer";
706 if (o is LSL_List) return "list";
707 if (o is LSL_Rotation) return "rotation";
708 if (o is LSL_String) return "string";
709 if (o is string) return "string";
710 if (o is LSL_Vector) return "vector";
711
712 /*
713 * A script-defined interface is represented as an array of delegates.
714 * If that is the case, convert it to the object of the script-defined
715 * class that is implementing the interface. This should let the next
716 * step get the script-defined type name of the object.
717 */
718 if (o is Delegate[]) {
719 o = ((Delegate[])o)[0].Target;
720 }
721
722 /*
723 * If script-defined class instance, get the script-defined
724 * type name.
725 */
726 if (o is XMRSDTypeClObj) {
727 return ((XMRSDTypeClObj)o).sdtcClass.longName.val;
728 }
729
730 /*
731 * If it's a delegate, maybe we can look up its script-defined type name.
732 */
733 Type ot = o.GetType ();
734 if (o is Delegate) {
735 String os;
736 if (m_ObjCode.sdDelTypes.TryGetValue (ot, out os)) return os;
737 }
738
739 /*
740 * Don't know what it is, get the C#-level type name.
741 */
742 return ot.ToString ();
743 }
744
745 /**
746 * @brief Call the current state's event handler.
747 * @param ev = as returned by xmrEventDequeue saying which event handler to call
748 * and what argument list to pass to it. The llDetect...() parameters
749 * are as currently set for the script (use xmrEventLoadDets to set how
750 * you want them to be different).
751 */
752 public void xmrEventCallHandler (LSL_List ev)
753 {
754 object[] data = ev.Data;
755 int evc = (int)(ev.GetLSLIntegerItem (0).value & 0xFFFFFFFF);
756 ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode,evc];
757 if (seh != null) {
758 int nargs = data.Length - 1;
759 object[] args = new object[nargs];
760 Array.Copy (data, 1, args, 0, nargs);
761
762 object[] saveEHArgs = this.ehArgs;
763 ScriptEventCode saveEventCode = this.eventCode;
764
765 this.ehArgs = args;
766 this.eventCode = (ScriptEventCode)evc;
767
768 seh (this);
769
770 this.ehArgs = saveEHArgs;
771 this.eventCode = saveEventCode;
772 }
773 }
774
775 /**
776 * @brief Sane substring functions.
777 */
778 public string xmrSubstring (string s, int offset)
779 {
780 if (offset >= s.Length) return "";
781 return s.Substring (offset);
782 }
783 // C# style
784 public string xmrSubstring (string s, int offset, int length)
785 {
786 if (length <= 0) return "";
787 if (offset >= s.Length) return "";
788 if (length > s.Length - offset) length = s.Length - offset;
789 return s.Substring (offset, length);
790 }
791 // java style
792 public string xmrJSubstring (string s, int beg, int end)
793 {
794 if (end <= beg) return "";
795 if (beg >= s.Length) return "";
796 if (end > s.Length) end = s.Length;
797 return s.Substring (beg, end - beg);
798 }
799
800 /**
801 * @brief String begins and ends with test.
802 */
803 public bool xmrStringStartsWith (string s, string t)
804 {
805 return s.StartsWith (t);
806 }
807 public bool xmrStringEndsWith (string s, string t)
808 {
809 return s.EndsWith (t);
810 }
811
812 /**
813 * @brief [Last]IndexOf with starting position (just like C#)
814 */
815 public int xmrStringIndexOf (string haystack, string needle)
816 {
817 return haystack.IndexOf (needle);
818 }
819 public int xmrStringIndexOf (string haystack, string needle, int startat)
820 {
821 return haystack.IndexOf (needle, startat);
822 }
823 public int xmrStringLastIndexOf (string haystack, string needle)
824 {
825 return haystack.LastIndexOf (needle);
826 }
827 public int xmrStringLastIndexOf (string haystack, string needle, int startat)
828 {
829 return haystack.LastIndexOf (needle, startat);
830 }
831
832 /**
833 * @brief These conversions throw exceptions if there is anything stinky...
834 */
835 public double xmrString2Float (string s)
836 {
837 return double.Parse (s, CultureInfo.InvariantCulture);
838 }
839 public int xmrString2Integer (string s)
840 {
841 s = s.Trim ();
842 if (s.StartsWith ("0x") || s.StartsWith ("0X")) {
843 return int.Parse (s.Substring (2), NumberStyles.HexNumber);
844 }
845 return int.Parse (s, CultureInfo.InvariantCulture);
846 }
847 public LSL_Rotation xmrString2Rotation (string s)
848 {
849 s = s.Trim ();
850 if (!s.StartsWith ("<") || !s.EndsWith (">")) {
851 throw new FormatException ("doesn't begin with < and end with >");
852 }
853 s = s.Substring (1, s.Length - 2);
854 string[] splitup = s.Split (justacomma, 5);
855 if (splitup.Length != 4) {
856 throw new FormatException ("doesn't have exactly 3 commas");
857 }
858 double x = double.Parse (splitup[0], CultureInfo.InvariantCulture);
859 double y = double.Parse (splitup[1], CultureInfo.InvariantCulture);
860 double z = double.Parse (splitup[2], CultureInfo.InvariantCulture);
861 double w = double.Parse (splitup[3], CultureInfo.InvariantCulture);
862 return new LSL_Rotation (x, y, z, w);
863 }
864 public LSL_Vector xmrString2Vector (string s)
865 {
866 s = s.Trim ();
867 if (!s.StartsWith ("<") || !s.EndsWith (">")) {
868 throw new FormatException ("doesn't begin with < and end with >");
869 }
870 s = s.Substring (1, s.Length - 2);
871 string[] splitup = s.Split (justacomma, 4);
872 if (splitup.Length != 3) {
873 throw new FormatException ("doesn't have exactly 2 commas");
874 }
875 double x = double.Parse (splitup[0], CultureInfo.InvariantCulture);
876 double y = double.Parse (splitup[1], CultureInfo.InvariantCulture);
877 double z = double.Parse (splitup[2], CultureInfo.InvariantCulture);
878 return new LSL_Vector (x, y, z);
879 }
880
881 /**
882 * @brief Access C#-style formatted numeric conversions.
883 */
884 public string xmrFloat2String (double val, string fmt)
885 {
886 return val.ToString (fmt, CultureInfo.InvariantCulture);
887 }
888 public string xmrInteger2String (int val, string fmt)
889 {
890 return val.ToString (fmt, CultureInfo.InvariantCulture);
891 }
892 public string xmrRotation2String (LSL_Rotation val, string fmt)
893 {
894 return "<" + val.x.ToString (fmt, CultureInfo.InvariantCulture) + "," +
895 val.y.ToString (fmt, CultureInfo.InvariantCulture) + "," +
896 val.z.ToString (fmt, CultureInfo.InvariantCulture) + "," +
897 val.s.ToString (fmt, CultureInfo.InvariantCulture) + ">";
898 }
899 public string xmrVector2String (LSL_Vector val, string fmt)
900 {
901 return "<" + val.x.ToString (fmt, CultureInfo.InvariantCulture) + "," +
902 val.y.ToString (fmt, CultureInfo.InvariantCulture) + "," +
903 val.z.ToString (fmt, CultureInfo.InvariantCulture) + ">";
904 }
905
906 /**
907 * @brief Get a delegate for a script-defined function.
908 * @param name = name of the function including arg types, eg,
909 * "Verify(array,list,string)"
910 * @param sig = script-defined type name
911 * @param targ = function's 'this' pointer or null if static
912 * @returns delegate for the script-defined function
913 */
914 public Delegate GetScriptMethodDelegate (string name, string sig, object targ)
915 {
916 DynamicMethod dm = m_ObjCode.dynamicMethods[name];
917 TokenDeclSDTypeDelegate dt = (TokenDeclSDTypeDelegate)m_ObjCode.sdObjTypesName[sig];
918 return dm.CreateDelegate (dt.GetSysType (), targ);
919 }
920
921 /**
922 * @brief Try to cast the thrown object to the given script-defined type.
923 * @param thrown = what object was thrown
924 * @param inst = what script instance we are running in
925 * @param sdtypeindex = script-defined type to try to cast it to
926 * @returns null: thrown is not castable to sdtypename
927 * else: an object casted to sdtypename
928 */
929 public static object XMRSDTypeCatchTryCastToSDType (object thrown, XMRInstAbstract inst, int sdtypeindex)
930 {
931 TokenDeclSDType sdType = inst.m_ObjCode.sdObjTypesIndx[sdtypeindex];
932
933 /*
934 * If it is a script-defined interface object, convert to the original XMRSDTypeClObj.
935 */
936 if (thrown is Delegate[]) {
937 thrown = ((Delegate[])thrown)[0].Target;
938 }
939
940 /*
941 * If it is a script-defined delegate object, make sure it is an instance of the expected type.
942 */
943 if (thrown is Delegate) {
944 Type ot = thrown.GetType ();
945 Type tt = sdType.GetSysType ();
946 return (ot == tt) ? thrown : null;
947 }
948
949 /*
950 * If it is a script-defined class object, make sure it is an instance of the expected class.
951 */
952 if (thrown is XMRSDTypeClObj) {
953
954 /*
955 * Step from the object's actual class rootward.
956 * If we find the requested class along the way, the cast is valid.
957 * If we run off the end of the root, the cast is not valid.
958 */
959 for (TokenDeclSDTypeClass ac = ((XMRSDTypeClObj)thrown).sdtcClass; ac != null; ac = ac.extends) {
960 if (ac == sdType) return thrown;
961 }
962 }
963
964 /*
965 * Don't know what it is, assume it is not what caller wants.
966 */
967 return null;
968 }
969
970 /**
971 * @brief Allocate and access fixed-dimension arrays.
972 */
973 public static object xmrFixedArrayAllocC (int len) { return new char[len]; }
974 public static object xmrFixedArrayAllocF (int len) { return new double[len]; }
975 public static object xmrFixedArrayAllocI (int len) { return new int[len]; }
976 public static object xmrFixedArrayAllocO (int len) { return new object[len]; }
977
978 public static char xmrFixedArrayGetC (object arr, int idx) { return ( (char[])arr)[idx]; }
979 public static double xmrFixedArrayGetF (object arr, int idx) { return ((double[])arr)[idx]; }
980 public static int xmrFixedArrayGetI (object arr, int idx) { return ( (int[])arr)[idx]; }
981 public static object xmrFixedArrayGetO (object arr, int idx) { return ((object[])arr)[idx]; }
982
983 public static void xmrFixedArraySetC (object arr, int idx, char val) { ((char[])arr)[idx] = val; }
984 public static void xmrFixedArraySetF (object arr, int idx, double val) { ((double[])arr)[idx] = val; }
985 public static void xmrFixedArraySetI (object arr, int idx, int val) { ((int[])arr)[idx] = val; }
986 public static void xmrFixedArraySetO (object arr, int idx, object val) { ((object[])arr)[idx] = val; }
987
988 /**
989 * @brief Copy from one script-defined array to another.
990 * @param srcobj = source script-defined array class object pointer
991 * @param srcstart = offset in source array to start copying from
992 * @param dstobj = destination script-defined array class object pointer
993 * @param dststart = offset in destination arry to start copying to
994 * @param count = number of elements to copy
995 */
996 public static void xmrArrayCopy (object srcobj, int srcstart, object dstobj, int dststart, int count)
997 {
998 /*
999 * The script writer should only pass us script-defined class objects.
1000 * Throw exception otherwise.
1001 */
1002 XMRSDTypeClObj srcsdt = (XMRSDTypeClObj)srcobj;
1003 XMRSDTypeClObj dstsdt = (XMRSDTypeClObj)dstobj;
1004
1005 /*
1006 * Get the script-visible type name of the arrays, brackets and all.
1007 */
1008 string srctypename = srcsdt.sdtcClass.longName.val;
1009 string dsttypename = dstsdt.sdtcClass.longName.val;
1010
1011 /*
1012 * The part before the first '[' of each should match exactly,
1013 * meaning the basic data type (eg, float, List<string>) is the same.
1014 * And there must be a '[' in each meaning that it is a script-defined array type.
1015 */
1016 int i = srctypename.IndexOf ('[');
1017 int j = dsttypename.IndexOf ('[');
1018 if ((i < 0) || (j < 0)) throw new InvalidCastException ("non-array passed: " + srctypename + " and/or " + dsttypename);
1019 if ((i != j) || !srctypename.StartsWith (dsttypename.Substring (0, j))) {
1020 throw new ArrayTypeMismatchException (srctypename + " vs " + dsttypename);
1021 }
1022
1023 /*
1024 * The number of brackets must match exactly.
1025 * This permits copying from something like a float[,][] to something like a float[][].
1026 * But you cannot copy from a float[][] to a float[] or wisa wersa.
1027 * Counting either '[' or ']' would work equally well.
1028 */
1029 int srclen = srctypename.Length;
1030 int dstlen = dsttypename.Length;
1031 int srcjags = 0;
1032 int dstjags = 0;
1033 while (++ i < srclen) if (srctypename[i] == ']') srcjags ++;
1034 while (++ j < dstlen) if (dsttypename[j] == ']') dstjags ++;
1035 if (dstjags != srcjags) {
1036 throw new ArrayTypeMismatchException (srctypename + " vs " + dsttypename);
1037 }
1038
1039 /*
1040 * Perform the copy.
1041 */
1042 Array srcarray = (Array)srcsdt.instVars.iarObjects[0];
1043 Array dstarray = (Array)dstsdt.instVars.iarObjects[0];
1044 Array.Copy (srcarray, srcstart, dstarray, dststart, count);
1045 }
1046
1047 /**
1048 * @brief Copy from an array to a list.
1049 * @param srcar = the array to copy from
1050 * @param start = where to start in the array
1051 * @param count = number of elements
1052 * @returns the list
1053 */
1054 public static LSL_List xmrArray2List (object srcar, int start, int count)
1055 {
1056 /*
1057 * Get the script-visible type of the array.
1058 * We only do arrays.
1059 */
1060 XMRSDTypeClObj array = (XMRSDTypeClObj)srcar;
1061 TokenDeclSDTypeClass sdtClass = array.sdtcClass;
1062 if (sdtClass.arrayOfRank == 0) {
1063 throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val);
1064 }
1065
1066 /*
1067 * Validate objects they want to put in the list.
1068 * We can't allow anything funky that OpenSim runtime doesn't expect.
1069 */
1070 Array srcarray = (Array)array.instVars.iarObjects[0];
1071 object[] output = new object[count];
1072 for (int i = 0; i < count; i ++) {
1073 object src = srcarray.GetValue (i + start);
1074 if (src == null) throw new NullReferenceException ("null element " + i);
1075 if (src is double) {
1076 output[i] = new LSL_Float ((double)src);
1077 continue;
1078 }
1079 if (src is int) {
1080 output[i] = new LSL_Integer ((int)src);
1081 continue;
1082 }
1083 if (src is LSL_Rotation) {
1084 output[i] = src;
1085 continue;
1086 }
1087 if (src is LSL_Vector) {
1088 output[i] = src;
1089 continue;
1090 }
1091 if (src is string) {
1092 output[i] = new LSL_String ((string)src);
1093 continue;
1094 }
1095 throw new InvalidCastException ("invalid element " + i + " type " + src.GetType ().Name);
1096 }
1097
1098 /*
1099 * Make a list out of that now immutable array.
1100 */
1101 return new LSL_List (output);
1102 }
1103
1104 /**
1105 * @brief Copy from a list to an array.
1106 * @param srclist = list to copy from
1107 * @param srcstart = where to start in the list
1108 * @param dstobj = array to copy to
1109 * @param dststart = where to start in the array
1110 * @param count = number of elements
1111 */
1112 public static void xmrList2Array (LSL_List srclist, int srcstart, object dstobj, int dststart, int count)
1113 {
1114 /*
1115 * Get the script-visible type of the destination.
1116 * We only do arrays.
1117 */
1118 XMRSDTypeClObj dstarray = (XMRSDTypeClObj)dstobj;
1119 TokenDeclSDTypeClass sdtClass = dstarray.sdtcClass;
1120 if (sdtClass.arrayOfType == null) {
1121 throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val);
1122 }
1123
1124 /*
1125 * Copy from the immutable array to the mutable array.
1126 * Strip off any LSL wrappers as the script code doesn't expect any.
1127 */
1128 object[] srcarr = srclist.Data;
1129 Array dstarr = (Array)dstarray.instVars.iarObjects[0];
1130
1131 for (int i = 0; i < count; i ++) {
1132 object obj = srcarr[i+srcstart];
1133 if (obj is LSL_Float) obj = ((LSL_Float)obj).value;
1134 if (obj is LSL_Integer) obj = ((LSL_Integer)obj).value;
1135 if (obj is LSL_String) obj = ((LSL_String)obj).m_string;
1136 dstarr.SetValue (obj, i + dststart);
1137 }
1138 }
1139
1140 /**
1141 * @brief Copy from an array of characters to a string.
1142 * @param srcar = the array to copy from
1143 * @param start = where to start in the array
1144 * @param count = number of elements
1145 * @returns the string
1146 */
1147 public static string xmrChars2String (object srcar, int start, int count)
1148 {
1149 /*
1150 * Make sure they gave us a script-defined array object.
1151 */
1152 XMRSDTypeClObj array = (XMRSDTypeClObj)srcar;
1153 TokenDeclSDTypeClass sdtClass = array.sdtcClass;
1154 if (sdtClass.arrayOfRank == 0) {
1155 throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val);
1156 }
1157
1158 /*
1159 * We get a type cast error from mono if they didn't give us a character array.
1160 * But if it is ok, create a string from the requested characters.
1161 */
1162 char[] srcarray = (char[])array.instVars.iarObjects[0];
1163 return new string (srcarray, start, count);
1164 }
1165
1166 /**
1167 * @brief Copy from a string to a character array.
1168 * @param srcstr = string to copy from
1169 * @param srcstart = where to start in the string
1170 * @param dstobj = array to copy to
1171 * @param dststart = where to start in the array
1172 * @param count = number of elements
1173 */
1174 public static void xmrString2Chars (string srcstr, int srcstart, object dstobj, int dststart, int count)
1175 {
1176 /*
1177 * Make sure they gave us a script-defined array object.
1178 */
1179 XMRSDTypeClObj dstarray = (XMRSDTypeClObj)dstobj;
1180 TokenDeclSDTypeClass sdtClass = dstarray.sdtcClass;
1181 if (sdtClass.arrayOfType == null) {
1182 throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val);
1183 }
1184
1185 /*
1186 * We get a type cast error from mono if they didn't give us a character array.
1187 * But if it is ok, copy from the string to the character array.
1188 */
1189 char[] dstarr = (char[])dstarray.instVars.iarObjects[0];
1190 for (int i = 0; i < count; i ++) {
1191 dstarr[i+dststart] = srcstr[i+srcstart];
1192 }
1193 }
1194
1195 /**
1196 * @brief Implement osParseJSON() so we return an array to the script.
1197 * No coherent example of its use in scripts on web found.
1198 * see http://www.json.org/ for more details on JSON
1199 */
1200 private static LSL_List nullList = new LSL_List (new object[0]);
1201 public new XMR_Array osParseJSON (string json)
1202 {
1203 XMR_Array dict = new XMR_Array (this);
1204 int idx = ParseJSON (dict, nullList, json, 0);
1205 while (idx < json.Length) {
1206 if (json[idx] > ' ') throw new Exception ("left-over json " + json);
1207 idx ++;
1208 }
1209 return dict;
1210 }
1211
1212 private static int ParseJSON (XMR_Array dict, LSL_List keys, string json, int idx)
1213 {
1214 char c;
1215
1216 while ((c = json[idx++]) <= ' ') { }
1217 switch (c) {
1218
1219 // '{' <keystring> ':' <value> [ ',' <keystring> ':' <value> ... ] '}'
1220 case '{': {
1221 do {
1222 string key = ParseJSONString (json, ref idx);
1223 while ((c = json[idx++]) <= ' ') { }
1224 if (c != ':') throw new Exception ("missing : after key");
1225 idx = ParseJSON (dict, ParseJSONKeyAdd (keys, key), json, idx);
1226 while ((c = json[idx++]) <= ' ') { }
1227 } while (c == ',');
1228 if (c != '}') throw new Exception ("missing , or } after value");
1229 break;
1230 }
1231
1232 // '[' <value> [ ',' <value> ... ] ']'
1233 case '[': {
1234 int index = 0;
1235 do {
1236 object key = index ++;
1237 idx = ParseJSON (dict, ParseJSONKeyAdd (keys, key), json, idx);
1238 while ((c = json[idx++]) <= ' ') { }
1239 } while (c == ',');
1240 if (c != ']') throw new Exception ("missing , or ] after value");
1241 break;
1242 }
1243
1244 // '"'<string>'"'
1245 case '"': {
1246 -- idx;
1247 string val = ParseJSONString (json, ref idx);
1248 dict.SetByKey (keys, val);
1249 break;
1250 }
1251
1252 // true false null
1253 case 't': {
1254 if (json.Substring (idx, 3) != "rue") throw new Exception ("bad true in json");
1255 idx += 3;
1256 dict.SetByKey (keys, 1);
1257 break;
1258 }
1259
1260 case 'f': {
1261 if (json.Substring (idx, 4) != "alse") throw new Exception ("bad false in json");
1262 idx += 4;
1263 dict.SetByKey (keys, 0);
1264 break;
1265 }
1266
1267 case 'n': {
1268 if (json.Substring (idx, 3) != "ull") throw new Exception ("bad null in json");
1269 idx += 3;
1270 dict.SetByKey (keys, null);
1271 break;
1272 }
1273
1274 // otherwise assume it's a number
1275 default: {
1276 -- idx;
1277 object val = ParseJSONNumber (json, ref idx);
1278 dict.SetByKey (keys, val);
1279 break;
1280 }
1281 }
1282
1283 return idx;
1284 }
1285
1286 // Given the key for a whole array, create a key for a given element of the array
1287 private static LSL_List ParseJSONKeyAdd (LSL_List oldkeys, object key)
1288 {
1289 int oldkeyslen = oldkeys.Length;
1290 object[] array = oldkeys.Data;
1291 Array.Resize<object> (ref array, oldkeyslen + 1);
1292 array[oldkeyslen] = key;
1293 return new LSL_List (array);
1294 }
1295
1296 // Parse out a JSON string
1297 private static string ParseJSONString (string json, ref int idx)
1298 {
1299 char c;
1300
1301 while ((c = json[idx++]) <= ' ') { }
1302 if (c != '"') throw new Exception ("bad start of json string");
1303
1304 StringBuilder sb = new StringBuilder ();
1305 while ((c = json[idx++]) != '"') {
1306 if (c == '\\') {
1307 c = json[idx++];
1308 switch (c) {
1309 case 'b': {
1310 c = '\b';
1311 break;
1312 }
1313 case 'f': {
1314 c = '\f';
1315 break;
1316 }
1317 case 'n': {
1318 c = '\n';
1319 break;
1320 }
1321 case 'r': {
1322 c = '\r';
1323 break;
1324 }
1325 case 't': {
1326 c = '\t';
1327 break;
1328 }
1329 case 'u': {
1330 c = (char) Int32.Parse (json.Substring (idx, 4),
1331 System.Globalization.NumberStyles.HexNumber);
1332 idx += 4;
1333 break;
1334 }
1335 default: break;
1336 }
1337 }
1338 sb.Append (c);
1339 }
1340 return sb.ToString ();
1341 }
1342
1343 // Parse out a JSON number
1344 private static object ParseJSONNumber (string json, ref int idx)
1345 {
1346 char c;
1347
1348 while ((c = json[idx++]) <= ' ') { }
1349
1350 bool expneg = false;
1351 bool isneg = false;
1352 int decpt = -1;
1353 int expon = 0;
1354 int ival = 0;
1355 double dval = 0;
1356
1357 if (c == '-') {
1358 isneg = true;
1359 c = json[idx++];
1360 }
1361 if ((c < '0') || (c > '9')) {
1362 throw new Exception ("bad json number");
1363 }
1364 while ((c >= '0') && (c <= '9')) {
1365 dval *= 10;
1366 ival *= 10;
1367 dval += c - '0';
1368 ival += c - '0';
1369 c = '\0';
1370 if (idx < json.Length) c = json[idx++];
1371 }
1372 if (c == '.') {
1373 decpt = 0;
1374 c = '\0';
1375 if (idx < json.Length) c = json[idx++];
1376 while ((c >= '0') && (c <= '9')) {
1377 dval *= 10;
1378 dval += c - '0';
1379 decpt ++;
1380 c = '\0';
1381 if (idx < json.Length) c = json[idx++];
1382 }
1383 }
1384 if ((c == 'e') || (c == 'E')) {
1385 if (decpt < 0) decpt = 0;
1386 c = json[idx++];
1387 if (c == '-') expneg = true;
1388 if ((c == '-') || (c == '+')) c = json[idx++];
1389 while ((c >= '0') && (c <= '9')) {
1390 expon *= 10;
1391 expon += c - '0';
1392 c = '\0';
1393 if (idx < json.Length) c = json[idx++];
1394 }
1395 if (expneg) expon = -expon;
1396 }
1397
1398 if (c != 0) -- idx;
1399 if (decpt < 0) {
1400 if (isneg) ival = -ival;
1401 return ival;
1402 } else {
1403 if (isneg) dval = -dval;
1404 dval *= Math.Pow (10, expon - decpt);
1405 return dval;
1406 }
1407 }
1408
1409 /**
1410 * @brief Exception-related runtime calls.
1411 */
1412 // Return exception message (no type information just the message)
1413 public static string xmrExceptionMessage (Exception ex)
1414 {
1415 return ex.Message;
1416 }
1417
1418 // Return stack trace (no type or message, just stack trace lines: at ... \n)
1419 public string xmrExceptionStackTrace (Exception ex)
1420 {
1421 return XMRExceptionStackString (ex);
1422 }
1423
1424 // Return value thrown by a throw statement
1425 public static object xmrExceptionThrownValue (Exception ex)
1426 {
1427 return ((ScriptThrownException)ex).thrown;
1428 }
1429
1430 // Return exception's short type name, eg, NullReferenceException, ScriptThrownException, etc.
1431 public static string xmrExceptionTypeName (Exception ex)
1432 {
1433 return ex.GetType ().Name;
1434 }
1435
1436 // internal use only: converts any IL addresses in script-defined methods to source location equivalent
1437 // at (wrapper dynamic-method) object.__seh_0_30_default_state_entry (OpenSim.Region.ScriptEngine.XMREngine.XMRInstAbstract) <IL 0x00d65, 0x03a53>
1438 public string XMRExceptionStackString (Exception ex)
1439 {
1440 string st = ex.StackTrace;
1441 StringBuilder sb = new StringBuilder ();
1442 int wrapDynMethObj = 0;
1443 int leftOffAt = 0;
1444 while ((wrapDynMethObj = st.IndexOf ("(wrapper dynamic-method) System.Object:", ++ wrapDynMethObj)) >= 0) {
1445 try {
1446 int begFuncName = wrapDynMethObj + 39;
1447 int endFuncName = st.IndexOf (" (", begFuncName);
1448 string funcName = st.Substring (begFuncName, endFuncName - begFuncName);
1449 KeyValuePair<int, ScriptSrcLoc>[] srcLocs = m_ObjCode.scriptSrcLocss[funcName];
1450
1451 int il0xPrefix = st.IndexOf (" [0x", endFuncName);
1452 int begILHex = il0xPrefix + 4;
1453 int endILHex = st.IndexOf (']', begILHex);
1454 string ilHex = st.Substring (begILHex, endILHex - begILHex);
1455 int offset = Int32.Parse (ilHex, System.Globalization.NumberStyles.HexNumber);
1456
1457 int srcLocIdx;
1458 int srcLocLen = srcLocs.Length;
1459 for (srcLocIdx = 0; ++ srcLocIdx < srcLocLen;) {
1460 if (offset < srcLocs[srcLocIdx].Key) break;
1461 }
1462 ScriptSrcLoc srcLoc = srcLocs[--srcLocIdx].Value;
1463
1464 sb.Append (st.Substring (leftOffAt, wrapDynMethObj - leftOffAt));
1465 sb.Append (st.Substring (begFuncName, endFuncName - begFuncName));
1466 sb.Append (" <");
1467 sb.Append (srcLoc.file);
1468 sb.Append ('(');
1469 sb.Append (srcLoc.line);
1470 sb.Append (',');
1471 sb.Append (srcLoc.posn);
1472 sb.Append (")>");
1473
1474 leftOffAt = ++ endILHex;
1475 } catch {
1476 }
1477 }
1478 sb.Append (st.Substring (leftOffAt));
1479 return sb.ToString ();
1480 }
1481
1482 /**
1483 * @brief List fonts available.
1484 */
1485 public LSL_List xmrFontsAvailable ()
1486 {
1487 System.Drawing.FontFamily[] families = System.Drawing.FontFamily.Families;
1488 object[] output = new object[families.Length];
1489 for (int i = 0; i < families.Length; i ++) {
1490 output[i] = new LSL_String (families[i].Name);
1491 }
1492 return new LSL_List (output);
1493 }
1494
1495 /************************\
1496 * Used by decompiler *
1497 \************************/
1498
1499 public bool xmrRotationToBool (LSL_Rotation x) { return TypeCast.RotationToBool (x); }
1500 public bool xmrStringToBool (string x) { return TypeCast.StringToBool (x); }
1501 public bool xmrVectorToBool (LSL_Vector x) { return TypeCast.VectorToBool (x); }
1502 public bool xmrKeyToBool (string x) { return TypeCast.KeyToBool (x); }
1503 public bool xmrListToBool (LSL_List x) { return TypeCast.ListToBool (x); }
1504
1505 public int xmrStringCompare (string x, string y) { return string.Compare (x, y); }
1506
1507 /**
1508 * @brief types of data we serialize
1509 */
1510 private enum Ser : byte {
1511 NULL,
1512 EVENTCODE,
1513 LSLFLOAT,
1514 LSLINT,
1515 LSLKEY,
1516 LSLLIST,
1517 LSLROT,
1518 LSLSTR,
1519 LSLVEC,
1520 SYSARRAY,
1521 SYSDOUB,
1522 SYSFLOAT,
1523 SYSINT,
1524 SYSSTR,
1525 XMRARRAY,
1526 DUPREF,
1527 SYSBOOL,
1528 XMRINST,
1529 DELEGATE,
1530 SDTCLOBJ,
1531 SYSCHAR,
1532 SYSERIAL,
1533 THROWNEX
1534 }
1535
1536 /**
1537 * @brief Write state out to a stream.
1538 * Do not change script state.
1539 */
1540 public void MigrateOut (BinaryWriter mow)
1541 {
1542 try {
1543 this.migrateOutWriter = mow;
1544 this.migrateOutObjects = new Dictionary<object,int> ();
1545 this.migrateOutLists = new Dictionary<object[],ObjLslList> ();
1546 this.SendObjValue (this.ehArgs);
1547 mow.Write (this.doGblInit);
1548 mow.Write (this.stateCode);
1549 mow.Write ((int)this.eventCode);
1550 this.glblVars.SendArrays (this.SendObjValue);
1551 if (this.newStateCode >= 0) {
1552 mow.Write ("**newStateCode**");
1553 mow.Write (this.newStateCode);
1554 }
1555 for (XMRStackFrame thisSF = this.stackFrames; thisSF != null; thisSF = thisSF.nextSF) {
1556 mow.Write (thisSF.funcName);
1557 mow.Write (thisSF.callNo);
1558 this.SendObjValue (thisSF.objArray);
1559 }
1560 mow.Write ("");
1561 } finally {
1562 this.migrateOutWriter = null;
1563 this.migrateOutObjects = null;
1564 this.migrateOutLists = null;
1565 }
1566 }
1567
1568 /**
1569 * @brief Write an object to the output stream.
1570 * @param graph = object to send
1571 */
1572 private BinaryWriter migrateOutWriter;
1573 private Dictionary<object,int> migrateOutObjects;
1574 private Dictionary<object[],ObjLslList> migrateOutLists;
1575 public void SendObjValue (object graph)
1576 {
1577 BinaryWriter mow = this.migrateOutWriter;
1578
1579 /*
1580 * Value types (including nulls) are always output directly.
1581 */
1582 if (graph == null) {
1583 mow.Write ((byte)Ser.NULL);
1584 return;
1585 }
1586 if (graph is ScriptEventCode) {
1587 mow.Write ((byte)Ser.EVENTCODE);
1588 mow.Write ((int)graph);
1589 return;
1590 }
1591 if (graph is LSL_Float) {
1592 mow.Write ((byte)Ser.LSLFLOAT);
1593 mow.Write ((double)((LSL_Float)graph).value);
1594 return;
1595 }
1596 if (graph is LSL_Integer) {
1597 mow.Write ((byte)Ser.LSLINT);
1598 mow.Write ((int)((LSL_Integer)graph).value);
1599 return;
1600 }
1601 if (graph is LSL_Key) {
1602 mow.Write ((byte)Ser.LSLKEY);
1603 LSL_Key key = (LSL_Key)graph;
1604 SendObjValue (key.m_string); // m_string can be null
1605 return;
1606 }
1607 if (graph is LSL_Rotation) {
1608 mow.Write ((byte)Ser.LSLROT);
1609 mow.Write ((double)((LSL_Rotation)graph).x);
1610 mow.Write ((double)((LSL_Rotation)graph).y);
1611 mow.Write ((double)((LSL_Rotation)graph).z);
1612 mow.Write ((double)((LSL_Rotation)graph).s);
1613 return;
1614 }
1615 if (graph is LSL_String) {
1616 mow.Write ((byte)Ser.LSLSTR);
1617 LSL_String str = (LSL_String)graph;
1618 SendObjValue (str.m_string); // m_string can be null
1619 return;
1620 }
1621 if (graph is LSL_Vector) {
1622 mow.Write ((byte)Ser.LSLVEC);
1623 mow.Write ((double)((LSL_Vector)graph).x);
1624 mow.Write ((double)((LSL_Vector)graph).y);
1625 mow.Write ((double)((LSL_Vector)graph).z);
1626 return;
1627 }
1628 if (graph is bool) {
1629 mow.Write ((byte)Ser.SYSBOOL);
1630 mow.Write ((bool)graph);
1631 return;
1632 }
1633 if (graph is double) {
1634 mow.Write ((byte)Ser.SYSDOUB);
1635 mow.Write ((double)graph);
1636 return;
1637 }
1638 if (graph is float) {
1639 mow.Write ((byte)Ser.SYSFLOAT);
1640 mow.Write ((float)graph);
1641 return;
1642 }
1643 if (graph is int) {
1644 mow.Write ((byte)Ser.SYSINT);
1645 mow.Write ((int)graph);
1646 return;
1647 }
1648 if (graph is char) {
1649 mow.Write ((byte)Ser.SYSCHAR);
1650 mow.Write ((char)graph);
1651 return;
1652 }
1653
1654 /*
1655 * Script instance pointer is always just that.
1656 */
1657 if (graph == this) {
1658 mow.Write ((byte)Ser.XMRINST);
1659 return;
1660 }
1661
1662 /*
1663 * Convert lists to object type.
1664 * This is compatible with old migration data and also
1665 * two vars pointing to same list won't duplicate it.
1666 */
1667 if (graph is LSL_List) {
1668 object[] data = ((LSL_List) graph).Data;
1669 ObjLslList oll;
1670 if (!this.migrateOutLists.TryGetValue (data, out oll)) {
1671 oll = new ObjLslList ();
1672 oll.objarray = data;
1673 this.migrateOutLists[data] = oll;
1674 }
1675 graph = oll;
1676 }
1677
1678 /*
1679 * If this same exact object was already serialized,
1680 * just output an index telling the receiver to use
1681 * that same old object, rather than creating a whole
1682 * new object with the same values. Also this prevents
1683 * self-referencing objects (like arrays) from causing
1684 * an infinite loop.
1685 */
1686 int ident;
1687 if (this.migrateOutObjects.TryGetValue (graph, out ident)) {
1688 mow.Write ((byte)Ser.DUPREF);
1689 mow.Write (ident);
1690 return;
1691 }
1692
1693 /*
1694 * Object not seen before, save its address with an unique
1695 * ident number that the receiver can easily regenerate.
1696 */
1697 ident = this.migrateOutObjects.Count;
1698 this.migrateOutObjects.Add (graph, ident);
1699
1700 /*
1701 * Now output the object's value(s).
1702 * If the object self-references, the object is alreay entered
1703 * in the dictionary and so the self-reference will just emit
1704 * a DUPREF tag instead of trying to output the whole object
1705 * again.
1706 */
1707 if (graph is ObjLslList) {
1708 mow.Write ((byte)Ser.LSLLIST);
1709 ObjLslList oll = (ObjLslList) graph;
1710 SendObjValue (oll.objarray);
1711 } else if (graph is XMR_Array) {
1712 mow.Write ((byte)Ser.XMRARRAY);
1713 ((XMR_Array)graph).SendArrayObj (this.SendObjValue);
1714 } else if (graph is Array) {
1715 Array array = (Array)graph;
1716 mow.Write ((byte)Ser.SYSARRAY);
1717 mow.Write (SysType2String (array.GetType ().GetElementType ()));
1718 mow.Write ((int)array.Length);
1719 for (int i = 0; i < array.Length; i ++) {
1720 this.SendObjValue (array.GetValue (i));
1721 }
1722 } else if (graph is string) {
1723 mow.Write ((byte)Ser.SYSSTR);
1724 mow.Write ((string)graph);
1725 } else if (graph is Delegate) {
1726 Delegate del = (Delegate)graph;
1727 mow.Write ((byte)Ser.DELEGATE);
1728 mow.Write (del.Method.Name);
1729 Type delType = del.GetType ();
1730 foreach (KeyValuePair<string, TokenDeclSDType> kvp in m_ObjCode.sdObjTypesName) {
1731 TokenDeclSDType sdt = kvp.Value;
1732 if (sdt is TokenDeclSDTypeDelegate) {
1733 TokenDeclSDTypeDelegate sdtd = (TokenDeclSDTypeDelegate)sdt;
1734 if (sdtd.GetSysType () == delType) {
1735 mow.Write (kvp.Key);
1736 goto found;
1737 }
1738 }
1739 }
1740 throw new Exception ("cant find script-defined delegate for " + del.Method.Name + " type " + del.GetType ());
1741 found:
1742 SendObjValue (del.Target);
1743 } else if (graph is XMRSDTypeClObj) {
1744 mow.Write ((byte)Ser.SDTCLOBJ);
1745 ((XMRSDTypeClObj)graph).Capture (this.SendObjValue);
1746 } else if (graph is ScriptThrownException) {
1747 MemoryStream memoryStream = new MemoryStream ();
1748 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter =
1749 new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
1750 bformatter.Serialize (memoryStream, graph);
1751 byte[] rawBytes = memoryStream.ToArray ();
1752 mow.Write ((byte)Ser.THROWNEX);
1753 mow.Write ((int)rawBytes.Length);
1754 mow.Write (rawBytes);
1755 SendObjValue (((ScriptThrownException)graph).thrown);
1756 } else {
1757 MemoryStream memoryStream = new MemoryStream ();
1758 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter =
1759 new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
1760 bformatter.Serialize (memoryStream, graph);
1761 byte[] rawBytes = memoryStream.ToArray ();
1762 mow.Write ((byte)Ser.SYSERIAL);
1763 mow.Write ((int)rawBytes.Length);
1764 mow.Write (rawBytes);
1765 }
1766 }
1767
1768 /**
1769 * @brief Use short strings for known type names.
1770 */
1771 private static string SysType2String (Type type)
1772 {
1773 if (type.IsArray && (type.GetArrayRank () == 1)) {
1774 string str = KnownSysType2String (type.GetElementType ());
1775 if (str != null) return str + "[]";
1776 } else {
1777 string str = KnownSysType2String (type);
1778 if (str != null) return str;
1779 }
1780 return type.ToString ();
1781 }
1782 private static string KnownSysType2String (Type type)
1783 {
1784 if (type == typeof (bool)) return "bo";
1785 if (type == typeof (char)) return "ch";
1786 if (type == typeof (Delegate)) return "de";
1787 if (type == typeof (double)) return "do";
1788 if (type == typeof (float)) return "fl";
1789 if (type == typeof (int)) return "in";
1790 if (type == typeof (LSL_List)) return "li";
1791 if (type == typeof (object)) return "ob";
1792 if (type == typeof (LSL_Rotation)) return "ro";
1793 if (type == typeof (XMRSDTypeClObj)) return "sc";
1794 if (type == typeof (string)) return "st";
1795 if (type == typeof (LSL_Vector)) return "ve";
1796 if (type == typeof (XMR_Array)) return "xa";
1797 return null;
1798 }
1799 private static Type String2SysType (string str)
1800 {
1801 if (str.EndsWith ("[]")) {
1802 return String2SysType (str.Substring (0, str.Length - 2)).MakeArrayType ();
1803 }
1804 if (str == "bo") return typeof (bool);
1805 if (str == "ch") return typeof (char);
1806 if (str == "de") return typeof (Delegate);
1807 if (str == "do") return typeof (double);
1808 if (str == "fl") return typeof (float);
1809 if (str == "in") return typeof (int);
1810 if (str == "li") return typeof (LSL_List);
1811 if (str == "ob") return typeof (object);
1812 if (str == "ro") return typeof (LSL_Rotation);
1813 if (str == "sc") return typeof (XMRSDTypeClObj);
1814 if (str == "st") return typeof (string);
1815 if (str == "ve") return typeof (LSL_Vector);
1816 if (str == "xa") return typeof (XMR_Array);
1817 return Type.GetType (str, true);
1818 }
1819
1820 /**
1821 * @brief Read state in from a stream.
1822 */
1823 public void MigrateIn (BinaryReader mir)
1824 {
1825 try {
1826 this.migrateInReader = mir;
1827 this.migrateInObjects = new Dictionary<int, object> ();
1828 this.ehArgs = (object[])this.RecvObjValue ();
1829 this.doGblInit = mir.ReadBoolean ();
1830 this.stateCode = mir.ReadInt32 ();
1831 this.eventCode = (ScriptEventCode)mir.ReadInt32 ();
1832 this.newStateCode = -1;
1833 this.glblVars.RecvArrays (this.RecvObjValue);
1834 XMRStackFrame lastSF = null;
1835 string funcName;
1836 while ((funcName = mir.ReadString ()) != "") {
1837 if (funcName == "**newStateCode**") {
1838 this.newStateCode = mir.ReadInt32 ();
1839 continue;
1840 }
1841 XMRStackFrame thisSF = new XMRStackFrame ();
1842 thisSF.funcName = funcName;
1843 thisSF.callNo = mir.ReadInt32 ();
1844 thisSF.objArray = (object[])this.RecvObjValue ();
1845 if (lastSF == null) this.stackFrames = thisSF;
1846 else lastSF.nextSF = thisSF;
1847 lastSF = thisSF;
1848 }
1849 } finally {
1850 this.migrateInReader = null;
1851 this.migrateInObjects = null;
1852 }
1853 }
1854
1855 /**
1856 * @brief Read a single value from the stream.
1857 * @returns value (boxed as needed)
1858 */
1859 private BinaryReader migrateInReader;
1860 private Dictionary<int, object> migrateInObjects;
1861 public object RecvObjValue ()
1862 {
1863 BinaryReader mir = this.migrateInReader;
1864 int ident = this.migrateInObjects.Count;
1865 Ser code = (Ser)mir.ReadByte ();
1866 switch (code) {
1867 case Ser.NULL: {
1868 return null;
1869 }
1870 case Ser.EVENTCODE: {
1871 return (ScriptEventCode)mir.ReadInt32 ();
1872 }
1873 case Ser.LSLFLOAT: {
1874 return new LSL_Float (mir.ReadDouble ());
1875 }
1876 case Ser.LSLINT: {
1877 return new LSL_Integer (mir.ReadInt32 ());
1878 }
1879 case Ser.LSLKEY: {
1880 return new LSL_Key ((string)RecvObjValue ());
1881 }
1882 case Ser.LSLLIST: {
1883 this.migrateInObjects.Add (ident, null); // placeholder
1884 object[] data = (object[])RecvObjValue (); // read data, maybe using another index
1885 LSL_List list = new LSL_List (data); // make LSL-level list
1886 this.migrateInObjects[ident] = list; // fill in slot
1887 return list;
1888 }
1889 case Ser.LSLROT: {
1890 double x = mir.ReadDouble ();
1891 double y = mir.ReadDouble ();
1892 double z = mir.ReadDouble ();
1893 double s = mir.ReadDouble ();
1894 return new LSL_Rotation (x, y, z, s);
1895 }
1896 case Ser.LSLSTR: {
1897 return new LSL_String ((string)RecvObjValue ());
1898 }
1899 case Ser.LSLVEC: {
1900 double x = mir.ReadDouble ();
1901 double y = mir.ReadDouble ();
1902 double z = mir.ReadDouble ();
1903 return new LSL_Vector (x, y, z);
1904 }
1905 case Ser.SYSARRAY: {
1906 Type eletype = String2SysType (mir.ReadString ());
1907 int length = mir.ReadInt32 ();
1908 Array array = Array.CreateInstance (eletype, length);
1909 this.migrateInObjects.Add (ident, array);
1910 for (int i = 0; i < length; i ++) {
1911 array.SetValue (RecvObjValue (), i);
1912 }
1913 return array;
1914 }
1915 case Ser.SYSBOOL: {
1916 return mir.ReadBoolean ();
1917 }
1918 case Ser.SYSDOUB: {
1919 return mir.ReadDouble ();
1920 }
1921 case Ser.SYSFLOAT: {
1922 return mir.ReadSingle ();
1923 }
1924 case Ser.SYSINT: {
1925 return mir.ReadInt32 ();
1926 }
1927 case Ser.SYSCHAR: {
1928 return mir.ReadChar ();
1929 }
1930 case Ser.SYSSTR: {
1931 string s = mir.ReadString ();
1932 this.migrateInObjects.Add (ident, s);
1933 return s;
1934 }
1935 case Ser.XMRARRAY: {
1936 XMR_Array array = new XMR_Array (this);
1937 this.migrateInObjects.Add (ident, array);
1938 array.RecvArrayObj (this.RecvObjValue);
1939 return array;
1940 }
1941 case Ser.DUPREF: {
1942 ident = mir.ReadInt32 ();
1943 object obj = this.migrateInObjects[ident];
1944 if (obj is ObjLslList) obj = new LSL_List (((ObjLslList) obj).objarray);
1945 return obj;
1946 }
1947 case Ser.XMRINST: {
1948 return this;
1949 }
1950 case Ser.DELEGATE: {
1951 this.migrateInObjects.Add (ident, null); // placeholder
1952 string name = mir.ReadString (); // function name
1953 string sig = mir.ReadString (); // delegate type
1954 object targ = this.RecvObjValue (); // 'this' object
1955 Delegate del = this.GetScriptMethodDelegate (name, sig, targ);
1956 this.migrateInObjects[ident] = del; // actual value
1957 return del;
1958 }
1959 case Ser.SDTCLOBJ: {
1960 XMRSDTypeClObj clobj = new XMRSDTypeClObj ();
1961 this.migrateInObjects.Add (ident, clobj);
1962 clobj.Restore (this, this.RecvObjValue);
1963 return clobj;
1964 }
1965 case Ser.SYSERIAL: {
1966 int rawLength = mir.ReadInt32 ();
1967 byte[] rawBytes = mir.ReadBytes (rawLength);
1968 MemoryStream memoryStream = new MemoryStream (rawBytes);
1969 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter =
1970 new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
1971 object graph = bformatter.Deserialize (memoryStream);
1972 this.migrateInObjects.Add (ident, graph);
1973 return graph;
1974 }
1975 case Ser.THROWNEX: {
1976 int rawLength = mir.ReadInt32 ();
1977 byte[] rawBytes = mir.ReadBytes (rawLength);
1978 MemoryStream memoryStream = new MemoryStream (rawBytes);
1979 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter =
1980 new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
1981 object graph = bformatter.Deserialize (memoryStream);
1982 this.migrateInObjects.Add (ident, graph);
1983 ((ScriptThrownException)graph).thrown = RecvObjValue ();
1984 return graph;
1985 }
1986 default: throw new Exception ("bad stream code " + code.ToString ());
1987 }
1988 }
1989
1990 // wrapper around list object arrays to make sure they are always object types for migration purposes
1991 private class ObjLslList {
1992 public object[] objarray;
1993 }
1994 }
1995
1996 /**
1997 * @brief Common access to script microthread.
1998 */
1999 public interface IScriptUThread : IDisposable
2000 {
2001 Exception ResumeEx (); // called by macrothread to resume execution at most recent Hiber()
2002 Exception StartEx (); // called by macrothread to start execution at CallSEH()
2003 int Active (); // called by macrothread to query state of microthread
2004 int StackLeft (); // called by microthread to query amount of remaining stack space
2005 void Hiber (); // called by microthread to hibernate
2006 }
2007
2008 // Any xmr...() methods that call CheckRun() must be tagged with this attribute
2009 // so the ScriptCodeGen will know the method is non-trivial.
2010 public class xmrMethodCallsCheckRunAttribute : Attribute { }
2011
2012 // Any xmr...() methods in xmrengtest that call Stub<somethingorother>() must be
2013 // tagged with this attribute so the -builtins option will tell the user that
2014 // they are a stub function.
2015 public class xmrMethodIsNoisyAttribute : Attribute { }
2016
2017 // Any script callable methods that really return a key not a string should be
2018 // tagged with this attribute so the compiler will know they return type key and
2019 // not type string.
2020 public class xmrMethodReturnsKeyAttribute : Attribute { }
2021
2022 [SerializableAttribute]
2023 public class OutOfHeapException : Exception {
2024 public OutOfHeapException (int oldtotal, int newtotal, int limit)
2025 : base ("oldtotal=" + oldtotal + ", newtotal=" + newtotal + ", limit=" + limit)
2026 { }
2027 }
2028
2029 [SerializableAttribute]
2030 public class OutOfStackException : Exception { }
2031}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs
new file mode 100644
index 0000000..acf1e66
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs
@@ -0,0 +1,644 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Threading;
30using System.Reflection;
31using System.Collections;
32using System.Collections.Generic;
33using System.Runtime.Remoting.Lifetime;
34using System.Security.Policy;
35using System.IO;
36using System.Xml;
37using System.Text;
38using OpenMetaverse;
39using OpenSim.Framework;
40using OpenSim.Region.ScriptEngine.Interfaces;
41using OpenSim.Region.ScriptEngine.Shared;
42using OpenSim.Region.ScriptEngine.Shared.Api;
43using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
44using OpenSim.Region.ScriptEngine.XMREngine;
45using OpenSim.Region.Framework.Scenes;
46using OpenSim.Region.Framework.Scenes.Scripting;
47using OpenSim.Region.Framework.Interfaces;
48using log4net;
49
50using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
51using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
52using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
53using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
54using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
55using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
56using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
57
58namespace OpenSim.Region.ScriptEngine.XMREngine
59{
60 /****************************************************\
61 * This file contains routines called by scripts. *
62 \****************************************************/
63
64 public class XMRLSL_Api : LSL_Api
65 {
66 public AsyncCommandManager acm;
67 private XMRInstance inst;
68
69 public void InitXMRLSLApi(XMRInstance i)
70 {
71 acm = AsyncCommands;
72 inst = i;
73 }
74
75 protected override void ScriptSleep(int ms)
76 {
77 inst.Sleep(ms);
78 }
79
80 public override void llSleep(double sec)
81 {
82 inst.Sleep((int)(sec * 1000.0));
83 }
84
85 public override void llDie()
86 {
87 inst.Die();
88 }
89
90 /**
91 * @brief Seat avatar on prim.
92 * @param owner = true: owner of prim script is running in
93 * false: avatar that has given ANIMATION permission on the prim
94 * @returns 0: successful
95 * -1: no permission to animate
96 * -2: no av granted perms
97 * -3: av not in region
98 */
99 public int xmrSeatAvatar (bool owner)
100 {
101 // Get avatar to be seated and make sure they have given us ANIMATION permission
102
103 UUID avuuid;
104 if (owner) {
105 avuuid = inst.m_Part.OwnerID;
106 } else {
107 if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION) == 0) {
108 return -1;
109 }
110 avuuid = m_item.PermsGranter;
111 }
112 if (avuuid == UUID.Zero) {
113 return -2;
114 }
115
116 ScenePresence presence = World.GetScenePresence (avuuid);
117 if (presence == null) {
118 return -3;
119 }
120
121 // remoteClient = not used by ScenePresence.HandleAgentRequestSit()
122 // agentID = not used by ScenePresence.HandleAgentRequestSit()
123 // targetID = UUID of prim to sit on
124 // offset = offset of sitting position
125
126 presence.HandleAgentRequestSit (null, UUID.Zero, m_host.UUID, OpenMetaverse.Vector3.Zero);
127 return 0;
128 }
129
130 /**
131 * @brief llTeleportAgent() is broken in that if you pass it a landmark,
132 * it still subjects the position to spawn points, as it always
133 * calls RequestTeleportLocation() with TeleportFlags.ViaLocation.
134 * See llTeleportAgent() and CheckAndAdjustTelehub().
135 *
136 * @param agent = what agent to teleport
137 * @param landmark = inventory name or UUID of a landmark object
138 * @param lookat = looking direction after teleport
139 */
140 public void xmrTeleportAgent2Landmark (string agent, string landmark, LSL_Vector lookat)
141 {
142 // find out about agent to be teleported
143 UUID agentId;
144 if (!UUID.TryParse (agent, out agentId)) throw new ApplicationException ("bad agent uuid");
145
146 ScenePresence presence = World.GetScenePresence (agentId);
147 if (presence == null) throw new ApplicationException ("agent not present in scene");
148 if (presence.IsNPC) throw new ApplicationException ("agent is an NPC");
149 if (presence.IsGod) throw new ApplicationException ("agent is a god");
150
151 // prim must be owned by land owner or prim must be attached to agent
152 if (m_host.ParentGroup.AttachmentPoint == 0) {
153 if (m_host.OwnerID != World.LandChannel.GetLandObject (presence.AbsolutePosition).LandData.OwnerID) {
154 throw new ApplicationException ("prim not owned by land's owner");
155 }
156 } else {
157 if (m_host.OwnerID != presence.UUID) throw new ApplicationException ("prim not attached to agent");
158 }
159
160 // find landmark in inventory or by UUID
161 UUID assetID = ScriptUtils.GetAssetIdFromKeyOrItemName (m_host, landmark);
162 if (assetID == UUID.Zero) throw new ApplicationException ("no such landmark");
163
164 // read it in and make sure it is a landmark
165 AssetBase lma = World.AssetService.Get (assetID.ToString ());
166 if ((lma == null) || (lma.Type != (sbyte)AssetType.Landmark)) throw new ApplicationException ("not a landmark");
167
168 // parse the record
169 AssetLandmark lm = new AssetLandmark (lma);
170
171 // the regionhandle (based on region's world X,Y) might be out of date
172 // re-read the handle so we can pass it to RequestTeleportLocation()
173 var region = World.GridService.GetRegionByUUID (World.RegionInfo.ScopeID, lm.RegionID);
174 if (region == null) throw new ApplicationException ("no such region");
175
176 // finally ready to teleport
177 World.RequestTeleportLocation (presence.ControllingClient,
178 region.RegionHandle,
179 lm.Position,
180 lookat,
181 (uint)TeleportFlags.ViaLandmark);
182 }
183
184 /**
185 * @brief Allow any member of group given by config SetParcelMusicURLGroup to set music URL.
186 * Code modelled after llSetParcelMusicURL().
187 * @param newurl = new URL to set (or "" to leave it alone)
188 * @returns previous URL string
189 */
190 public string xmrSetParcelMusicURLGroup (string newurl)
191 {
192 string groupname = m_ScriptEngine.Config.GetString ("SetParcelMusicURLGroup", "");
193 if (groupname == "") throw new ApplicationException ("no SetParcelMusicURLGroup config param set");
194
195 IGroupsModule igm = World.RequestModuleInterface<IGroupsModule> ();
196 if (igm == null) throw new ApplicationException ("no GroupsModule loaded");
197
198 GroupRecord grouprec = igm.GetGroupRecord (groupname);
199 if (grouprec == null) throw new ApplicationException ("no such group " + groupname);
200
201 GroupMembershipData gmd = igm.GetMembershipData (grouprec.GroupID, m_host.OwnerID);
202 if (gmd == null) throw new ApplicationException ("not a member of group " + groupname);
203
204 ILandObject land = World.LandChannel.GetLandObject (m_host.AbsolutePosition);
205 if (land == null) throw new ApplicationException ("no land at " + m_host.AbsolutePosition.ToString ());
206 string oldurl = land.GetMusicUrl ();
207 if (oldurl == null) oldurl = "";
208 if ((newurl != null) && (newurl != "")) land.SetMusicUrl (newurl);
209 return oldurl;
210 }
211 }
212
213 public partial class XMRInstance
214 {
215 /**
216 * @brief The script is calling llReset().
217 * We throw an exception to unwind the script out to its main
218 * causing all the finally's to execute and it will also set
219 * eventCode = None to indicate event handler has completed.
220 */
221 public void ApiReset()
222 {
223 ClearQueueExceptLinkMessages();
224 throw new ScriptResetException();
225 }
226
227 /**
228 * @brief The script is calling one of the llDetected...(int number)
229 * functions. Return corresponding DetectParams pointer.
230 */
231 public DetectParams GetDetectParams(int number)
232 {
233 DetectParams dp = null;
234 if ((number >= 0) && (m_DetectParams != null) && (number < m_DetectParams.Length)) {
235 dp = m_DetectParams[number];
236 }
237 return dp;
238 }
239
240 /**
241 * @brief Script is calling llDie, so flag the run loop to delete script
242 * once we are off the microthread stack, and throw an exception
243 * to unwind the stack asap.
244 */
245 public void Die()
246 {
247 // llDie doesn't work in attachments!
248 if (m_Part.ParentGroup.IsAttachment || m_DetachQuantum > 0)
249 return;
250
251 throw new ScriptDieException();
252 }
253
254 /**
255 * @brief Called by script to sleep for the given number of milliseconds.
256 */
257 public void Sleep(int ms)
258 {
259 lock (m_QueueLock) {
260
261 /*
262 * Say how long to sleep.
263 */
264 m_SleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds(ms);
265
266 /*
267 * Don't wake on any events.
268 */
269 m_SleepEventMask1 = 0;
270 m_SleepEventMask2 = 0;
271 }
272
273 /*
274 * The compiler follows all calls to llSleep() with a call to CheckRun().
275 * So tell CheckRun() to suspend the microthread.
276 */
277 suspendOnCheckRunTemp = true;
278 }
279
280 /**
281 * Block script execution until an event is queued or a timeout is reached.
282 * @param timeout = maximum number of seconds to wait
283 * @param returnMask = if event is queued that matches these mask bits,
284 * the script is woken, that event is dequeued and
285 * returned to the caller. The event handler is not
286 * executed.
287 * @param backgroundMask = if any of these events are queued while waiting,
288 * execute their event handlers. When any such event
289 * handler exits, continue waiting for events or the
290 * timeout.
291 * @returns empty list: no event was queued that matched returnMask and the timeout was reached
292 * or a background event handler changed state (eg, via 'state' statement)
293 * else: list giving parameters of the event:
294 * [0] = event code (integer)
295 * [1..n] = call parameters to the event, if any
296 * Notes:
297 * 1) Scrips should use XMREVENTMASKn_<eventname> symbols for the mask arguments,
298 * where n is 1 or 2 for mask1 or mask2 arguments.
299 * The list[0] return argument can be decoded by using XMREVENTCODE_<eventname> symbols.
300 * 2) If all masks are zero, the call ends up acting like llSleep.
301 * 3) If an event is enabled in both returnMask and backgroundMask, the returnMask bit
302 * action takes precedence, ie, the event is returned. This allows a simple specification
303 * of -1 for both backgroundMask arguments to indicate that all events not listed in
304 * the returnMask argumetns should be handled in the background.
305 * 4) Any events not listed in either returnMask or backgroundMask arguments will be
306 * queued for later processing (subject to normal queue limits).
307 * 5) Background event handlers execute as calls from within xmrEventDequeue, they do
308 * not execute as separate threads. Thus any background event handlers must return
309 * before the call to xmrEventDequeue will return.
310 * 6) If a background event handler changes state (eg, via 'state' statement), the state
311 * is immediately changed and the script-level xmrEventDequeue call does not return.
312 * 7) For returned events, the detect parameters are overwritten by the returned event.
313 * For background events, the detect parameters are saved and restored.
314 * 8) Scripts must contain dummy event handler definitions for any event types that may
315 * be returned by xmrEventDequeue, to let the runtime know that the script is capable
316 * of processing that event type. Otherwise, the event may not be queued to the script.
317 */
318 private static LSL_List emptyList = new LSL_List (new object[0]);
319
320 public override LSL_List xmrEventDequeue (double timeout, int returnMask1, int returnMask2,
321 int backgroundMask1, int backgroundMask2)
322 {
323 DateTime sleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds (timeout * 1000.0);
324 EventParams evt = null;
325 int callNo, evc2;
326 int evc1 = 0;
327 int mask1 = returnMask1 | backgroundMask1; // codes 00..31
328 int mask2 = returnMask2 | backgroundMask2; // codes 32..63
329 LinkedListNode<EventParams> lln = null;
330 object[] sv;
331 ScriptEventCode evc = ScriptEventCode.None;
332
333 callNo = -1;
334 try {
335 if (callMode == CallMode_NORMAL) goto findevent;
336
337 /*
338 * Stack frame is being restored as saved via CheckRun...().
339 * Restore necessary values then jump to __call<n> label to resume processing.
340 */
341 sv = RestoreStackFrame ("xmrEventDequeue", out callNo);
342 sleepUntil = DateTime.Parse ((string)sv[0]);
343 returnMask1 = (int)sv[1];
344 returnMask2 = (int)sv[2];
345 mask1 = (int)sv[3];
346 mask2 = (int)sv[4];
347 switch (callNo) {
348 case 0: goto __call0;
349 case 1: {
350 evc1 = (int)sv[5];
351 evc = (ScriptEventCode)(int)sv[6];
352 DetectParams[] detprms = ObjArrToDetPrms ((object[])sv[7]);
353 object[] ehargs = (object[])sv[8];
354 evt = new EventParams (evc.ToString (), ehargs, detprms);
355 goto __call1;
356 }
357 }
358 throw new ScriptBadCallNoException (callNo);
359
360 /*
361 * Find first event that matches either the return or background masks.
362 */
363 findevent:
364 Monitor.Enter (m_QueueLock);
365 for (lln = m_EventQueue.First; lln != null; lln = lln.Next) {
366 evt = lln.Value;
367 evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), evt.EventName);
368 evc1 = (int)evc;
369 evc2 = evc1 - 32;
370 if ((((uint)evc1 < (uint)32) && (((mask1 >> evc1) & 1) != 0)) ||
371 (((uint)evc2 < (uint)32) && (((mask2 >> evc2) & 1) != 0))) goto remfromq;
372 }
373
374 /*
375 * Nothing found, sleep while one comes in.
376 */
377 m_SleepUntil = sleepUntil;
378 m_SleepEventMask1 = mask1;
379 m_SleepEventMask2 = mask2;
380 Monitor.Exit (m_QueueLock);
381 suspendOnCheckRunTemp = true;
382 callNo = 0;
383 __call0:
384 CheckRunQuick ();
385 goto checktmo;
386
387 /*
388 * Found one, remove it from queue.
389 */
390 remfromq:
391 m_EventQueue.Remove (lln);
392 if ((uint)evc1 < (uint)m_EventCounts.Length) {
393 m_EventCounts[evc1] --;
394 }
395 Monitor.Exit (m_QueueLock);
396 m_InstEHEvent ++;
397
398 /*
399 * See if returnable or background event.
400 */
401 if ((((uint)evc1 < (uint)32) && (((returnMask1 >> evc1) & 1) != 0)) ||
402 (((uint)evc2 < (uint)32) && (((returnMask2 >> evc2) & 1) != 0))) {
403
404 /*
405 * Returnable event, return its parameters in a list.
406 * Also set the detect parameters to what the event has.
407 */
408 int plen = evt.Params.Length;
409 object[] plist = new object[plen+1];
410 plist[0] = (LSL_Integer)evc1;
411 for (int i = 0; i < plen;) {
412 object ob = evt.Params[i];
413 if (ob is int) ob = (LSL_Integer)(int)ob;
414 else if (ob is double) ob = (LSL_Float)(double)ob;
415 else if (ob is string) ob = (LSL_String)(string)ob;
416 plist[++i] = ob;
417 }
418 m_DetectParams = evt.DetectParams;
419 return new LSL_List (plist);
420 }
421
422 /*
423 * It is a background event, simply call its event handler,
424 * then check event queue again.
425 */
426 callNo = 1;
427 __call1:
428 ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode,evc1];
429 if (seh == null) goto checktmo;
430
431 DetectParams[] saveDetParams = this.m_DetectParams;
432 object[] saveEHArgs = this.ehArgs;
433 ScriptEventCode saveEventCode = this.eventCode;
434
435 this.m_DetectParams = evt.DetectParams;
436 this.ehArgs = evt.Params;
437 this.eventCode = evc;
438
439 try {
440 seh (this);
441 } finally {
442 this.m_DetectParams = saveDetParams;
443 this.ehArgs = saveEHArgs;
444 this.eventCode = saveEventCode;
445 }
446
447 /*
448 * Keep waiting until we find a returnable event or timeout.
449 */
450 checktmo:
451 if (DateTime.UtcNow < sleepUntil) goto findevent;
452
453 /*
454 * We timed out, return an empty list.
455 */
456 return emptyList;
457 } finally {
458 if (callMode != CallMode_NORMAL) {
459
460 /*
461 * Stack frame is being saved by CheckRun...().
462 * Save everything we need at the __call<n> labels so we can restore it
463 * when we need to.
464 */
465 sv = CaptureStackFrame ("xmrEventDequeue", callNo, 9);
466 sv[0] = sleepUntil.ToString (); // needed at __call0,__call1
467 sv[1] = returnMask1; // needed at __call0,__call1
468 sv[2] = returnMask2; // needed at __call0,__call1
469 sv[3] = mask1; // needed at __call0,__call1
470 sv[4] = mask2; // needed at __call0,__call1
471 if (callNo == 1) {
472 sv[5] = evc1; // needed at __call1
473 sv[6] = (int)evc; // needed at __call1
474 sv[7] = DetPrmsToObjArr (evt.DetectParams); // needed at __call1
475 sv[8] = evt.Params; // needed at __call1
476 }
477 }
478 }
479 }
480
481 /**
482 * @brief Enqueue an event
483 * @param ev = as returned by xmrEventDequeue saying which event type to queue
484 * and what argument list to pass to it. The llDetect...() parameters
485 * are as currently set for the script (use xmrEventLoadDets to set how
486 * you want them to be different).
487 */
488 public override void xmrEventEnqueue (LSL_List ev)
489 {
490 object[] data = ev.Data;
491 ScriptEventCode evc = (ScriptEventCode)ListInt (data[0]);
492
493 int nargs = data.Length - 1;
494 object[] args = new object[nargs];
495 Array.Copy (data, 1, args, 0, nargs);
496
497 PostEvent (new EventParams (evc.ToString (), args, m_DetectParams));
498 }
499
500 /**
501 * @brief Save current detect params into a list
502 * @returns a list containing current detect param values
503 */
504 private const int saveDPVer = 1;
505
506 public override LSL_List xmrEventSaveDets ()
507 {
508 object[] obs = DetPrmsToObjArr (m_DetectParams);
509 return new LSL_List (obs);
510 }
511
512 private static object[] DetPrmsToObjArr (DetectParams[] dps)
513 {
514 int len = dps.Length;
515 object[] obs = new object[len*16+1];
516 int j = 0;
517 obs[j++] = (LSL_Integer)saveDPVer;
518 for (int i = 0; i < len; i ++) {
519 DetectParams dp = dps[i];
520 obs[j++] = (LSL_String)dp.Key.ToString(); // UUID
521 obs[j++] = dp.OffsetPos; // vector
522 obs[j++] = (LSL_Integer)dp.LinkNum; // integer
523 obs[j++] = (LSL_String)dp.Group.ToString(); // UUID
524 obs[j++] = (LSL_String)dp.Name; // string
525 obs[j++] = (LSL_String)dp.Owner.ToString(); // UUID
526 obs[j++] = dp.Position; // vector
527 obs[j++] = dp.Rotation; // rotation
528 obs[j++] = (LSL_Integer)dp.Type; // integer
529 obs[j++] = dp.Velocity; // vector
530 obs[j++] = dp.TouchST; // vector
531 obs[j++] = dp.TouchNormal; // vector
532 obs[j++] = dp.TouchBinormal; // vector
533 obs[j++] = dp.TouchPos; // vector
534 obs[j++] = dp.TouchUV; // vector
535 obs[j++] = (LSL_Integer)dp.TouchFace; // integer
536 }
537 return obs;
538 }
539
540
541 /**
542 * @brief Load current detect params from a list
543 * @param dpList = as returned by xmrEventSaveDets()
544 */
545 public override void xmrEventLoadDets (LSL_List dpList)
546 {
547 m_DetectParams = ObjArrToDetPrms (dpList.Data);
548 }
549
550 private static DetectParams[] ObjArrToDetPrms (object[] objs)
551 {
552 int j = 0;
553 if ((objs.Length % 16 != 1) || (ListInt (objs[j++]) != saveDPVer)) {
554 throw new Exception ("invalid detect param format");
555 }
556
557 int len = objs.Length / 16;
558 DetectParams[] dps = new DetectParams[len];
559
560 for (int i = 0; i < len; i ++) {
561 DetectParams dp = new DetectParams ();
562
563 dp.Key = new UUID (ListStr (objs[j++]));
564 dp.OffsetPos = (LSL_Vector)objs[j++];
565 dp.LinkNum = ListInt (objs[j++]);
566 dp.Group = new UUID (ListStr (objs[j++]));
567 dp.Name = ListStr (objs[j++]);
568 dp.Owner = new UUID (ListStr (objs[j++]));
569 dp.Position = (LSL_Vector)objs[j++];
570 dp.Rotation = (LSL_Rotation)objs[j++];
571 dp.Type = ListInt (objs[j++]);
572 dp.Velocity = (LSL_Vector)objs[j++];
573
574 SurfaceTouchEventArgs stea = new SurfaceTouchEventArgs ();
575
576 stea.STCoord = LSLVec2OMVec ((LSL_Vector)objs[j++]);
577 stea.Normal = LSLVec2OMVec ((LSL_Vector)objs[j++]);
578 stea.Binormal = LSLVec2OMVec ((LSL_Vector)objs[j++]);
579 stea.Position = LSLVec2OMVec ((LSL_Vector)objs[j++]);
580 stea.UVCoord = LSLVec2OMVec ((LSL_Vector)objs[j++]);
581 stea.FaceIndex = ListInt (objs[j++]);
582
583 dp.SurfaceTouchArgs = stea;
584
585 dps[i] = dp;
586 }
587
588 return dps;
589 }
590
591 /**
592 * @brief The script is executing a 'state <newState>;' command.
593 * Tell outer layers to cancel any event triggers, like llListen(),
594 * then tell outer layers which events the new state has handlers for.
595 * We also clear the event queue as per http://wiki.secondlife.com/wiki/State
596 */
597 public override void StateChange()
598 {
599 /*
600 * Cancel any llListen()s etc.
601 * But llSetTimerEvent() should persist.
602 */
603 object[] timers = m_XMRLSLApi.acm.TimerPlugin.GetSerializationData(m_ItemID);
604 AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
605 m_XMRLSLApi.acm.TimerPlugin.CreateFromData(m_LocalID, m_ItemID, UUID.Zero, timers);
606
607 /*
608 * Tell whoever cares which event handlers the new state has.
609 */
610 m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(stateCode));
611
612 /*
613 * Clear out any old events from the queue.
614 */
615 lock (m_QueueLock) {
616 m_EventQueue.Clear();
617 for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
618 }
619 }
620
621 /**
622 * @brief Script is calling xmrStackLeft().
623 */
624 public override int xmrStackLeft ()
625 {
626 return microthread.StackLeft ();
627 }
628 }
629
630 /**
631 * @brief Thrown by things like llResetScript() to unconditionally
632 * unwind as script and reset it to the default state_entry
633 * handler. We don't want script-level try/catch to intercept
634 * these so scripts can't interfere with the behavior.
635 */
636 public class ScriptResetException : Exception, IXMRUncatchable { }
637
638 /**
639 * @brief Thrown by things like llDie() to unconditionally unwind as
640 * script. We don't want script-level try/catch to intercept
641 * these so scripts can't interfere with the behavior.
642 */
643 public class ScriptDieException : Exception, IXMRUncatchable { }
644}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs
new file mode 100644
index 0000000..8950d63
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs
@@ -0,0 +1,436 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Threading;
30using System.Reflection;
31using System.Collections;
32using System.Collections.Generic;
33using System.Runtime.Remoting.Lifetime;
34using System.Security.Policy;
35using System.IO;
36using System.Xml;
37using System.Text;
38using OpenMetaverse;
39using OpenSim.Framework;
40using OpenSim.Region.ScriptEngine.Interfaces;
41using OpenSim.Region.ScriptEngine.Shared;
42using OpenSim.Region.ScriptEngine.Shared.Api;
43using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
44using OpenSim.Region.ScriptEngine.XMREngine;
45using OpenSim.Region.Framework.Scenes;
46using log4net;
47
48using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
49using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
50using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
51using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
52using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
53using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
54using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
55
56namespace OpenSim.Region.ScriptEngine.XMREngine
57{
58 public partial class XMRInstance
59 {
60 /********************************************************************************\
61 * The only method of interest to outside this module is GetExecutionState() *
62 * which captures the current state of the script into an XML document. *
63 * *
64 * The rest of this module contains support routines for GetExecutionState(). *
65 \********************************************************************************/
66
67 /**
68 * @brief Create an XML element that gives the current state of the script.
69 * <ScriptState Engine="XMREngine" SourceHash=m_ObjCode.sourceHash Asset=m_Item.AssetID>
70 * <Snapshot>globalsandstackdump</Snapshot>
71 * <Running>m_Running</Running>
72 * <DetectArray ...
73 * <EventQueue ...
74 * <Permissions ...
75 * <Plugins />
76 * </ScriptState>
77 * Updates the .state file while we're at it.
78 */
79 public XmlElement GetExecutionState(XmlDocument doc)
80 {
81 /*
82 * When we're detaching an attachment, we need to wait here.
83 */
84
85 // Change this to a 5 second timeout. If things do mess up,
86 // we don't want to be stuck forever.
87 //
88 m_DetachReady.WaitOne (5000, false);
89
90 XmlElement scriptStateN = doc.CreateElement("", "ScriptState", "");
91 scriptStateN.SetAttribute("Engine", m_Engine.ScriptEngineName);
92 scriptStateN.SetAttribute("Asset", m_Item.AssetID.ToString());
93 scriptStateN.SetAttribute ("SourceHash", m_ObjCode.sourceHash);
94
95 /*
96 * Make sure we aren't executing part of the script so it stays
97 * stable. Setting suspendOnCheckRun tells CheckRun() to suspend
98 * and return out so RunOne() will release the lock asap.
99 */
100 suspendOnCheckRunHold = true;
101 lock (m_RunLock)
102 {
103 m_RunOnePhase = "GetExecutionState enter";
104 CheckRunLockInvariants(true);
105
106 /*
107 * Get copy of script globals and stack in relocateable form.
108 */
109 MemoryStream snapshotStream = new MemoryStream();
110 MigrateOutEventHandler(snapshotStream);
111 Byte[] snapshotBytes = snapshotStream.ToArray();
112 snapshotStream.Close();
113 string snapshotString = Convert.ToBase64String(snapshotBytes);
114 XmlElement snapshotN = doc.CreateElement("", "Snapshot", "");
115 snapshotN.AppendChild(doc.CreateTextNode(snapshotString));
116 scriptStateN.AppendChild(snapshotN);
117 m_RunOnePhase = "GetExecutionState B"; CheckRunLockInvariants(true);
118
119 /*
120 * "Running" says whether or not we are accepting new events.
121 */
122 XmlElement runningN = doc.CreateElement("", "Running", "");
123 runningN.AppendChild(doc.CreateTextNode(m_Running.ToString()));
124 scriptStateN.AppendChild(runningN);
125 m_RunOnePhase = "GetExecutionState C"; CheckRunLockInvariants(true);
126
127 /*
128 * "DoGblInit" says whether or not default:state_entry() will init global vars.
129 */
130 XmlElement doGblInitN = doc.CreateElement("", "DoGblInit", "");
131 doGblInitN.AppendChild(doc.CreateTextNode(doGblInit.ToString()));
132 scriptStateN.AppendChild(doGblInitN);
133 m_RunOnePhase = "GetExecutionState D"; CheckRunLockInvariants(true);
134
135 /*
136 * More misc data.
137 */
138 XmlNode permissionsN = doc.CreateElement("", "Permissions", "");
139 scriptStateN.AppendChild(permissionsN);
140
141 XmlAttribute granterA = doc.CreateAttribute("", "granter", "");
142 granterA.Value = m_Item.PermsGranter.ToString();
143 permissionsN.Attributes.Append(granterA);
144
145 XmlAttribute maskA = doc.CreateAttribute("", "mask", "");
146 maskA.Value = m_Item.PermsMask.ToString();
147 permissionsN.Attributes.Append(maskA);
148 m_RunOnePhase = "GetExecutionState E"; CheckRunLockInvariants(true);
149
150 /*
151 * "DetectParams" are returned by llDetected...() script functions
152 * for the currently active event, if any.
153 */
154 if (m_DetectParams != null)
155 {
156 XmlElement detParArrayN = doc.CreateElement("", "DetectArray", "");
157 AppendXMLDetectArray(doc, detParArrayN, m_DetectParams);
158 scriptStateN.AppendChild(detParArrayN);
159 }
160 m_RunOnePhase = "GetExecutionState F"; CheckRunLockInvariants(true);
161
162 /*
163 * Save any events we have in the queue.
164 * <EventQueue>
165 * <Event Name="...">
166 * <param>...</param> ...
167 * <DetectParams>...</DetectParams> ...
168 * </Event>
169 * ...
170 * </EventQueue>
171 */
172 XmlElement queuedEventsN = doc.CreateElement("", "EventQueue", "");
173 lock (m_QueueLock)
174 {
175 foreach (EventParams evt in m_EventQueue)
176 {
177 XmlElement singleEventN = doc.CreateElement("", "Event", "");
178 singleEventN.SetAttribute("Name", evt.EventName);
179 AppendXMLObjectArray(doc, singleEventN, evt.Params, "param");
180 AppendXMLDetectArray(doc, singleEventN, evt.DetectParams);
181 queuedEventsN.AppendChild(singleEventN);
182 }
183 }
184 scriptStateN.AppendChild(queuedEventsN);
185 m_RunOnePhase = "GetExecutionState G"; CheckRunLockInvariants(true);
186
187 /*
188 * "Plugins" indicate enabled timers and listens, etc.
189 */
190 Object[] pluginData =
191 AsyncCommandManager.GetSerializationData(m_Engine,
192 m_ItemID);
193
194 XmlNode plugins = doc.CreateElement("", "Plugins", "");
195 AppendXMLObjectArray(doc, plugins, pluginData, "plugin");
196 scriptStateN.AppendChild(plugins);
197 m_RunOnePhase = "GetExecutionState H"; CheckRunLockInvariants(true);
198
199 /*
200 * Let script run again.
201 */
202 suspendOnCheckRunHold = false;
203
204 m_RunOnePhase = "GetExecutionState leave";
205 CheckRunLockInvariants(true);
206 }
207
208 /*
209 * scriptStateN represents the contents of the .state file so
210 * write the .state file while we are here.
211 */
212 FileStream fs = File.Create(m_StateFileName);
213 StreamWriter sw = new StreamWriter(fs);
214 sw.Write(scriptStateN.OuterXml);
215 sw.Close();
216 fs.Close();
217
218 return scriptStateN;
219 }
220
221 /**
222 * @brief Write script state to output stream.
223 * The script microthread is at same state on return,
224 * ie, either inactive or suspended inside CheckRun().
225 *
226 * Input:
227 * stream = stream to write event handler state information to
228 */
229 private void MigrateOutEventHandler (Stream stream)
230 {
231 moehexcep = null;
232
233 // do all the work in the MigrateOutEventHandlerThread() method below
234 moehstream = stream;
235
236 XMRScriptThread cst = XMRScriptThread.CurrentScriptThread ();
237 if (cst != null) {
238
239 // we might be getting called inside some LSL Api function
240 // so we are already in script thread and thus must do
241 // migration directly
242 MigrateOutEventHandlerThread ();
243 } else {
244
245 // some other thread, do migration via a script thread
246 lock (XMRScriptThread.m_WakeUpLock) {
247 m_Engine.m_ThunkQueue.Enqueue (this.MigrateOutEventHandlerThread);
248 }
249 XMRScriptThread.WakeUpOne ();
250
251 // wait for it to complete
252 lock (moehdone) {
253 while (moehstream != null) {
254 Monitor.Wait (moehdone);
255 }
256 }
257 }
258
259 // maybe it threw up
260 if (moehexcep != null) throw moehexcep;
261 }
262 private Exception moehexcep;
263 private object moehdone = new object ();
264 private Stream moehstream;
265 private void MigrateOutEventHandlerThread ()
266 {
267 Exception except;
268
269 try {
270
271 /*
272 * Resume the microthread and it will throw a StackCaptureException()
273 * with the stack frames saved to this.stackFrames.
274 * Then write the saved stack frames to the output stream.
275 *
276 * There is a stack only if the event code is not None.
277 */
278 if (this.eventCode != ScriptEventCode.None) {
279
280 // tell microthread to continue
281 // it should see captureStackFrames and throw StackCaptureException()
282 // ...generating XMRStackFrames as it unwinds
283 this.captureStackFrames = true;
284 except = this.microthread.ResumeEx ();
285 this.captureStackFrames = false;
286 if (except == null) {
287 throw new Exception ("stack save did not complete");
288 }
289 if (!(except is StackCaptureException)) {
290 throw except;
291 }
292 }
293
294 /*
295 * Write script state out, frames and all, to the stream.
296 * Does not change script state.
297 */
298 moehstream.WriteByte (migrationVersion);
299 moehstream.WriteByte ((byte)16);
300 this.MigrateOut (new BinaryWriter (moehstream));
301
302 /*
303 * Now restore script stack.
304 * Microthread will suspend inside CheckRun() when restore is complete.
305 */
306 if (this.eventCode != ScriptEventCode.None) {
307 this.stackFramesRestored = false;
308 except = this.microthread.StartEx ();
309 if (except != null) {
310 throw except;
311 }
312 if (!this.stackFramesRestored) {
313 throw new Exception ("restore after save did not complete");
314 }
315 }
316 } catch (Exception e) {
317 moehexcep = e;
318 } finally {
319
320 // make sure CheckRunLockInvariants() won't puque
321 if (this.microthread.Active () == 0) {
322 this.eventCode = ScriptEventCode.None;
323 }
324
325 // wake the MigrateOutEventHandler() method above
326 lock (moehdone) {
327 moehstream = null;
328 Monitor.Pulse (moehdone);
329 }
330 }
331 }
332
333 /**
334 * @brief Convert an DetectParams[] to corresponding XML.
335 * DetectParams[] holds the values retrievable by llDetected...() for
336 * a given event.
337 */
338 private static void AppendXMLDetectArray(XmlDocument doc, XmlElement parent, DetectParams[] detect)
339 {
340 foreach (DetectParams d in detect)
341 {
342 XmlElement detectParamsN = GetXMLDetect(doc, d);
343 parent.AppendChild(detectParamsN);
344 }
345 }
346
347 private static XmlElement GetXMLDetect(XmlDocument doc, DetectParams d)
348 {
349 XmlElement detectParamsN = doc.CreateElement("", "DetectParams", "");
350
351 XmlAttribute d_key = doc.CreateAttribute("", "key", "");
352 d_key.Value = d.Key.ToString();
353 detectParamsN.Attributes.Append(d_key);
354
355 XmlAttribute pos = doc.CreateAttribute("", "pos", "");
356 pos.Value = d.OffsetPos.ToString();
357 detectParamsN.Attributes.Append(pos);
358
359 XmlAttribute d_linkNum = doc.CreateAttribute("", "linkNum", "");
360 d_linkNum.Value = d.LinkNum.ToString();
361 detectParamsN.Attributes.Append(d_linkNum);
362
363 XmlAttribute d_group = doc.CreateAttribute("", "group", "");
364 d_group.Value = d.Group.ToString();
365 detectParamsN.Attributes.Append(d_group);
366
367 XmlAttribute d_name = doc.CreateAttribute("", "name", "");
368 d_name.Value = d.Name.ToString();
369 detectParamsN.Attributes.Append(d_name);
370
371 XmlAttribute d_owner = doc.CreateAttribute("", "owner", "");
372 d_owner.Value = d.Owner.ToString();
373 detectParamsN.Attributes.Append(d_owner);
374
375 XmlAttribute d_position = doc.CreateAttribute("", "position", "");
376 d_position.Value = d.Position.ToString();
377 detectParamsN.Attributes.Append(d_position);
378
379 XmlAttribute d_rotation = doc.CreateAttribute("", "rotation", "");
380 d_rotation.Value = d.Rotation.ToString();
381 detectParamsN.Attributes.Append(d_rotation);
382
383 XmlAttribute d_type = doc.CreateAttribute("", "type", "");
384 d_type.Value = d.Type.ToString();
385 detectParamsN.Attributes.Append(d_type);
386
387 XmlAttribute d_velocity = doc.CreateAttribute("", "velocity", "");
388 d_velocity.Value = d.Velocity.ToString();
389 detectParamsN.Attributes.Append(d_velocity);
390
391 return detectParamsN;
392 }
393
394 /**
395 * @brief Append elements of an array of objects to an XML parent.
396 * @param doc = document the parent is part of
397 * @param parent = parent to append the items to
398 * @param array = array of objects
399 * @param tag = <tag ..>...</tag> for each element
400 */
401 private static void AppendXMLObjectArray(XmlDocument doc, XmlNode parent, object[] array, string tag)
402 {
403 foreach (object o in array)
404 {
405 XmlElement element = GetXMLObject(doc, o, tag);
406 parent.AppendChild(element);
407 }
408 }
409
410 /**
411 * @brief Get and XML representation of an object.
412 * @param doc = document the tag will be put in
413 * @param o = object to be represented
414 * @param tag = <tag ...>...</tag>
415 */
416 private static XmlElement GetXMLObject(XmlDocument doc, object o, string tag)
417 {
418 XmlAttribute typ = doc.CreateAttribute("", "type", "");
419 XmlElement n = doc.CreateElement("", tag, "");
420
421 if (o is LSL_List)
422 {
423 typ.Value = "list";
424 n.Attributes.Append(typ);
425 AppendXMLObjectArray(doc, n, ((LSL_List)o).Data, "item");
426 }
427 else
428 {
429 typ.Value = o.GetType().ToString();
430 n.Attributes.Append(typ);
431 n.AppendChild(doc.CreateTextNode(o.ToString()));
432 }
433 return n;
434 }
435 }
436}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs
new file mode 100644
index 0000000..d9c578a
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs
@@ -0,0 +1,878 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Threading;
30using System.Reflection;
31using System.Collections;
32using System.Collections.Generic;
33using System.Runtime.Remoting.Lifetime;
34using System.Security.Policy;
35using System.IO;
36using System.Xml;
37using System.Text;
38using OpenMetaverse;
39using OpenSim.Framework;
40using OpenSim.Region.ScriptEngine.Interfaces;
41using OpenSim.Region.ScriptEngine.Shared;
42using OpenSim.Region.ScriptEngine.Shared.Api;
43using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
44using OpenSim.Region.ScriptEngine.XMREngine;
45using OpenSim.Region.Framework.Scenes;
46using log4net;
47
48using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
49using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
50using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
51using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
52using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
53using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
54using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
55
56namespace OpenSim.Region.ScriptEngine.XMREngine
57{
58 public partial class XMRInstance
59 {
60 /****************************************************************************\
61 * The only method of interest to outside this module is the Initializer. *
62 * *
63 * The rest of this module contains support routines for the Initializer. *
64 \****************************************************************************/
65
66 /**
67 * @brief Initializer, loads script in memory and all ready for running.
68 * @param engine = XMREngine instance this is part of
69 * @param scriptBasePath = directory name where files are
70 * @param stackSize = number of bytes to allocate for stacks
71 * @param errors = return compiler errors in this array
72 * @param forceRecomp = force recompile
73 * Throws exception if any error, so it was successful if it returns.
74 */
75 public void Initialize(XMREngine engine, string scriptBasePath,
76 int stackSize, int heapSize, ArrayList errors)
77 {
78 if (stackSize < 16384) stackSize = 16384;
79 if (heapSize < 16384) heapSize = 16384;
80
81 /*
82 * Save all call parameters in instance vars for easy access.
83 */
84 m_Engine = engine;
85 m_ScriptBasePath = scriptBasePath;
86 m_StackSize = stackSize;
87 m_HeapSize = heapSize;
88 m_CompilerErrors = errors;
89 m_StateFileName = GetStateFileName(scriptBasePath, m_ItemID);
90
91 /*
92 * Not in any XMRInstQueue.
93 */
94 m_NextInst = this;
95 m_PrevInst = this;
96
97 /*
98 * Set up list of API calls it has available.
99 * This also gets the API modules ready to accept setup data, such as
100 * active listeners being restored.
101 */
102 IScriptApi scriptApi;
103 ApiManager am = new ApiManager();
104 foreach (string api in am.GetApis())
105 {
106 /*
107 * Instantiate the API for this script instance.
108 */
109 if (api != "LSL") {
110 scriptApi = am.CreateApi(api);
111 } else {
112 scriptApi = m_XMRLSLApi = new XMRLSL_Api();
113 }
114
115 /*
116 * Connect it up to the instance.
117 */
118 InitScriptApi (engine, api, scriptApi);
119 }
120
121 m_XMRLSLApi.InitXMRLSLApi(this);
122
123 /*
124 * Get object loaded, compiling script and reading .state file as
125 * necessary to restore the state.
126 */
127 suspendOnCheckRunHold = true;
128 InstantiateScript();
129 m_SourceCode = null;
130 if (m_ObjCode == null) throw new ArgumentNullException ("m_ObjCode");
131 if (m_ObjCode.scriptEventHandlerTable == null) {
132 throw new ArgumentNullException ("m_ObjCode.scriptEventHandlerTable");
133 }
134
135 suspendOnCheckRunHold = false;
136 suspendOnCheckRunTemp = false;
137
138 /*
139 * Declare which events the script's current state can handle.
140 */
141 int eventMask = GetStateEventFlags(stateCode);
142 m_Part.SetScriptEvents(m_ItemID, eventMask);
143 }
144
145 private void InitScriptApi (XMREngine engine, string api, IScriptApi scriptApi)
146 {
147 /*
148 * Set up m_ApiManager_<APINAME> = instance pointer.
149 */
150 engine.m_XMRInstanceApiCtxFieldInfos[api].SetValue (this, scriptApi);
151
152 /*
153 * Initialize the API instance.
154 */
155 scriptApi.Initialize(m_Engine, m_Part, m_Item);
156 this.InitApi (api, scriptApi);
157 }
158
159
160 // Get script object code loaded in memory and all ready to run,
161 // ready to resume it from where the .state file says it was last
162 private void InstantiateScript()
163 {
164 bool compiledIt = false;
165 ScriptObjCode objCode;
166
167 /*
168 * If source code string is empty, use the asset ID as the object file name.
169 * Allow lines of // comments at the beginning (for such as engine selection).
170 */
171 int i, j, len;
172 if (m_SourceCode == null) m_SourceCode = String.Empty;
173 for (len = m_SourceCode.Length; len > 0; -- len) {
174 if (m_SourceCode[len-1] > ' ') break;
175 }
176 for (i = 0; i < len; i ++) {
177 char c = m_SourceCode[i];
178 if (c <= ' ') continue;
179 if (c != '/') break;
180 if ((i + 1 >= len) || (m_SourceCode[i+1] != '/')) break;
181 i = m_SourceCode.IndexOf ('\n', i);
182 if (i < 0) i = len - 1;
183 }
184 if ((i >= len) || !m_Engine.m_UseSourceHashCode) {
185
186 /*
187 * Source consists of nothing but // comments and whitespace,
188 * or we are being forced to use the asset-id as the key, to
189 * open an already existing object code file.
190 */
191 m_ScriptObjCodeKey = m_Item.AssetID.ToString ();
192 if (i >= len) m_SourceCode = "";
193 } else {
194
195 /*
196 * Make up dictionary key for the object code.
197 * Use the same object code for identical source code
198 * regardless of asset ID, so we don't care if they
199 * copy scripts or not.
200 */
201 byte[] scbytes = System.Text.Encoding.UTF8.GetBytes (m_SourceCode);
202 StringBuilder sb = new StringBuilder ((256 + 5) / 6);
203 ByteArrayToSixbitStr (sb, System.Security.Cryptography.SHA256.Create ().ComputeHash (scbytes));
204 m_ScriptObjCodeKey = sb.ToString ();
205
206 /*
207 * But source code can be just a sixbit string itself
208 * that identifies an already existing object code file.
209 */
210 if (len - i == m_ScriptObjCodeKey.Length) {
211 for (j = len; -- j >= i;) {
212 if (sixbit.IndexOf (m_SourceCode[j]) < 0) break;
213 }
214 if (j < i) {
215 m_ScriptObjCodeKey = m_SourceCode.Substring (i, len - i);
216 m_SourceCode = "";
217 }
218 }
219 }
220
221 /*
222 * There may already be an ScriptObjCode struct in memory that
223 * we can use. If not, try to compile it.
224 */
225 lock (m_CompileLock) {
226 if (!m_CompiledScriptObjCode.TryGetValue (m_ScriptObjCodeKey, out objCode) || m_ForceRecomp) {
227 objCode = TryToCompile ();
228 compiledIt = true;
229 }
230
231 /*
232 * Loaded successfully, increment reference count.
233 *
234 * If we just compiled it though, reset count to 0 first as
235 * this is the one-and-only existance of this objCode struct,
236 * and we want any old ones for this source code to be garbage
237 * collected.
238 */
239 if (compiledIt) {
240 m_CompiledScriptObjCode[m_ScriptObjCodeKey] = objCode;
241 objCode.refCount = 0;
242 }
243 objCode.refCount ++;
244
245 /*
246 * Now set up to decrement ref count on dispose.
247 */
248 m_ObjCode = objCode;
249 }
250
251 try {
252
253 /*
254 * Fill in script instance from object code
255 * Script instance is put in a "never-ever-has-run-before" state.
256 */
257 LoadObjCode();
258
259 /*
260 * Fill in script intial state
261 * - either as loaded from a .state file
262 * - or initial default state_entry() event
263 */
264 LoadInitialState();
265 } catch {
266
267 /*
268 * If any error loading, decrement object code reference count.
269 */
270 DecObjCodeRefCount ();
271 throw;
272 }
273 }
274
275 private const string sixbit = "0123456789_abcdefghijklmnopqrstuvwxyz@ABCDEFGHIJKLMNOPQRSTUVWXYZ";
276 private static void ByteArrayToSixbitStr (StringBuilder sb, byte[] bytes)
277 {
278 int bit = 0;
279 int val = 0;
280 foreach (byte b in bytes) {
281 val |= (int)((uint)b << bit);
282 bit += 8;
283 while (bit >= 6) {
284 sb.Append (sixbit[val&63]);
285 val >>= 6;
286 bit -= 6;
287 }
288 }
289 if (bit > 0) {
290 sb.Append (sixbit[val&63]);
291 }
292 }
293
294 // Try to create object code from source code
295 // If error, just throw exception
296 private ScriptObjCode TryToCompile ()
297 {
298 m_CompilerErrors.Clear();
299
300 /*
301 * If object file exists, create ScriptObjCode directly from that.
302 * Otherwise, compile the source to create object file then create
303 * ScriptObjCode from that.
304 */
305 string assetID = m_Item.AssetID.ToString();
306 m_CameFrom = "asset://" + assetID;
307 ScriptObjCode objCode = Compile ();
308 if (m_CompilerErrors.Count != 0)
309 {
310 throw new Exception ("compilation errors");
311 }
312 if (objCode == null)
313 {
314 throw new Exception ("compilation failed");
315 }
316
317 return objCode;
318 }
319
320 /*
321 * Retrieve source from asset server.
322 */
323 private string FetchSource (string cameFrom)
324 {
325 m_log.Debug ("[XMREngine]: fetching source " + cameFrom);
326 if (!cameFrom.StartsWith ("asset://")) {
327 throw new Exception ("unable to retrieve source from " + cameFrom);
328 }
329 string assetID = cameFrom.Substring (8);
330 AssetBase asset = m_Engine.World.AssetService.Get(assetID);
331 if (asset == null) {
332 throw new Exception ("source not found " + cameFrom);
333 }
334 string source = Encoding.UTF8.GetString (asset.Data);
335 if (EmptySource (source)) {
336 throw new Exception ("fetched source empty " + cameFrom);
337 }
338 return source;
339 }
340
341 /*
342 * Fill in script object initial contents.
343 * Set the initial state to "default".
344 */
345 private void LoadObjCode ()
346 {
347 /*
348 * Script must leave this much stack remaining on calls to CheckRun().
349 */
350 this.stackLimit = m_StackSize / 2;
351
352 /*
353 * This is how many total heap bytes script is allowed to use.
354 */
355 this.heapLimit = m_HeapSize;
356
357 /*
358 * Allocate global variable arrays.
359 */
360 this.glblVars.AllocVarArrays (m_ObjCode.glblSizes);
361
362 /*
363 * Script can handle these event codes.
364 */
365 m_HaveEventHandlers = new bool[m_ObjCode.scriptEventHandlerTable.GetLength(1)];
366 for (int i = m_ObjCode.scriptEventHandlerTable.GetLength(0); -- i >= 0;) {
367 for (int j = m_ObjCode.scriptEventHandlerTable.GetLength(1); -- j >= 0;) {
368 if (m_ObjCode.scriptEventHandlerTable[i,j] != null) {
369 m_HaveEventHandlers[j] = true;
370 }
371 }
372 }
373
374 /*
375 * Set up microthread object which actually calls the script event handler functions.
376 */
377 this.microthread = (IScriptUThread)m_Engine.uThreadCtor.Invoke (new object[] { this });
378 }
379
380 // LoadInitialState()
381 // if no state XML file exists for the asset,
382 // post initial default state events
383 // else
384 // try to restore from .state file
385 // If any error, throw exception
386 //
387 private void LoadInitialState()
388 {
389 /*
390 * If no .state file exists, start from default state
391 * Otherwise, read initial state from the .state file
392 */
393 if (!File.Exists(m_StateFileName)) {
394 m_Running = true; // event processing is enabled
395 eventCode = ScriptEventCode.None; // not processing any event
396
397 // default state_entry() must initialize global variables
398 doGblInit = true;
399 stateCode = 0;
400
401 PostEvent(new EventParams("state_entry",
402 zeroObjectArray,
403 zeroDetectParams));
404 } else {
405 FileStream fs = File.Open(m_StateFileName,
406 FileMode.Open,
407 FileAccess.Read);
408 StreamReader ss = new StreamReader(fs);
409 string xml = ss.ReadToEnd();
410 ss.Close();
411 fs.Close();
412
413 XmlDocument doc = new XmlDocument();
414 doc.LoadXml(xml);
415 LoadScriptState(doc);
416 }
417
418 /*
419 * Post event(s) saying what caused the script to start.
420 */
421 if (m_PostOnRez) {
422 PostEvent(new EventParams("on_rez",
423 new Object[] { m_StartParam },
424 zeroDetectParams));
425 }
426
427 switch (m_StateSource) {
428 case StateSource.AttachedRez: {
429// PostEvent(new EventParams("attach",
430// new object[] { m_Part.ParentGroup.AttachedAvatar.ToString() },
431// zeroDetectParams));
432 break;
433 }
434
435 case StateSource.PrimCrossing: {
436 PostEvent(new EventParams("changed",
437 sbcCR,
438 zeroDetectParams));
439 break;
440 }
441
442 case StateSource.Teleporting: {
443 PostEvent(new EventParams("changed",
444 sbcCR,
445 zeroDetectParams));
446 PostEvent(new EventParams("changed",
447 sbcCT,
448 zeroDetectParams));
449 break;
450 }
451
452 case StateSource.RegionStart: {
453 PostEvent(new EventParams("changed",
454 sbcCRS,
455 zeroDetectParams));
456 break;
457 }
458 }
459 }
460
461 private static Object[] sbcCRS = new Object[] { ScriptBaseClass.CHANGED_REGION_START };
462 private static Object[] sbcCR = new Object[] { ScriptBaseClass.CHANGED_REGION };
463 private static Object[] sbcCT = new Object[] { ScriptBaseClass.CHANGED_TELEPORT };
464
465 /**
466 * @brief Save compilation error messages for later retrieval
467 * via GetScriptErrors().
468 */
469 private void ErrorHandler(Token token, string message)
470 {
471 if (token != null) {
472 string srcloc = token.SrcLoc;
473 if (srcloc.StartsWith (m_CameFrom)) {
474 srcloc = srcloc.Substring (m_CameFrom.Length);
475 }
476 m_CompilerErrors.Add(srcloc + " Error: " + message);
477 } else if (message != null) {
478 m_CompilerErrors.Add("(0,0) Error: " + message);
479 } else {
480 m_CompilerErrors.Add("(0,0) Error compiling, see exception in log");
481 }
482 }
483
484 /**
485 * @brief Load script state from the given XML doc into the script memory
486 * <ScriptState Engine="XMREngine" Asset=...>
487 * <Running>...</Running>
488 * <DoGblInit>...</DoGblInit>
489 * <Permissions granted=... mask=... />
490 * RestoreDetectParams()
491 * <Plugins>
492 * ExtractXMLObjectArray("plugin")
493 * </Plugins>
494 * <Snapshot>
495 * MigrateInEventHandler()
496 * </Snapshot>
497 * </ScriptState>
498 */
499 private void LoadScriptState(XmlDocument doc)
500 {
501 DetectParams[] detParams;
502 LinkedList<EventParams> eventQueue;
503
504 // Everything we know is enclosed in <ScriptState>...</ScriptState>
505 XmlElement scriptStateN = (XmlElement)doc.SelectSingleNode("ScriptState");
506 if (scriptStateN == null) {
507 throw new Exception("no <ScriptState> tag");
508 }
509 string sen = scriptStateN.GetAttribute("Engine");
510 if ((sen == null) || (sen != m_Engine.ScriptEngineName)) {
511 throw new Exception("<ScriptState> missing Engine=\"XMREngine\" attribute");
512 }
513
514 // AssetID is unique for the script source text so make sure the
515 // state file was written for that source file
516 string assetID = scriptStateN.GetAttribute("Asset");
517 if (assetID != m_Item.AssetID.ToString())
518 {
519 throw new Exception("<ScriptState> assetID mismatch");
520 }
521
522 // Also match the sourceHash in case script was
523 // loaded via 'xmroption fetchsource' and has changed
524 string sourceHash = scriptStateN.GetAttribute ("SourceHash");
525 if ((sourceHash == null) || (sourceHash != m_ObjCode.sourceHash)) {
526 throw new Exception ("<ScriptState> SourceHash mismatch");
527 }
528
529 // Get various attributes
530 XmlElement runningN = (XmlElement)scriptStateN.SelectSingleNode("Running");
531 m_Running = bool.Parse(runningN.InnerText);
532
533 XmlElement doGblInitN = (XmlElement)scriptStateN.SelectSingleNode("DoGblInit");
534 doGblInit = bool.Parse(doGblInitN.InnerText);
535
536 XmlElement permissionsN = (XmlElement)scriptStateN.SelectSingleNode("Permissions");
537 m_Item.PermsGranter = new UUID(permissionsN.GetAttribute("granter"));
538 m_Item.PermsMask = Convert.ToInt32(permissionsN.GetAttribute("mask"));
539 m_Part.Inventory.UpdateInventoryItem(m_Item, false, false);
540
541 // get values used by stuff like llDetectedGrab, etc.
542 detParams = RestoreDetectParams(scriptStateN.SelectSingleNode("DetectArray"));
543
544 // Restore queued events
545 eventQueue = RestoreEventQueue(scriptStateN.SelectSingleNode("EventQueue"));
546
547 // Restore timers and listeners
548 XmlElement pluginN = (XmlElement)scriptStateN.SelectSingleNode("Plugins");
549 Object[] pluginData = ExtractXMLObjectArray(pluginN, "plugin");
550
551 // Script's global variables and stack contents
552 XmlElement snapshotN =
553 (XmlElement)scriptStateN.SelectSingleNode("Snapshot");
554
555 Byte[] data = Convert.FromBase64String(snapshotN.InnerText);
556 MemoryStream ms = new MemoryStream();
557 ms.Write(data, 0, data.Length);
558 ms.Seek(0, SeekOrigin.Begin);
559 MigrateInEventHandler(ms);
560 ms.Close();
561
562 // Restore event queues, preserving any events that queued
563 // whilst we were restoring the state
564 lock (m_QueueLock) {
565 m_DetectParams = detParams;
566 foreach (EventParams evt in m_EventQueue) {
567 eventQueue.AddLast (evt);
568 }
569 m_EventQueue = eventQueue;
570 for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
571 foreach (EventParams evt in m_EventQueue)
572 {
573 ScriptEventCode eventCode = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
574 evt.EventName);
575 m_EventCounts[(int)eventCode] ++;
576 }
577 }
578
579 // Requeue timer and listeners (possibly queuing new events)
580 AsyncCommandManager.CreateFromData(m_Engine,
581 m_LocalID, m_ItemID, m_Part.UUID,
582 pluginData);
583 }
584
585 /**
586 * @brief Read llDetectedGrab, etc, values from XML
587 * <EventQueue>
588 * <DetectParams>...</DetectParams>
589 * .
590 * .
591 * .
592 * </EventQueue>
593 */
594 private LinkedList<EventParams> RestoreEventQueue(XmlNode eventsN)
595 {
596 LinkedList<EventParams> eventQueue = new LinkedList<EventParams>();
597 if (eventsN != null) {
598 XmlNodeList eventL = eventsN.SelectNodes("Event");
599 foreach (XmlNode evnt in eventL)
600 {
601 string name = ((XmlElement)evnt).GetAttribute("Name");
602 object[] parms = ExtractXMLObjectArray(evnt, "param");
603 DetectParams[] detects = RestoreDetectParams(evnt);
604
605 if (parms == null) parms = zeroObjectArray;
606 if (detects == null) detects = zeroDetectParams;
607
608 EventParams evt = new EventParams(name, parms, detects);
609 eventQueue.AddLast(evt);
610 }
611 }
612 return eventQueue;
613 }
614
615 /**
616 * @brief Read llDetectedGrab, etc, values from XML
617 * <DetectArray>
618 * <DetectParams>...</DetectParams>
619 * .
620 * .
621 * .
622 * </DetectArray>
623 */
624 private DetectParams[] RestoreDetectParams(XmlNode detectedN)
625 {
626 if (detectedN == null) return null;
627
628 List<DetectParams> detected = new List<DetectParams>();
629 XmlNodeList detectL = detectedN.SelectNodes("DetectParams");
630
631 DetectParams detprm = new DetectParams();
632 foreach (XmlNode detxml in detectL) {
633 try {
634 detprm.Group = new UUID(detxml.Attributes.GetNamedItem("group").Value);
635 detprm.Key = new UUID(detxml.Attributes.GetNamedItem("key").Value);
636 detprm.Owner = new UUID(detxml.Attributes.GetNamedItem("owner").Value);
637
638 detprm.LinkNum = Int32.Parse(detxml.Attributes.GetNamedItem("linkNum").Value);
639 detprm.Type = Int32.Parse(detxml.Attributes.GetNamedItem("type").Value);
640
641 detprm.Name = detxml.Attributes.GetNamedItem("name").Value;
642
643 detprm.OffsetPos = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("pos").Value);
644 detprm.Position = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("position").Value);
645 detprm.Velocity = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("velocity").Value);
646
647 detprm.Rotation = new LSL_Types.Quaternion(detxml.Attributes.GetNamedItem("rotation").Value);
648
649 detected.Add(detprm);
650 detprm = new DetectParams();
651 } catch (Exception e) {
652 m_log.Warn("[XMREngine]: RestoreDetectParams bad XML: " + detxml.ToString());
653 m_log.Warn("[XMREngine]: ... " + e.ToString());
654 }
655 }
656
657 return detected.ToArray();
658 }
659
660 /**
661 * @brief Extract elements of an array of objects from an XML parent.
662 * Each element is of form <tag ...>...</tag>
663 * @param parent = XML parent to extract them from
664 * @param tag = what the value's tag is
665 * @returns object array of the values
666 */
667 private static object[] ExtractXMLObjectArray(XmlNode parent, string tag)
668 {
669 List<Object> olist = new List<Object>();
670
671 XmlNodeList itemL = parent.SelectNodes(tag);
672 foreach (XmlNode item in itemL)
673 {
674 olist.Add(ExtractXMLObjectValue(item));
675 }
676
677 return olist.ToArray();
678 }
679
680 private static object ExtractXMLObjectValue(XmlNode item)
681 {
682 string itemType = item.Attributes.GetNamedItem("type").Value;
683
684 if (itemType == "list")
685 {
686 return new LSL_List(ExtractXMLObjectArray(item, "item"));
687 }
688
689 if (itemType == "OpenMetaverse.UUID")
690 {
691 UUID val = new UUID();
692 UUID.TryParse(item.InnerText, out val);
693 return val;
694 }
695
696 Type itemT = Type.GetType(itemType);
697 if (itemT == null)
698 {
699 Object[] args = new Object[] { item.InnerText };
700
701 string assembly = itemType + ", OpenSim.Region.ScriptEngine.Shared";
702 itemT = Type.GetType(assembly);
703 if (itemT == null)
704 {
705 return null;
706 }
707 return Activator.CreateInstance(itemT, args);
708 }
709
710 return Convert.ChangeType(item.InnerText, itemT);
711 }
712
713 /*
714 * Migrate an event handler in from a stream.
715 *
716 * Input:
717 * stream = as generated by MigrateOutEventHandler()
718 */
719 private void MigrateInEventHandler (Stream stream)
720 {
721 miehexcep = null;
722
723 // do all the work in the MigrateInEventHandlerThread() method below
724 miehstream = stream;
725
726 XMRScriptThread cst = XMRScriptThread.CurrentScriptThread ();
727 if (cst != null) {
728
729 // in case we are getting called inside some LSL Api function
730 MigrateInEventHandlerThread ();
731 } else {
732
733 // some other thread, do migration via a script thread
734 lock (XMRScriptThread.m_WakeUpLock) {
735 m_Engine.m_ThunkQueue.Enqueue (this.MigrateInEventHandlerThread);
736 }
737 XMRScriptThread.WakeUpOne ();
738
739 // wait for it to complete
740 lock (miehdone) {
741 while (miehstream != null) {
742 Monitor.Wait (miehdone);
743 }
744 }
745 }
746
747 // maybe it threw up
748 if (miehexcep != null) throw miehexcep;
749 }
750 private Exception miehexcep;
751 private object miehdone = new object ();
752 private Stream miehstream;
753 private void MigrateInEventHandlerThread ()
754 {
755 try {
756 int mv = miehstream.ReadByte ();
757 if (mv != migrationVersion) {
758 throw new Exception ("incoming migration version " + mv + " but accept only " + migrationVersion);
759 }
760 miehstream.ReadByte (); // ignored
761
762 /*
763 * Restore script variables and stack and other state from stream.
764 * And it also marks us busy (by setting this.eventCode) so we can't be
765 * started again and this event lost.
766 */
767 BinaryReader br = new BinaryReader (miehstream);
768 this.MigrateIn (br);
769
770 /*
771 * If eventCode is None, it means the script was idle when migrated.
772 */
773 if (this.eventCode != ScriptEventCode.None) {
774
775 /*
776 * So microthread.Start() calls XMRScriptUThread.Main() which calls the
777 * event handler function. The event handler function sees the stack
778 * frames in this.stackFrames and restores its args and locals, then calls
779 * whatever it was calling when the snapshot was taken. That function also
780 * sees this.stackFrames and restores its args and locals, and so on...
781 * Eventually it gets to the point of calling CheckRun() which sees we are
782 * doing a restore and it suspends, returning here with the microthread
783 * stack all restored. It shouldn't ever throw an exception.
784 */
785 this.stackFramesRestored = false;
786 Exception te = microthread.StartEx ();
787 if (te != null) throw te;
788 if (!this.stackFramesRestored) throw new Exception ("migrate in did not complete");
789 }
790 } catch (Exception e) {
791 miehexcep = e;
792 } finally {
793
794 /*
795 * Wake the MigrateInEventHandler() method above.
796 */
797 lock (miehdone) {
798 miehstream = null;
799 Monitor.Pulse (miehdone);
800 }
801 }
802 }
803
804 /**
805 * See if permitted by configuration file.
806 * See OSSL_Api.CheckThreatLevelTest().
807 */
808 public string CheckFetchbinaryAllowed ()
809 {
810 string ownerPerm = m_Engine.Config.GetString ("Allow_fetchbinary", "");
811 UUID ownerID = m_Item.OwnerID;
812 string[] ids = ownerPerm.Split (new char[] { ',' });
813 foreach (string id in ids) {
814 string curuc = id.Trim ().ToUpperInvariant ();
815
816 switch (curuc) {
817 case "ESTATE_MANAGER": {
818 if (m_Engine.m_Scene.RegionInfo.EstateSettings.IsEstateManagerOrOwner (ownerID) &&
819 (m_Engine.m_Scene.RegionInfo.EstateSettings.EstateOwner != ownerID)) {
820 return null;
821 }
822 break;
823 }
824
825 case "ESTATE_OWNER": {
826 if (m_Engine.m_Scene.RegionInfo.EstateSettings.EstateOwner == ownerID) {
827 return null;
828 }
829 break;
830 }
831
832 case "PARCEL_GROUP_MEMBER": {
833 ILandObject land = m_Engine.m_Scene.LandChannel.GetLandObject (m_Part.AbsolutePosition);
834 if (land.LandData.GroupID == m_Item.GroupID && land.LandData.GroupID != UUID.Zero) {
835 return null;
836 }
837 break;
838 }
839
840 case "PARCEL_OWNER": {
841 ILandObject land = m_Engine.m_Scene.LandChannel.GetLandObject (m_Part.AbsolutePosition);
842 if (land.LandData.OwnerID == ownerID) {
843 return null;
844 }
845 break;
846 }
847
848 case "TRUE": {
849 return null;
850 }
851
852 default: {
853 UUID uuid;
854 if (UUID.TryParse (curuc, out uuid)) {
855 if (uuid == ownerID) return null;
856 }
857 break;
858 }
859 }
860 }
861
862 string creatorPerm = m_Engine.Config.GetString ("Creators_fetchbinary", "");
863 UUID creatorID = m_Item.CreatorID;
864 ids = creatorPerm.Split (new char[] { ',' });
865 foreach (string id in ids) {
866 string current = id.Trim ();
867 UUID uuid;
868 if (UUID.TryParse (current, out uuid)) {
869 if (uuid != UUID.Zero) {
870 if (creatorID == uuid) return null;
871 }
872 }
873 }
874
875 return "fetchbinary not enabled for owner " + ownerID + " creator " + creatorID;
876 }
877 }
878}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstMain.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstMain.cs
new file mode 100644
index 0000000..436434a
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstMain.cs
@@ -0,0 +1,230 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Threading;
30using System.Reflection;
31using System.Collections;
32using System.Collections.Generic;
33using System.Runtime.Remoting.Lifetime;
34using System.Security.Policy;
35using System.IO;
36using System.Xml;
37using System.Text;
38using OpenMetaverse;
39using OpenSim.Framework;
40using OpenSim.Region.ScriptEngine.Interfaces;
41using OpenSim.Region.ScriptEngine.Shared;
42using OpenSim.Region.ScriptEngine.Shared.Api;
43using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
44using OpenSim.Region.ScriptEngine.XMREngine;
45using OpenSim.Region.Framework.Scenes;
46using log4net;
47
48using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
49using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
50using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
51using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
52using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
53using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
54using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
55
56// This class exists in the main app domain
57//
58namespace OpenSim.Region.ScriptEngine.XMREngine
59{
60 /**
61 * @brief Which queue it is in as far as running is concerned,
62 * ie, m_StartQueue, m_YieldQueue, m_SleepQueue, etc.
63 * Allowed transitions:
64 * Starts in CONSTRUCT when constructed
65 * CONSTRUCT->ONSTARTQ : only by thread that constructed and compiled it
66 * IDLE->ONSTARTQ,RESETTING : by any thread but must have m_QueueLock when transitioning
67 * ONSTARTQ->RUNNING,RESETTING : only by thread that removed it from m_StartQueue
68 * ONYIELDQ->RUNNING,RESETTING : only by thread that removed it from m_YieldQueue
69 * ONSLEEPQ->REMDFROMSLPQ : by any thread but must have m_SleepQueue when transitioning
70 * REMDFROMSLPQ->ONYIELDQ,RESETTING : only by thread that removed it from m_SleepQueue
71 * RUNNING->whatever1 : only by thread that transitioned it to RUNNING
72 * whatever1 = IDLE,ONSLEEPQ,ONYIELDQ,ONSTARTQ,SUSPENDED,FINISHED
73 * FINSHED->whatever2 : only by thread that transitioned it to FINISHED
74 * whatever2 = IDLE,ONSTARTQ,DISPOSED
75 * SUSPENDED->ONSTARTQ : by any thread (NOT YET IMPLEMENTED, should be under some kind of lock?)
76 * RESETTING->ONSTARTQ : only by the thread that transitioned it to RESETTING
77 */
78 public enum XMRInstState {
79 CONSTRUCT, // it is being constructed
80 IDLE, // nothing happening (finished last event and m_EventQueue is empty)
81 ONSTARTQ, // inserted on m_Engine.m_StartQueue
82 RUNNING, // currently being executed by RunOne()
83 ONSLEEPQ, // inserted on m_Engine.m_SleepQueue
84 REMDFROMSLPQ, // removed from m_SleepQueue but not yet on m_YieldQueue
85 ONYIELDQ, // inserted on m_Engine.m_YieldQueue
86 FINISHED, // just finished handling an event
87 SUSPENDED, // m_SuspendCount > 0
88 RESETTING, // being reset via external call
89 DISPOSED // has been disposed
90 }
91
92 public partial class XMRInstance : XMRInstAbstract, IDisposable
93 {
94 /******************************************************************\
95 * This module contains the instance variables for XMRInstance. *
96 \******************************************************************/
97
98 public const int MAXEVENTQUEUE = 64;
99
100 public static readonly DetectParams[] zeroDetectParams = new DetectParams[0];
101 public static readonly object[] zeroObjectArray = new object[0];
102
103 public static readonly ILog m_log =
104 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
105
106 // For a given m_Item.AssetID, do we have the compiled object code and where
107 // is it?
108 public static object m_CompileLock = new object();
109 private static Dictionary<string,ScriptObjCode> m_CompiledScriptObjCode = new Dictionary<string,ScriptObjCode>();
110
111 public XMRInstance m_NextInst; // used by XMRInstQueue
112 public XMRInstance m_PrevInst;
113 public XMRInstState m_IState;
114
115 public bool m_ForceRecomp = false;
116 public SceneObjectPart m_Part = null;
117 public uint m_LocalID = 0;
118 public TaskInventoryItem m_Item = null;
119 public UUID m_ItemID;
120 public UUID m_PartUUID;
121 private string m_CameFrom;
122 private string m_ScriptObjCodeKey;
123
124 private XMREngine m_Engine = null;
125 private string m_ScriptBasePath;
126 private string m_StateFileName;
127 public string m_SourceCode;
128 public bool m_PostOnRez;
129 private DetectParams[] m_DetectParams = null;
130 public int m_StartParam = 0;
131 public StateSource m_StateSource;
132 public string m_DescName;
133 private bool[] m_HaveEventHandlers;
134 public int m_StackSize;
135 public int m_HeapSize;
136 private ArrayList m_CompilerErrors;
137 private DateTime m_LastRanAt = DateTime.MinValue;
138 private string m_RunOnePhase = "hasn't run";
139 private string m_CheckRunPhase = "hasn't checked";
140 public int m_InstEHEvent = 0; // number of events dequeued (StartEventHandler called)
141 public int m_InstEHSlice = 0; // number of times handler timesliced (ResumeEx called)
142 public double m_CPUTime = 0; // accumulated CPU time (milliseconds)
143
144 // If code needs to have both m_QueueLock and m_RunLock,
145 // be sure to lock m_RunLock first then m_QueueLock, as
146 // that is the order used in RunOne().
147 // These locks are currently separated to allow the script
148 // to call API routines that queue events back to the script.
149 // If we just had one lock, then the queuing would deadlock.
150
151 // guards m_DetachQuantum, m_EventQueue, m_EventCounts, m_Running, m_Suspended
152 public Object m_QueueLock = new Object();
153
154 // true iff allowed to accept new events
155 public bool m_Running = true;
156
157 // queue of events that haven't been acted upon yet
158 public LinkedList<EventParams> m_EventQueue = new LinkedList<EventParams> ();
159
160 // number of events of each code currently in m_EventQueue.
161 private int[] m_EventCounts = new int[(int)ScriptEventCode.Size];
162
163 // locked whilst running on the microthread stack (or about to run on it or just ran on it)
164 private Object m_RunLock = new Object();
165
166 // script won't step while > 0. bus-atomic updates only.
167 private int m_SuspendCount = 0;
168
169 // don't run any of script until this time
170 // or until one of these events are queued
171 public DateTime m_SleepUntil = DateTime.MinValue;
172 public int m_SleepEventMask1 = 0;
173 public int m_SleepEventMask2 = 0;
174
175 private XMRLSL_Api m_XMRLSLApi;
176
177 /*
178 * We will use this microthread to run the scripts event handlers.
179 */
180 private IScriptUThread microthread;
181
182 /*
183 * Set to perform migration.
184 */
185 public bool stackFramesRestored; // set true by CheckRun() when stack has been
186 // restored and is about to suspend the microthread
187 public bool captureStackFrames; // set true to tell CheckRun() to throw a
188 // StackCaptureException() causing it to capture a
189 // snapshot of the script's stack
190
191 /*
192 * Makes sure migration data version is same on both ends.
193 */
194 public static byte migrationVersion = 10;
195
196 // Incremented each time script gets reset.
197 public int m_ResetCount = 0;
198
199 // Scripts start suspended now. This means that event queues will
200 // accept events, but will not actually run them until the core
201 // tells it it's OK. This is needed to prevent loss of link messages
202 // in complex objects, where no event can be allowed to run until
203 // all possible link message receivers' queues are established.
204 // Guarded by m_QueueLock.
205 public bool m_Suspended = true;
206
207 // We really don't want to save state for a script that hasn't had
208 // a chance to run, because it's state will be blank. That would
209 // cause attachment state loss.
210 public bool m_HasRun = false;
211
212 // When llDie is executed within the attach(NULL_KEY) event of
213 // a script being detached to inventory, the DeleteSceneObject call
214 // it causes will delete the script instances before their state can
215 // be saved. Therefore, the instance needs to know that it's being
216 // detached to inventory, rather than to ground.
217 // Also, the attach(NULL_KEY) event needs to run with priority, and
218 // it also needs to have a limited quantum.
219 // If this is nonzero, we're detaching to inventory.
220 // Guarded by m_QueueLock.
221 private int m_DetachQuantum = 0;
222
223 // Finally, we need to wait until the quantum is done, or the script
224 // suspends itself. This should be efficient, so we use an event
225 // for it instead of spinning busy.
226 // It's born ready, but will be reset when the detach is posted.
227 // It will then be set again on suspend/completion
228 private ManualResetEvent m_DetachReady = new ManualResetEvent(true);
229 }
230}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstMisc.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstMisc.cs
new file mode 100644
index 0000000..6ff486a
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstMisc.cs
@@ -0,0 +1,384 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Threading;
30using System.Reflection;
31using System.Collections;
32using System.Collections.Generic;
33using System.Reflection.Emit;
34using System.Runtime.Remoting.Lifetime;
35using System.Security.Policy;
36using System.IO;
37using System.Xml;
38using System.Text;
39using OpenMetaverse;
40using OpenSim.Framework;
41using OpenSim.Region.ScriptEngine.Interfaces;
42using OpenSim.Region.ScriptEngine.Shared;
43using OpenSim.Region.ScriptEngine.Shared.Api;
44using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
45using OpenSim.Region.ScriptEngine.XMREngine;
46using OpenSim.Region.Framework.Scenes;
47using log4net;
48
49using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
50using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
51using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
52using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
53using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
54using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
55using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
56
57// This class exists in the main app domain
58//
59namespace OpenSim.Region.ScriptEngine.XMREngine
60{
61 public partial class XMRInstance
62 {
63
64 // In case Dispose() doesn't get called, we want to be sure to clean
65 // up. This makes sure we decrement m_CompiledScriptRefCount.
66 ~XMRInstance()
67 {
68 Dispose();
69 }
70
71 /**
72 * @brief Clean up stuff.
73 * We specifically leave m_DescName intact for 'xmr ls' command.
74 */
75 public void Dispose()
76 {
77 /*
78 * Tell script stop executing next time it calls CheckRun().
79 */
80 suspendOnCheckRunHold = true;
81
82 /*
83 * Wait for it to stop executing and prevent it from starting again
84 * as it can't run without a microthread.
85 */
86 lock (m_RunLock)
87 {
88 if (microthread != null)
89 {
90 m_RunOnePhase = "disposing";
91 CheckRunLockInvariants(true);
92 microthread.Dispose ();
93 microthread = null;
94 }
95 }
96
97 /*
98 * Don't send us any more events.
99 */
100 if (m_Part != null)
101 {
102 xmrTrapRegionCrossing (0);
103 m_Part.RemoveScriptEvents(m_ItemID);
104 AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
105 m_Part = null;
106 }
107
108 /*
109 * Let script methods get garbage collected if no one else is using
110 * them.
111 */
112 DecObjCodeRefCount ();
113 }
114
115 private void DecObjCodeRefCount ()
116 {
117 if (m_ObjCode != null) {
118 lock (m_CompileLock) {
119 ScriptObjCode objCode;
120
121 if (m_CompiledScriptObjCode.TryGetValue (m_ScriptObjCodeKey, out objCode) &&
122 (objCode == m_ObjCode) &&
123 (-- objCode.refCount == 0)) {
124 m_CompiledScriptObjCode.Remove (m_ScriptObjCodeKey);
125 }
126 }
127 m_ObjCode = null;
128 }
129 }
130
131 public void Verbose (string format, params object[] args)
132 {
133 if (m_Engine.m_Verbose) m_log.DebugFormat (format, args);
134 }
135
136 // Called by 'xmr top' console command
137 // to dump this script's state to console
138 // Sacha
139 public void RunTestTop()
140 {
141 if (m_InstEHSlice > 0){
142 Console.WriteLine(m_DescName);
143 Console.WriteLine(" m_LocalID = " + m_LocalID);
144 Console.WriteLine(" m_ItemID = " + m_ItemID);
145 Console.WriteLine(" m_Item.AssetID = " + m_Item.AssetID);
146 Console.WriteLine(" m_StartParam = " + m_StartParam);
147 Console.WriteLine(" m_PostOnRez = " + m_PostOnRez);
148 Console.WriteLine(" m_StateSource = " + m_StateSource);
149 Console.WriteLine(" m_SuspendCount = " + m_SuspendCount);
150 Console.WriteLine(" m_SleepUntil = " + m_SleepUntil);
151 Console.WriteLine(" m_IState = " + m_IState.ToString());
152 Console.WriteLine(" m_StateCode = " + GetStateName(stateCode));
153 Console.WriteLine(" eventCode = " + eventCode.ToString());
154 Console.WriteLine(" m_LastRanAt = " + m_LastRanAt.ToString());
155 Console.WriteLine(" heapUsed/Limit = " + xmrHeapUsed () + "/" + heapLimit);
156 Console.WriteLine(" m_InstEHEvent = " + m_InstEHEvent.ToString());
157 Console.WriteLine(" m_InstEHSlice = " + m_InstEHSlice.ToString());
158 }
159 }
160
161 // Called by 'xmr ls' console command
162 // to dump this script's state to console
163 public string RunTestLs(bool flagFull)
164 {
165 if (flagFull) {
166 StringBuilder sb = new StringBuilder();
167 sb.AppendLine(m_DescName);
168 sb.AppendLine(" m_LocalID = " + m_LocalID);
169 sb.AppendLine(" m_ItemID = " + m_ItemID + " (.state file)");
170 sb.AppendLine(" m_Item.AssetID = " + m_Item.AssetID);
171 sb.AppendLine(" m_Part.WorldPosition = " + m_Part.GetWorldPosition ());
172 sb.AppendLine(" m_ScriptObjCodeKey = " + m_ScriptObjCodeKey + " (source text)");
173 sb.AppendLine(" m_StartParam = " + m_StartParam);
174 sb.AppendLine(" m_PostOnRez = " + m_PostOnRez);
175 sb.AppendLine(" m_StateSource = " + m_StateSource);
176 sb.AppendLine(" m_SuspendCount = " + m_SuspendCount);
177 sb.AppendLine(" m_SleepUntil = " + m_SleepUntil);
178 sb.AppendLine(" m_SleepEvMask1 = 0x" + m_SleepEventMask1.ToString("X"));
179 sb.AppendLine(" m_SleepEvMask2 = 0x" + m_SleepEventMask2.ToString("X"));
180 sb.AppendLine(" m_IState = " + m_IState.ToString());
181 sb.AppendLine(" m_StateCode = " + GetStateName(stateCode));
182 sb.AppendLine(" eventCode = " + eventCode.ToString());
183 sb.AppendLine(" m_LastRanAt = " + m_LastRanAt.ToString());
184 sb.AppendLine(" m_RunOnePhase = " + m_RunOnePhase);
185 sb.AppendLine(" suspOnCkRunHold = " + suspendOnCheckRunHold);
186 sb.AppendLine(" suspOnCkRunTemp = " + suspendOnCheckRunTemp);
187 sb.AppendLine(" m_CheckRunPhase = " + m_CheckRunPhase);
188 sb.AppendLine(" heapUsed/Limit = " + xmrHeapUsed () + "/" + heapLimit);
189 sb.AppendLine(" m_InstEHEvent = " + m_InstEHEvent.ToString());
190 sb.AppendLine(" m_InstEHSlice = " + m_InstEHSlice.ToString());
191 sb.AppendLine(" m_CPUTime = " + m_CPUTime);
192 sb.AppendLine(" callMode = " + callMode);
193 sb.AppendLine(" captureStackFrames = " + captureStackFrames);
194 sb.AppendLine(" stackFramesRestored = " + stackFramesRestored);
195 lock (m_QueueLock)
196 {
197 sb.AppendLine(" m_Running = " + m_Running);
198 foreach (EventParams evt in m_EventQueue)
199 {
200 sb.AppendLine(" evt.EventName = " + evt.EventName);
201 }
202 }
203 return sb.ToString();
204 } else {
205 return String.Format("{0} {1} {2} {3} {4} {5}",
206 m_ItemID,
207 m_CPUTime.ToString("F3").PadLeft(9),
208 m_InstEHEvent.ToString().PadLeft(9),
209 m_IState.ToString().PadRight(10),
210 m_Part.GetWorldPosition().ToString().PadRight(32),
211 m_DescName);
212 }
213 }
214
215 /**
216 * @brief For a given stateCode, get a mask of the low 32 event codes
217 * that the state has handlers defined for.
218 */
219 public int GetStateEventFlags(int stateCode)
220 {
221 if ((stateCode < 0) ||
222 (stateCode >= m_ObjCode.scriptEventHandlerTable.GetLength(0)))
223 {
224 return 0;
225 }
226
227 int code = 0;
228 for (int i = 0 ; i < 32; i ++)
229 {
230 if (m_ObjCode.scriptEventHandlerTable[stateCode, i] != null)
231 {
232 code |= 1 << i;
233 }
234 }
235
236 return code;
237 }
238
239 /**
240 * @brief Get the .state file name.
241 */
242 public static string GetStateFileName (string scriptBasePath, UUID itemID)
243 {
244 return GetScriptFileName (scriptBasePath, itemID.ToString() + ".state");
245 }
246
247 public string GetScriptFileName (string filename)
248 {
249 return GetScriptFileName (m_ScriptBasePath, filename);
250 }
251
252 public static string GetScriptFileName (string scriptBasePath, string filename)
253 {
254 /*
255 * Get old path, ie, all files lumped in a single huge directory.
256 */
257 string oldPath = Path.Combine (scriptBasePath, filename);
258
259 /*
260 * Get new path, ie, files split up based on first 2 chars of name.
261 */
262 string subdir = filename.Substring (0, 2);
263 filename = filename.Substring (2);
264 scriptBasePath = Path.Combine (scriptBasePath, subdir);
265 Directory.CreateDirectory (scriptBasePath);
266 string newPath = Path.Combine (scriptBasePath, filename);
267
268 /*
269 * If file exists only in old location, move to new location.
270 * If file exists in both locations, delete old location.
271 */
272 if (File.Exists (oldPath)) {
273 if (File.Exists (newPath)) {
274 File.Delete (oldPath);
275 } else {
276 File.Move (oldPath, newPath);
277 }
278 }
279
280 /*
281 * Always return new location.
282 */
283 return newPath;
284 }
285
286 /**
287 * @brief Decode state code (int) to state name (string).
288 */
289 public string GetStateName (int stateCode)
290 {
291 try {
292 return m_ObjCode.stateNames[stateCode];
293 } catch {
294 return stateCode.ToString ();
295 }
296 }
297
298 /**
299 * @brief various gets & sets.
300 */
301 public int StartParam
302 {
303 get { return m_StartParam; }
304 set { m_StartParam = value; }
305 }
306
307 public SceneObjectPart SceneObject
308 {
309 get { return m_Part; }
310 }
311
312 public DetectParams[] DetectParams
313 {
314 get { return m_DetectParams; }
315 set { m_DetectParams = value; }
316 }
317
318 public UUID ItemID
319 {
320 get { return m_ItemID; }
321 }
322
323 public UUID AssetID
324 {
325 get { return m_Item.AssetID; }
326 }
327
328 public bool Running
329 {
330 get { return m_Running; }
331 set
332 {
333 lock (m_QueueLock)
334 {
335 m_Running = value;
336 if (!value)
337 {
338 EmptyEventQueues ();
339 }
340 }
341 }
342 }
343
344 /**
345 * @brief Empty out the event queues.
346 * Assumes caller has the m_QueueLock locked.
347 */
348 public void EmptyEventQueues ()
349 {
350 m_EventQueue.Clear();
351 for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
352 }
353
354 /**
355 * @brief Convert an LSL vector to an Openmetaverse vector.
356 */
357 public static OpenMetaverse.Vector3 LSLVec2OMVec (LSL_Vector lslVec)
358 {
359 return new OpenMetaverse.Vector3 ((float)lslVec.x, (float)lslVec.y, (float)lslVec.z);
360 }
361
362 /**
363 * @brief Extract an integer from an element of an LSL_List.
364 */
365 public static int ListInt (object element)
366 {
367 if (element is LSL_Integer) {
368 return (int)(LSL_Integer)element;
369 }
370 return (int)element;
371 }
372
373 /**
374 * @brief Extract a string from an element of an LSL_List.
375 */
376 public static string ListStr (object element)
377 {
378 if (element is LSL_String) {
379 return (string)(LSL_String)element;
380 }
381 return (string)element;
382 }
383 }
384}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstQueue.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstQueue.cs
new file mode 100644
index 0000000..41acac8
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstQueue.cs
@@ -0,0 +1,186 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29
30namespace OpenSim.Region.ScriptEngine.XMREngine
31{
32 /**
33 * @brief Implements a queue of XMRInstance's.
34 * Do our own queue to avoid shitty little mallocs.
35 *
36 * Note: looping inst.m_NextInst and m_PrevInst back to itself
37 * when inst is removed from a queue is purely for debug.
38 */
39 public class XMRInstQueue
40 {
41 private XMRInstance m_Head = null;
42 private XMRInstance m_Tail = null;
43
44 /**
45 * @brief Insert instance at head of queue (in front of all others)
46 * @param inst = instance to insert
47 */
48 public void InsertHead(XMRInstance inst)
49 {
50 if ((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) {
51 throw new Exception("already in list");
52 }
53 inst.m_PrevInst = null;
54 if ((inst.m_NextInst = m_Head) == null) {
55 m_Tail = inst;
56 } else {
57 m_Head.m_PrevInst = inst;
58 }
59 m_Head = inst;
60 }
61
62 /**
63 * @brief Insert instance at tail of queue (behind all others)
64 * @param inst = instance to insert
65 */
66 public void InsertTail(XMRInstance inst)
67 {
68 if ((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) {
69 throw new Exception("already in list");
70 }
71 inst.m_NextInst = null;
72 if ((inst.m_PrevInst = m_Tail) == null) {
73 m_Head = inst;
74 } else {
75 m_Tail.m_NextInst = inst;
76 }
77 m_Tail = inst;
78 }
79
80 /**
81 * @brief Insert instance before another element in queue
82 * @param inst = instance to insert
83 * @param after = element that is to come after one being inserted
84 */
85 public void InsertBefore(XMRInstance inst, XMRInstance after)
86 {
87 if ((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) {
88 throw new Exception("already in list");
89 }
90 if (after == null) {
91 InsertTail(inst);
92 } else {
93 inst.m_NextInst = after;
94 inst.m_PrevInst = after.m_PrevInst;
95 if (inst.m_PrevInst == null) {
96 m_Head = inst;
97 } else {
98 inst.m_PrevInst.m_NextInst = inst;
99 }
100 after.m_PrevInst = inst;
101 }
102 }
103
104 /**
105 * @brief Peek to see if anything in queue
106 * @returns first XMRInstance in queue but doesn't remove it
107 * null if queue is empty
108 */
109 public XMRInstance PeekHead()
110 {
111 return m_Head;
112 }
113
114 /**
115 * @brief Remove first element from queue, if any
116 * @returns null if queue is empty
117 * else returns first element in queue and removes it
118 */
119 public XMRInstance RemoveHead()
120 {
121 XMRInstance inst = m_Head;
122 if (inst != null) {
123 if ((m_Head = inst.m_NextInst) == null) {
124 m_Tail = null;
125 } else {
126 m_Head.m_PrevInst = null;
127 }
128 inst.m_NextInst = inst;
129 inst.m_PrevInst = inst;
130 }
131 return inst;
132 }
133
134 /**
135 * @brief Remove last element from queue, if any
136 * @returns null if queue is empty
137 * else returns last element in queue and removes it
138 */
139 public XMRInstance RemoveTail()
140 {
141 XMRInstance inst = m_Tail;
142 if (inst != null) {
143 if ((m_Tail = inst.m_PrevInst) == null) {
144 m_Head = null;
145 } else {
146 m_Tail.m_NextInst = null;
147 }
148 inst.m_NextInst = inst;
149 inst.m_PrevInst = inst;
150 }
151 return inst;
152 }
153
154 /**
155 * @brief Remove arbitrary element from queue, if any
156 * @param inst = element to remove (assumed to be in the queue)
157 * @returns with element removed
158 */
159 public void Remove(XMRInstance inst)
160 {
161 XMRInstance next = inst.m_NextInst;
162 XMRInstance prev = inst.m_PrevInst;
163 if ((prev == inst) || (next == inst)) {
164 throw new Exception("not in a list");
165 }
166 if (next == null) {
167 if (m_Tail != inst) {
168 throw new Exception("not in this list");
169 }
170 m_Tail = prev;
171 } else {
172 next.m_PrevInst = prev;
173 }
174 if (prev == null) {
175 if (m_Head != inst) {
176 throw new Exception("not in this list");
177 }
178 m_Head = next;
179 } else {
180 prev.m_NextInst = next;
181 }
182 inst.m_NextInst = inst;
183 inst.m_PrevInst = inst;
184 }
185 }
186}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs
new file mode 100644
index 0000000..61ae549
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs
@@ -0,0 +1,1051 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Threading;
30using System.Reflection;
31using System.Collections;
32using System.Collections.Generic;
33using System.Reflection.Emit;
34using System.Runtime.Remoting.Lifetime;
35using System.Security.Policy;
36using System.IO;
37using System.Xml;
38using System.Text;
39using OpenMetaverse;
40using OpenSim.Framework;
41using OpenSim.Region.Framework.Interfaces;
42using OpenSim.Region.ScriptEngine.Interfaces;
43using OpenSim.Region.ScriptEngine.Shared;
44using OpenSim.Region.ScriptEngine.Shared.Api;
45using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
46using OpenSim.Region.ScriptEngine.XMREngine;
47using OpenSim.Region.Framework.Scenes;
48using log4net;
49
50using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
51using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
52using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
53using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
54using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
55using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
56using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
57
58namespace OpenSim.Region.ScriptEngine.XMREngine
59{
60 public partial class XMRInstance
61 {
62 /************************************************************************************\
63 * This module contains these externally useful methods: *
64 * PostEvent() - queues an event to script and wakes script thread to process it *
65 * RunOne() - runs script for a time slice or until it volunteers to give up cpu *
66 * CallSEH() - runs in the microthread to call the event handler *
67 \************************************************************************************/
68
69 /**
70 * @brief This can be called in any thread (including the script thread itself)
71 * to queue event to script for processing.
72 */
73 public void PostEvent(EventParams evt)
74 {
75 ScriptEventCode evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
76 evt.EventName);
77
78 /*
79 * Put event on end of event queue.
80 */
81 bool startIt = false;
82 bool wakeIt = false;
83 lock (m_QueueLock)
84 {
85 bool construct = (m_IState == XMRInstState.CONSTRUCT);
86
87 /*
88 * Ignore event if we don't even have such an handler in any state.
89 * We can't be state-specific here because state might be different
90 * by the time this event is dequeued and delivered to the script.
91 */
92 if (!construct && // make sure m_HaveEventHandlers is filled in
93 ((uint)evc < (uint)m_HaveEventHandlers.Length) &&
94 !m_HaveEventHandlers[(int)evc]) { // don't bother if we don't have such a handler in any state
95 return;
96 }
97
98 /*
99 * Not running means we ignore any incoming events.
100 * But queue if still constructing because m_Running is not yet valid.
101 */
102 if (!m_Running && !construct) {
103 return;
104 }
105
106 /*
107 * Only so many of each event type allowed to queue.
108 */
109 if ((uint)evc < (uint)m_EventCounts.Length) {
110 int maxAllowed = MAXEVENTQUEUE;
111 if (evc == ScriptEventCode.timer) maxAllowed = 1;
112 if (m_EventCounts[(int)evc] >= maxAllowed)
113 {
114 return;
115 }
116 m_EventCounts[(int)evc] ++;
117 }
118
119 /*
120 * Put event on end of instance's event queue.
121 */
122 LinkedListNode<EventParams> lln = new LinkedListNode<EventParams>(evt);
123 switch (evc) {
124
125 /*
126 * These need to go first. The only time we manually
127 * queue them is for the default state_entry() and we
128 * need to make sure they go before any attach() events
129 * so the heapLimit value gets properly initialized.
130 */
131 case ScriptEventCode.state_entry: {
132 m_EventQueue.AddFirst(lln);
133 break;
134 }
135
136 /*
137 * The attach event sneaks to the front of the queue.
138 * This is needed for quantum limiting to work because
139 * we want the attach(NULL_KEY) event to come in front
140 * of all others so the m_DetachQuantum won't run out
141 * before attach(NULL_KEY) is executed.
142 */
143 case ScriptEventCode.attach: {
144 if (evt.Params[0].ToString() == UUID.Zero.ToString())
145 {
146 LinkedListNode<EventParams> lln2 = null;
147 for (lln2 = m_EventQueue.First; lln2 != null; lln2 = lln2.Next) {
148 EventParams evt2 = lln2.Value;
149 ScriptEventCode evc2 = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
150 evt2.EventName);
151 if ((evc2 != ScriptEventCode.state_entry) &&
152 (evc2 != ScriptEventCode.attach)) break;
153 }
154 if (lln2 == null) {
155 m_EventQueue.AddLast(lln);
156 } else {
157 m_EventQueue.AddBefore(lln2, lln);
158 }
159 /* If we're detaching, limit the qantum. This will also
160 * cause the script to self-suspend after running this
161 * event
162 */
163
164 m_DetachReady.Reset();
165 m_DetachQuantum = 100;
166 }
167 else
168 {
169 m_EventQueue.AddLast(lln);
170 }
171 break;
172 }
173
174 /*
175 * All others just go on end in the order queued.
176 */
177 default: {
178 m_EventQueue.AddLast(lln);
179 break;
180 }
181 }
182
183 /*
184 * If instance is idle (ie, not running or waiting to run),
185 * flag it to be on m_StartQueue as we are about to do so.
186 * Flag it now before unlocking so another thread won't try
187 * to do the same thing right now.
188 * Dont' flag it if it's still suspended!
189 */
190 if ((m_IState == XMRInstState.IDLE) && !m_Suspended) {
191 m_IState = XMRInstState.ONSTARTQ;
192 startIt = true;
193 }
194
195 /*
196 * If instance is sleeping (ie, possibly in xmrEventDequeue),
197 * wake it up if event is in the mask.
198 */
199 if ((m_SleepUntil > DateTime.UtcNow) && !m_Suspended) {
200 int evc1 = (int)evc;
201 int evc2 = evc1 - 32;
202 if ((((uint)evc1 < (uint)32) && (((m_SleepEventMask1 >> evc1) & 1) != 0)) ||
203 (((uint)evc2 < (uint)32) && (((m_SleepEventMask2 >> evc2) & 1) != 0))) {
204 wakeIt = true;
205 }
206 }
207 }
208
209 /*
210 * If transitioned from IDLE->ONSTARTQ, actually go insert it
211 * on m_StartQueue and give the RunScriptThread() a wake-up.
212 */
213 if (startIt) {
214 m_Engine.QueueToStart(this);
215 }
216
217 /*
218 * Likewise, if the event mask triggered a wake, wake it up.
219 */
220 if (wakeIt) {
221 m_SleepUntil = DateTime.MinValue;
222 m_Engine.WakeFromSleep(this);
223 }
224 }
225
226 /*
227 * This is called in the script thread to step script until it calls
228 * CheckRun(). It returns what the instance's next state should be,
229 * ONSLEEPQ, ONYIELDQ, SUSPENDED or FINISHED.
230 */
231 public XMRInstState RunOne()
232 {
233 DateTime now = DateTime.UtcNow;
234
235 /*
236 * If script has called llSleep(), don't do any more until time is
237 * up.
238 */
239 m_RunOnePhase = "check m_SleepUntil";
240 if (m_SleepUntil > now)
241 {
242 m_RunOnePhase = "return is sleeping";
243 return XMRInstState.ONSLEEPQ;
244 }
245
246 /*
247 * Also, someone may have called Suspend().
248 */
249 m_RunOnePhase = "check m_SuspendCount";
250 if (m_SuspendCount > 0) {
251 m_RunOnePhase = "return is suspended";
252 return XMRInstState.SUSPENDED;
253 }
254
255 /*
256 * Make sure we aren't being migrated in or out and prevent that
257 * whilst we are in here. If migration has it locked, don't call
258 * back right away, delay a bit so we don't get in infinite loop.
259 */
260 m_RunOnePhase = "lock m_RunLock";
261 if (!Monitor.TryEnter (m_RunLock)) {
262 m_SleepUntil = now.AddMilliseconds(3);
263 m_RunOnePhase = "return was locked";
264 return XMRInstState.ONSLEEPQ;
265 }
266 try
267 {
268 m_RunOnePhase = "check entry invariants";
269 CheckRunLockInvariants(true);
270 Exception e = null;
271
272 /*
273 * Maybe we have been disposed.
274 */
275 m_RunOnePhase = "check disposed";
276 if (microthread == null)
277 {
278 m_RunOnePhase = "return disposed";
279 return XMRInstState.DISPOSED;
280 }
281
282 /*
283 * Do some more of the last event if it didn't finish.
284 */
285 else if (this.eventCode != ScriptEventCode.None)
286 {
287 lock (m_QueueLock)
288 {
289 if (m_DetachQuantum > 0 && --m_DetachQuantum == 0)
290 {
291 m_Suspended = true;
292 m_DetachReady.Set();
293 m_RunOnePhase = "detach quantum went zero";
294 CheckRunLockInvariants(true);
295 return XMRInstState.FINISHED;
296 }
297 }
298
299 m_RunOnePhase = "resume old event handler";
300 m_LastRanAt = now;
301 m_InstEHSlice ++;
302 callMode = CallMode_NORMAL;
303 e = microthread.ResumeEx ();
304 }
305
306 /*
307 * Otherwise, maybe we can dequeue a new event and start
308 * processing it.
309 */
310 else
311 {
312 m_RunOnePhase = "lock event queue";
313 EventParams evt = null;
314 ScriptEventCode evc = ScriptEventCode.None;
315
316 lock (m_QueueLock)
317 {
318
319 /* We can't get here unless the script has been resumed
320 * after creation, then suspended again, and then had
321 * an event posted to it. We just pretend there is no
322 * event int he queue and let the normal mechanics
323 * carry out the suspension. A Resume will handle the
324 * restarting gracefully. This is taking the easy way
325 * out and may be improved in the future.
326 */
327
328 if (m_Suspended)
329 {
330 m_RunOnePhase = "m_Suspended is set";
331 CheckRunLockInvariants(true);
332 return XMRInstState.FINISHED;
333 }
334
335 m_RunOnePhase = "dequeue event";
336 if (m_EventQueue.First != null)
337 {
338 evt = m_EventQueue.First.Value;
339 if (m_DetachQuantum > 0)
340 {
341 evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
342 evt.EventName);
343 if (evc != ScriptEventCode.attach)
344 {
345 /*
346 * This is the case where the attach event
347 * has completed and another event is queued
348 * Stop it from running and suspend
349 */
350 m_Suspended = true;
351 m_DetachReady.Set();
352 m_DetachQuantum = 0;
353 m_RunOnePhase = "nothing to do #3";
354 CheckRunLockInvariants(true);
355 return XMRInstState.FINISHED;
356 }
357 }
358 m_EventQueue.RemoveFirst();
359 evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
360 evt.EventName);
361 if ((int)evc >= 0) m_EventCounts[(int)evc] --;
362 }
363
364 /*
365 * If there is no event to dequeue, don't run this script
366 * until another event gets queued.
367 */
368 if (evt == null)
369 {
370 if (m_DetachQuantum > 0)
371 {
372 /*
373 * This will happen if the attach event has run
374 * and exited with time slice left.
375 */
376 m_Suspended = true;
377 m_DetachReady.Set();
378 m_DetachQuantum = 0;
379 }
380 m_RunOnePhase = "nothing to do #4";
381 CheckRunLockInvariants(true);
382 return XMRInstState.FINISHED;
383 }
384 }
385
386 /*
387 * Dequeued an event, so start it going until it either
388 * finishes or it calls CheckRun().
389 */
390 m_RunOnePhase = "start event handler";
391 m_DetectParams = evt.DetectParams;
392 m_LastRanAt = now;
393 m_InstEHEvent ++;
394 e = StartEventHandler (evc, evt.Params);
395 }
396 m_RunOnePhase = "done running";
397 m_CPUTime += DateTime.UtcNow.Subtract(now).TotalMilliseconds;
398
399 /*
400 * Maybe it puqued.
401 */
402 if (e != null)
403 {
404 m_RunOnePhase = "handling exception " + e.Message;
405 HandleScriptException(e);
406 m_RunOnePhase = "return had exception " + e.Message;
407 CheckRunLockInvariants(true);
408 return XMRInstState.FINISHED;
409 }
410
411 /*
412 * If event handler completed, get rid of detect params.
413 */
414 if (this.eventCode == ScriptEventCode.None)
415 {
416 m_DetectParams = null;
417 }
418 }
419 finally
420 {
421 m_RunOnePhase += "; checking exit invariants and unlocking";
422 CheckRunLockInvariants(false);
423 Monitor.Exit(m_RunLock);
424 }
425
426 /*
427 * Cycle script through the yield queue and call it back asap.
428 */
429 m_RunOnePhase = "last return";
430 return XMRInstState.ONYIELDQ;
431 }
432
433 /**
434 * @brief Immediately after taking m_RunLock or just before releasing it, check invariants.
435 */
436 private ScriptEventCode lastEventCode = ScriptEventCode.None;
437 private int lastActive = 0;
438 private string lastRunPhase = "";
439
440 public void CheckRunLockInvariants(bool throwIt)
441 {
442 /*
443 * If not executing any event handler, active should be 0 indicating the microthread stack is not in use.
444 * If executing an event handler, active should be -1 indicating stack is in use but suspended.
445 */
446 IScriptUThread uth = microthread;
447 if (uth != null) {
448 int active = uth.Active ();
449 ScriptEventCode ec = this.eventCode;
450 if (((ec == ScriptEventCode.None) && (active != 0)) ||
451 ((ec != ScriptEventCode.None) && (active >= 0))) {
452 Console.WriteLine("CheckRunLockInvariants: script=" + m_DescName);
453 Console.WriteLine("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString());
454 Console.WriteLine("CheckRunLockInvariants: m_RunOnePhase=" + m_RunOnePhase);
455 Console.WriteLine("CheckRunLockInvariants: lastec=" + lastEventCode + ", lastAct=" + lastActive + ", lastPhase=" + lastRunPhase);
456 if (throwIt) {
457 throw new Exception("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString());
458 }
459 }
460 lastEventCode = ec;
461 lastActive = active;
462 lastRunPhase = m_RunOnePhase;
463 }
464 }
465
466 /*
467 * Start event handler.
468 *
469 * Input:
470 * eventCode = code of event to be processed
471 * ehArgs = arguments for the event handler
472 *
473 * Caution:
474 * It is up to the caller to make sure ehArgs[] is correct for
475 * the particular event handler being called. The first thing
476 * a script event handler method does is to unmarshall the args
477 * from ehArgs[] and will throw an array bounds or cast exception
478 * if it can't.
479 */
480 private Exception StartEventHandler (ScriptEventCode eventCode, object[] ehArgs)
481 {
482 /*
483 * We use this.eventCode == ScriptEventCode.None to indicate we are idle.
484 * So trying to execute ScriptEventCode.None might make a mess.
485 */
486 if (eventCode == ScriptEventCode.None) {
487 return new Exception ("Can't process ScriptEventCode.None");
488 }
489
490 /*
491 * Silly to even try if there is no handler defined for this event.
492 */
493 if (((int)eventCode >= 0) && (m_ObjCode.scriptEventHandlerTable[this.stateCode,(int)eventCode] == null)) {
494 return null;
495 }
496
497 /*
498 * The microthread shouldn't be processing any event code.
499 * These are assert checks so we throw them directly as exceptions.
500 */
501 if (this.eventCode != ScriptEventCode.None) {
502 throw new Exception ("still processing event " + this.eventCode.ToString ());
503 }
504 int active = microthread.Active ();
505 if (active != 0) {
506 throw new Exception ("microthread is active " + active.ToString ());
507 }
508
509 /*
510 * Save eventCode so we know what event handler to run in the microthread.
511 * And it also marks us busy so we can't be started again and this event lost.
512 */
513 this.eventCode = eventCode;
514 this.ehArgs = ehArgs;
515
516 /*
517 * This calls ScriptUThread.Main() directly, and returns when Main() [indirectly]
518 * calls Suspend() or when Main() returns, whichever occurs first.
519 * Setting stackFrames = null means run the event handler from the beginning
520 * without doing any stack frame restores first.
521 */
522 this.stackFrames = null;
523 Exception e;
524 e = microthread.StartEx ();
525 return e;
526 }
527
528
529 /**
530 * @brief There was an exception whilst starting/running a script event handler.
531 * Maybe we handle it directly or just print an error message.
532 */
533 private void HandleScriptException(Exception e)
534 {
535 /*
536 * The script threw some kind of exception that was not caught at
537 * script level, so the script is no longer running an event handler.
538 */
539 eventCode = ScriptEventCode.None;
540
541 if (e is ScriptDeleteException)
542 {
543 /*
544 * Script did something like llRemoveInventory(llGetScriptName());
545 * ... to delete itself from the object.
546 */
547 m_SleepUntil = DateTime.MaxValue;
548 Verbose ("[XMREngine]: script self-delete {0}", m_ItemID);
549 m_Part.Inventory.RemoveInventoryItem(m_ItemID);
550 }
551 else if (e is ScriptDieException)
552 {
553 /*
554 * Script did an llDie()
555 */
556 m_RunOnePhase = "dying...";
557 m_SleepUntil = DateTime.MaxValue;
558 m_Engine.World.DeleteSceneObject(m_Part.ParentGroup, false);
559 }
560 else if (e is ScriptResetException)
561 {
562 /*
563 * Script did an llResetScript().
564 */
565 m_RunOnePhase = "resetting...";
566 ResetLocked("HandleScriptResetException");
567 }
568 else
569 {
570 /*
571 * Some general script error.
572 */
573 SendErrorMessage(e);
574 }
575 return;
576 }
577
578 /**
579 * @brief There was an exception running script event handler.
580 * Display error message and disable script (in a way
581 * that the script can be reset to be restarted).
582 */
583 private void SendErrorMessage(Exception e)
584 {
585 StringBuilder msg = new StringBuilder();
586
587 msg.Append ("[XMREngine]: Exception while running ");
588 msg.Append (m_ItemID);
589 msg.Append ('\n');
590
591 /*
592 * Add exception message.
593 */
594 string des = e.Message;
595 des = (des == null) ? "" : (": " + des);
596 msg.Append (e.GetType ().Name + des + "\n");
597
598 /*
599 * Tell script owner what to do.
600 */
601 msg.Append ("Prim: <");
602 msg.Append (m_Part.Name);
603 msg.Append (">, Script: <");
604 msg.Append (m_Item.Name);
605 msg.Append (">, Location: ");
606 msg.Append (m_Engine.World.RegionInfo.RegionName);
607 msg.Append (" <");
608 Vector3 pos = m_Part.AbsolutePosition;
609 msg.Append ((int) Math.Floor (pos.X));
610 msg.Append (',');
611 msg.Append ((int) Math.Floor (pos.Y));
612 msg.Append (',');
613 msg.Append ((int) Math.Floor (pos.Z));
614 msg.Append (">\nScript must be Reset to re-enable.\n");
615
616 /*
617 * Display full exception message in log.
618 */
619 m_log.Info (msg.ToString() + XMRExceptionStackString (e), e);
620
621 /*
622 * Give script owner the stack dump.
623 */
624 msg.Append (XMRExceptionStackString (e));
625
626 /*
627 * Send error message to owner.
628 * Suppress internal code stack trace lines.
629 */
630 string msgst = msg.ToString();
631 if (!msgst.EndsWith ("\n")) msgst += '\n';
632 int j = 0;
633 StringBuilder imstr = new StringBuilder ();
634 for (int i = 0; (i = msgst.IndexOf ('\n', i)) >= 0; j = ++ i) {
635 string line = msgst.Substring (j, i - j);
636 if (line.StartsWith ("at ")) {
637 if (line.StartsWith ("at (wrapper")) continue; // at (wrapper ...
638 int k = line.LastIndexOf (".cs:"); // ... .cs:linenumber
639 if (Int32.TryParse (line.Substring (k + 4), out k)) continue;
640 }
641 this.llOwnerSay (line);
642 imstr.Append (line);
643 imstr.Append ('\n');
644 }
645
646 /*
647 * Send as instant message in case user not online.
648 * Code modelled from llInstantMessage().
649 */
650 IMessageTransferModule transferModule = m_Engine.World.RequestModuleInterface<IMessageTransferModule>();
651 if (transferModule != null) {
652 UUID friendTransactionID = UUID.Random();
653 GridInstantMessage gim = new GridInstantMessage();
654 gim.fromAgentID = new Guid (m_Part.UUID.ToString());
655 gim.toAgentID = new Guid (m_Part.OwnerID.ToString ());
656 gim.imSessionID = new Guid(friendTransactionID.ToString());
657 gim.timestamp = (uint)Util.UnixTimeSinceEpoch();
658 gim.message = imstr.ToString ();
659 gim.dialog = (byte)19; // messgage from script
660 gim.fromGroup = false;
661 gim.offline = (byte)0;
662 gim.ParentEstateID = 0;
663 gim.Position = pos;
664 gim.RegionID = m_Engine.World.RegionInfo.RegionID.Guid;
665 gim.binaryBucket = Util.StringToBytes256(
666 "{0}/{1}/{2}/{3}",
667 m_Engine.World.RegionInfo.RegionName,
668 (int)Math.Floor(pos.X),
669 (int)Math.Floor(pos.Y),
670 (int)Math.Floor(pos.Z));
671 transferModule.SendInstantMessage(gim, delegate(bool success) {});
672 }
673
674 /*
675 * Say script is sleeping for a very long time.
676 * Reset() is able to cancel this sleeping.
677 */
678 m_SleepUntil = DateTime.MaxValue;
679 }
680
681 /**
682 * @brief The user clicked the Reset Script button.
683 * We want to reset the script to a never-has-ever-run-before state.
684 */
685 public void Reset()
686 {
687 checkstate:
688 XMRInstState iState = m_IState;
689 switch (iState) {
690
691 /*
692 * If it's really being constructed now, that's about as reset as we get.
693 */
694 case XMRInstState.CONSTRUCT: {
695 return;
696 }
697
698 /*
699 * If it's idle, that means it is ready to receive a new event.
700 * So we lock the event queue to prevent another thread from taking
701 * it out of idle, verify that it is still in idle then transition
702 * it to resetting so no other thread will touch it.
703 */
704 case XMRInstState.IDLE: {
705 lock (m_QueueLock) {
706 if (m_IState == XMRInstState.IDLE) {
707 m_IState = XMRInstState.RESETTING;
708 break;
709 }
710 }
711 goto checkstate;
712 }
713
714 /*
715 * If it's on the start queue, that means it is about to dequeue an
716 * event and start processing it. So we lock the start queue so it
717 * can't be started and transition it to resetting so no other thread
718 * will touch it.
719 */
720 case XMRInstState.ONSTARTQ: {
721 lock (m_Engine.m_StartQueue) {
722 if (m_IState == XMRInstState.ONSTARTQ) {
723 m_Engine.m_StartQueue.Remove(this);
724 m_IState = XMRInstState.RESETTING;
725 break;
726 }
727 }
728 goto checkstate;
729 }
730
731 /*
732 * If it's running, tell CheckRun() to suspend the thread then go back
733 * to see what it got transitioned to.
734 */
735 case XMRInstState.RUNNING: {
736 suspendOnCheckRunHold = true;
737 lock (m_QueueLock) { }
738 goto checkstate;
739 }
740
741 /*
742 * If it's sleeping, remove it from sleep queue and transition it to
743 * resetting so no other thread will touch it.
744 */
745 case XMRInstState.ONSLEEPQ: {
746 lock (m_Engine.m_SleepQueue) {
747 if (m_IState == XMRInstState.ONSLEEPQ) {
748 m_Engine.m_SleepQueue.Remove(this);
749 m_IState = XMRInstState.RESETTING;
750 break;
751 }
752 }
753 goto checkstate;
754 }
755
756 /*
757 * It was just removed from the sleep queue and is about to be put
758 * on the yield queue (ie, is being woken up).
759 * Let that thread complete transition and try again.
760 */
761 case XMRInstState.REMDFROMSLPQ: {
762 Sleep (10);
763 goto checkstate;
764 }
765
766 /*
767 * If it's yielding, remove it from yield queue and transition it to
768 * resetting so no other thread will touch it.
769 */
770 case XMRInstState.ONYIELDQ: {
771 lock (m_Engine.m_YieldQueue) {
772 if (m_IState == XMRInstState.ONYIELDQ) {
773 m_Engine.m_YieldQueue.Remove(this);
774 m_IState = XMRInstState.RESETTING;
775 break;
776 }
777 }
778 goto checkstate;
779 }
780
781 /*
782 * If it just finished running something, let that thread transition it
783 * to its next state then check again.
784 */
785 case XMRInstState.FINISHED: {
786 Sleep (10);
787 goto checkstate;
788 }
789
790 /*
791 * If it's disposed, that's about as reset as it gets.
792 */
793 case XMRInstState.DISPOSED: {
794 return;
795 }
796
797 /*
798 * Some other thread is already resetting it, let it finish.
799 */
800 case XMRInstState.RESETTING: {
801 return;
802 }
803
804 default: throw new Exception("bad state");
805 }
806
807 /*
808 * This thread transitioned the instance to RESETTING so reset it.
809 */
810 lock (m_RunLock) {
811 CheckRunLockInvariants(true);
812
813 /*
814 * No other thread should have transitioned it from RESETTING.
815 */
816 if (m_IState != XMRInstState.RESETTING) throw new Exception("bad state");
817
818 /*
819 * If the microthread is active, that means it has call frame
820 * context that we don't want. Throw it out and get a fresh one.
821 */
822 if (microthread.Active () < 0) {
823 microthread.Dispose ();
824 microthread = (IScriptUThread)m_Engine.uThreadCtor.Invoke (new object[] { this });
825 }
826
827 /*
828 * Mark it idle now so it can get queued to process new stuff.
829 */
830 m_IState = XMRInstState.IDLE;
831
832 /*
833 * Reset everything and queue up default's start_entry() event.
834 */
835 ClearQueue();
836 ResetLocked("external Reset");
837
838 CheckRunLockInvariants(true);
839 }
840 }
841
842 private void ClearQueueExceptLinkMessages()
843 {
844 lock (m_QueueLock) {
845 EventParams[] linkMessages = new EventParams[m_EventQueue.Count];
846 int n = 0;
847 foreach (EventParams evt2 in m_EventQueue) {
848 if (evt2.EventName == "link_message") {
849 linkMessages[n++] = evt2;
850 }
851 }
852
853 m_EventQueue.Clear();
854 for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
855
856 for (int i = 0; i < n; i ++) {
857 m_EventQueue.AddLast(linkMessages[i]);
858 }
859
860 m_EventCounts[(int)ScriptEventCode.link_message] = n;
861 }
862 }
863
864 private void ClearQueue()
865 {
866 lock (m_QueueLock)
867 {
868 m_EventQueue.Clear(); // no events queued
869 for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
870 }
871 }
872
873 /**
874 * @brief The script called llResetScript() while it was running and
875 * has suspended. We want to reset the script to a never-has-
876 * ever-run-before state.
877 *
878 * Caller must have m_RunLock locked so we know script isn't
879 * running.
880 */
881 private void ResetLocked(string from)
882 {
883 m_RunOnePhase = "ResetLocked: releasing controls";
884 ReleaseControls();
885
886 m_RunOnePhase = "ResetLocked: removing script";
887 m_Part.Inventory.GetInventoryItem(m_ItemID).PermsMask = 0;
888 m_Part.Inventory.GetInventoryItem(m_ItemID).PermsGranter = UUID.Zero;
889 IUrlModule urlModule = m_Engine.World.RequestModuleInterface<IUrlModule>();
890 if (urlModule != null)
891 urlModule.ScriptRemoved(m_ItemID);
892
893 AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
894
895 m_RunOnePhase = "ResetLocked: clearing current event";
896 this.eventCode = ScriptEventCode.None; // not processing an event
897 m_DetectParams = null; // not processing an event
898 m_SleepUntil = DateTime.MinValue; // not doing llSleep()
899 m_ResetCount ++; // has been reset once more
900
901 /*
902 * Tell next call to 'default state_entry()' to reset all global
903 * vars to their initial values.
904 */
905 doGblInit = true;
906
907 /*
908 * Set script to 'default' state and queue call to its
909 * 'state_entry()' event handler.
910 */
911 m_RunOnePhase = "ResetLocked: posting default:state_entry() event";
912 stateCode = 0;
913 m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(0));
914 PostEvent(new EventParams("state_entry",
915 zeroObjectArray,
916 zeroDetectParams));
917
918 /*
919 * Tell CheckRun() to let script run.
920 */
921 suspendOnCheckRunHold = false;
922 suspendOnCheckRunTemp = false;
923 m_RunOnePhase = "ResetLocked: reset complete";
924 }
925
926 private void ReleaseControls()
927 {
928 if (m_Part != null)
929 {
930 bool found;
931 int permsMask;
932 UUID permsGranter;
933
934 try {
935 permsGranter = m_Part.TaskInventory[m_ItemID].PermsGranter;
936 permsMask = m_Part.TaskInventory[m_ItemID].PermsMask;
937 found = true;
938 } catch {
939 permsGranter = UUID.Zero;
940 permsMask = 0;
941 found = false;
942 }
943
944 if (found && ((permsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)) {
945 ScenePresence presence = m_Engine.World.GetScenePresence(permsGranter);
946 if (presence != null) {
947 presence.UnRegisterControlEventsToScript(m_LocalID, m_ItemID);
948 }
949 }
950 }
951 }
952
953 /**
954 * @brief The script code should call this routine whenever it is
955 * convenient to perform a migation or switch microthreads.
956 */
957 public override void CheckRunWork ()
958 {
959 m_CheckRunPhase = "entered";
960
961 /*
962 * Stay stuck in this loop as long as something wants us suspended.
963 */
964 while (suspendOnCheckRunHold || suspendOnCheckRunTemp) {
965 m_CheckRunPhase = "top of while";
966
967 /*
968 * See if MigrateOutEventHandler() has been called.
969 * If so, dump our stack to stackFrames and unwind.
970 */
971 if (this.captureStackFrames) {
972
973 /*
974 * Puque our stack to the output stream.
975 * But otherwise, our state remains intact.
976 */
977 m_CheckRunPhase = "saving";
978 this.callMode = CallMode_SAVE;
979 this.stackFrames = null;
980 throw new StackCaptureException ();
981 }
982
983 /*
984 * We get here when the script state has been read in by MigrateInEventHandler().
985 * Since the stack is completely restored at this point, any subsequent calls
986 * within the functions should do their normal processing instead of trying to
987 * restore their state.
988 */
989 if (this.callMode == CallMode_RESTORE) {
990 stackFramesRestored = true;
991 this.callMode = CallMode_NORMAL;
992 }
993
994 /*
995 * Now we are ready to suspend the microthread.
996 * This is like a longjmp() to the most recent StartEx() or ResumeEx()
997 * with a simultaneous setjmp() so ResumeEx() can longjmp() back here.
998 */
999 m_CheckRunPhase = "suspending";
1000 suspendOnCheckRunTemp = false;
1001 microthread.Hiber ();
1002 m_CheckRunPhase = "resumed";
1003 }
1004
1005 m_CheckRunPhase = "returning";
1006
1007 /*
1008 * Upon return from CheckRun() it should always be the case that the script is
1009 * going to process calls normally, neither saving nor restoring stack frame state.
1010 */
1011 if (callMode != CallMode_NORMAL) throw new Exception ("bad callMode " + callMode);
1012 }
1013
1014 /**
1015 * @brief Allow script to dequeue events.
1016 */
1017 public void ResumeIt()
1018 {
1019 lock (m_QueueLock)
1020 {
1021 m_Suspended = false;
1022 if ((m_EventQueue != null) &&
1023 (m_EventQueue.First != null) &&
1024 (m_IState == XMRInstState.IDLE)) {
1025 m_IState = XMRInstState.ONSTARTQ;
1026 m_Engine.QueueToStart(this);
1027 }
1028 m_HasRun = true;
1029 }
1030 }
1031
1032 /**
1033 * @brief Block script from dequeuing events.
1034 */
1035 public void SuspendIt()
1036 {
1037 lock (m_QueueLock)
1038 {
1039 m_Suspended = true;
1040 }
1041 }
1042 }
1043
1044 /**
1045 * @brief Thrown by CheckRun() to unwind the script stack, capturing frames to
1046 * instance.stackFrames as it unwinds. We don't want scripts to be able
1047 * to intercept this exception as it would block the stack capture
1048 * functionality.
1049 */
1050 public class StackCaptureException : Exception, IXMRUncatchable { }
1051}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstSorpra.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstSorpra.cs
new file mode 100644
index 0000000..dd60cb2
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstSorpra.cs
@@ -0,0 +1,76 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenMetaverse;
29using OpenSim.Framework;
30using OpenSim.Region.Framework.Scenes;
31using OpenSim.Region.ScriptEngine.Shared;
32using System;
33
34using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
35using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
36using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
37using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
38using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
39using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
40using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
41
42namespace OpenSim.Region.ScriptEngine.XMREngine
43{
44 public partial class XMRInstance
45 {
46 /**
47 * @brief If RegionCrossing trapping is enabled, any attempt to move the object
48 * outside its current region will cause the event to fire and the object
49 * will remain in its current region.
50 */
51 public override void xmrTrapRegionCrossing (int en)
52 { }
53
54 /**
55 * @brief Move object to new position and rotation asynchronously.
56 * Can move object across region boundary.
57 * @param pos = new position within current region (same coords as llGetPos())
58 * @param rot = new rotation within current region (same coords as llGetRot())
59 * @param options = not used
60 * @param evcode = not used
61 * @param evargs = arguments to pass to event handler
62 * @returns false: completed synchronously, no event will be queued
63 */
64 public const double Sorpra_MIN_CROSS = 1.0 / 512.0; // ie, ~2mm
65 public const int Sorpra_TIMEOUT_MS = 30000; // ie, 30sec
66 public override bool xmrSetObjRegPosRotAsync (LSL_Vector pos, LSL_Rotation rot, int options, int evcode, LSL_List evargs)
67 {
68 // do the move
69 SceneObjectGroup sog = m_Part.ParentGroup;
70 sog.UpdateGroupRotationPR (pos, rot);
71
72 // it is always synchronous
73 return false;
74 }
75 }
76}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRObjectTokens.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRObjectTokens.cs
new file mode 100644
index 0000000..f214f28
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRObjectTokens.cs
@@ -0,0 +1,5476 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using System;
30using System.Collections.Generic;
31using System.IO;
32using System.Reflection;
33using System.Reflection.Emit;
34using System.Text;
35
36using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
37using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
38using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
39using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
40using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
41using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
42using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
43
44/**
45 * Contains classes that disassemble or decompile an xmrobj file.
46 * See xmrengcomp.cx utility program.
47 */
48
49namespace OpenSim.Region.ScriptEngine.XMREngine {
50
51 /*
52 * Encapsulate object code for a method.
53 */
54 public abstract class ObjectTokens {
55 public ScriptObjCode scriptObjCode;
56
57 public ObjectTokens (ScriptObjCode scriptObjCode)
58 {
59 this.scriptObjCode = scriptObjCode;
60 }
61
62 public abstract void Close ();
63 public abstract void BegMethod (DynamicMethod method);
64 public abstract void EndMethod ();
65 public abstract void DefineLabel (int number, string name);
66 public abstract void DefineLocal (int number, string name, string type, Type syType);
67 public abstract void DefineMethod (string methName, Type retType, Type[] argTypes, string[] argNames);
68 public abstract void MarkLabel (int offset, int number);
69 public abstract void BegExcBlk (int offset);
70 public abstract void BegCatBlk (int offset, Type excType);
71 public abstract void BegFinBlk (int offset);
72 public abstract void EndExcBlk (int offset);
73 public abstract void EmitNull (int offset, OpCode opCode);
74 public abstract void EmitField (int offset, OpCode opCode, FieldInfo field);
75 public abstract void EmitLocal (int offset, OpCode opCode, int number);
76 public abstract void EmitType (int offset, OpCode opCode, Type type);
77 public abstract void EmitLabel (int offset, OpCode opCode, int number);
78 public abstract void EmitLabels (int offset, OpCode opCode, int[] numbers);
79 public abstract void EmitMethod (int offset, OpCode opCode, MethodInfo method);
80 public abstract void EmitCtor (int offset, OpCode opCode, ConstructorInfo ctor);
81 public abstract void EmitDouble (int offset, OpCode opCode, double value);
82 public abstract void EmitFloat (int offset, OpCode opCode, float value);
83 public abstract void EmitInteger (int offset, OpCode opCode, int value);
84 public abstract void EmitString (int offset, OpCode opCode, string value);
85 }
86
87 /******************\
88 * DISASSEMBLER *
89 \******************/
90
91 public class OTDisassemble : ObjectTokens {
92 private static readonly int OPCSTRWIDTH = 12;
93
94 private Dictionary<int,string> labelNames;
95 private Dictionary<int,string> localNames;
96 private StringBuilder lbuf = new StringBuilder ();
97 private TextWriter twout;
98
99 public OTDisassemble (ScriptObjCode scriptObjCode, TextWriter twout) : base (scriptObjCode)
100 {
101 this.twout = twout;
102 }
103
104 public override void Close ()
105 {
106 twout.WriteLine ("TheEnd.");
107 }
108
109 /**
110 * About to generate object code for this method.
111 */
112 public override void BegMethod (DynamicMethod method)
113 {
114 labelNames = new Dictionary<int,string> ();
115 localNames = new Dictionary<int,string> ();
116
117 twout.WriteLine ("");
118
119 lbuf.Append (method.ReturnType.Name);
120 lbuf.Append (' ');
121 lbuf.Append (method.Name);
122
123 ParameterInfo[] parms = method.GetParameters ();
124 int nArgs = parms.Length;
125 lbuf.Append (" (");
126 for (int i = 0; i < nArgs; i ++) {
127 if (i > 0) lbuf.Append (", ");
128 lbuf.Append (parms[i].ParameterType.Name);
129 }
130 lbuf.Append (')');
131 FlushLine ();
132
133 lbuf.Append ('{');
134 FlushLine ();
135 }
136
137 /**
138 * Dump out reconstructed source for this method.
139 */
140 public override void EndMethod ()
141 {
142 lbuf.Append ('}');
143 FlushLine ();
144 }
145
146 /**
147 * Add instructions to stream.
148 */
149 public override void DefineLabel (int number, string name)
150 {
151 labelNames[number] = name + "$" + number;
152 }
153
154 public override void DefineLocal (int number, string name, string type, Type syType)
155 {
156 localNames[number] = name + "$" + number;
157
158 lbuf.Append (" ");
159 lbuf.Append (type.PadRight (OPCSTRWIDTH - 1));
160 lbuf.Append (' ');
161 lbuf.Append (localNames[number]);
162 FlushLine ();
163 }
164
165 public override void DefineMethod (string methName, Type retType, Type[] argTypes, string[] argNames)
166 { }
167
168 public override void MarkLabel (int offset, int number)
169 {
170 LinePrefix (offset);
171 lbuf.Append (labelNames[number]);
172 lbuf.Append (":");
173 FlushLine ();
174 }
175
176 public override void BegExcBlk (int offset)
177 {
178 LinePrefix (offset);
179 lbuf.Append (" BeginExceptionBlock");
180 FlushLine ();
181 }
182
183 public override void BegCatBlk (int offset, Type excType)
184 {
185 LinePrefix (offset);
186 lbuf.Append (" BeginCatchBlock ");
187 lbuf.Append (excType.Name);
188 FlushLine ();
189 }
190
191 public override void BegFinBlk (int offset)
192 {
193 LinePrefix (offset);
194 lbuf.Append (" BeginFinallyBlock");
195 FlushLine ();
196 }
197
198 public override void EndExcBlk (int offset)
199 {
200 LinePrefix (offset);
201 lbuf.Append (" EndExceptionBlock");
202 FlushLine ();
203 }
204
205 public override void EmitNull (int offset, OpCode opCode)
206 {
207 LinePrefix (offset, opCode);
208 FlushLine ();
209 }
210
211 public override void EmitField (int offset, OpCode opCode, FieldInfo field)
212 {
213 LinePrefix (offset, opCode);
214 lbuf.Append (field.DeclaringType.Name);
215 lbuf.Append (':');
216 lbuf.Append (field.Name);
217 lbuf.Append (" -> ");
218 lbuf.Append (field.FieldType.Name);
219 lbuf.Append (" (field)");
220 FlushLine ();
221 }
222
223 public override void EmitLocal (int offset, OpCode opCode, int number)
224 {
225 LinePrefix (offset, opCode);
226 lbuf.Append (localNames[number]);
227 lbuf.Append (" (local)");
228 FlushLine ();
229 }
230
231 public override void EmitType (int offset, OpCode opCode, Type type)
232 {
233 LinePrefix (offset, opCode);
234 lbuf.Append (type.Name);
235 lbuf.Append (" (type)");
236 FlushLine ();
237 }
238
239 public override void EmitLabel (int offset, OpCode opCode, int number)
240 {
241 LinePrefix (offset, opCode);
242 lbuf.Append (labelNames[number]);
243 lbuf.Append (" (label)");
244 FlushLine ();
245 }
246
247 public override void EmitLabels (int offset, OpCode opCode, int[] numbers)
248 {
249 LinePrefix (offset, opCode);
250
251 int lineLen = lbuf.Length;
252 int nLabels = numbers.Length;
253 for (int i = 0; i < nLabels; i ++) {
254 if (i > 0) {
255 lbuf.AppendLine ();
256 lbuf.Append (",".PadLeft (lineLen));
257 }
258 lbuf.Append (labelNames[numbers[i]]);
259 }
260
261 FlushLine ();
262 }
263
264 public override void EmitMethod (int offset, OpCode opCode, MethodInfo method)
265 {
266 LinePrefix (offset, opCode);
267
268 ParameterInfo[] parms = method.GetParameters ();
269 int nArgs = parms.Length;
270 if (method.DeclaringType != null) {
271 lbuf.Append (method.DeclaringType.Name);
272 lbuf.Append (':');
273 }
274 lbuf.Append (method.Name);
275 lbuf.Append ('(');
276 for (int i = 0; i < nArgs; i ++) {
277 if (i > 0) lbuf.Append (",");
278 lbuf.Append (parms[i].ParameterType.Name);
279 }
280 lbuf.Append (") -> ");
281 lbuf.Append (method.ReturnType.Name);
282
283 FlushLine ();
284 }
285
286 public override void EmitCtor (int offset, OpCode opCode, ConstructorInfo ctor)
287 {
288 LinePrefix (offset, opCode);
289
290 ParameterInfo[] parms = ctor.GetParameters ();
291 int nArgs = parms.Length;
292 lbuf.Append (ctor.DeclaringType.Name);
293 lbuf.Append (":(");
294 for (int i = 0; i < nArgs; i ++) {
295 if (i > 0) lbuf.Append (",");
296 lbuf.Append (parms[i].ParameterType.Name);
297 }
298 lbuf.Append (")");
299
300 FlushLine ();
301 }
302
303 public override void EmitDouble (int offset, OpCode opCode, double value)
304 {
305 LinePrefix (offset, opCode);
306 lbuf.Append (value.ToString ());
307 lbuf.Append (" (double)");
308 FlushLine ();
309 }
310
311 public override void EmitFloat (int offset, OpCode opCode, float value)
312 {
313 LinePrefix (offset, opCode);
314 lbuf.Append (value.ToString ());
315 lbuf.Append (" (float)");
316 FlushLine ();
317 }
318
319 public override void EmitInteger (int offset, OpCode opCode, int value)
320 {
321 LinePrefix (offset, opCode);
322 lbuf.Append (value.ToString ());
323 lbuf.Append (" (int)");
324 FlushLine ();
325 }
326
327 public override void EmitString (int offset, OpCode opCode, string value)
328 {
329 LinePrefix (offset, opCode);
330 lbuf.Append ("\"");
331 lbuf.Append (value);
332 lbuf.Append ("\" (string)");
333 FlushLine ();
334 }
335
336 /**
337 * Put offset and opcode at beginning of line.
338 */
339 private void LinePrefix (int offset, OpCode opCode)
340 {
341 LinePrefix (offset);
342 lbuf.Append (" ");
343 lbuf.Append (opCode.ToString ().PadRight (OPCSTRWIDTH - 1));
344 lbuf.Append (' ');
345 }
346
347 private void LinePrefix (int offset)
348 {
349 lbuf.Append (" ");
350 lbuf.Append (offset.ToString ("X4"));
351 lbuf.Append (" ");
352 }
353
354 /**
355 * Flush line buffer to output file.
356 */
357 private void FlushLine ()
358 {
359 if (lbuf.Length > 0) {
360 twout.WriteLine (lbuf.ToString ());
361 lbuf.Remove (0, lbuf.Length);
362 }
363 }
364 }
365
366 /****************\
367 * DECOMPILER *
368 \****************/
369
370 /**
371 * Note: The decompiler does not handle any xmroption extensions
372 * such as &&&, |||, ? operators and switch statements, as
373 * they do branches with a non-empty stack, which is way
374 * beyond this code's ability to analyze.
375 */
376
377 public class OTDecompile : ObjectTokens {
378 public const string _mainCallNo = "__mainCallNo$";
379 public const string _callLabel = "__call_";
380 public const string _callMode = "callMode";
381 public const string _checkRunQuick = "CheckRunQuick";
382 public const string _checkRunStack = "CheckRunStack";
383 public const string _cmRestore = "__cmRestore";
384 public const string _doBreak = "dobreak_";
385 public const string _doCont = "docont_";
386 public const string _doGblInit = "doGblInit";
387 public const string _doLoop = "doloop_";
388 public const string _ehArgs = "ehArgs";
389 public const string _forBreak = "forbreak_";
390 public const string _forCont = "forcont_";
391 public const string _forLoop = "forloop_";
392 public const string _globalvarinit = "$globalvarinit()";
393 public const string _heapTrackerPop = "Pop";
394 public const string _heapTrackerPush = "Push";
395 public const string _ifDone = "ifdone_";
396 public const string _ifElse = "ifelse_";
397 public const string _llAbstemp = "llAbstemp";
398 public const string _retlbl = "__retlbl";
399 public const string _retval = "__retval$";
400 public const string _whileBreak = "whilebreak_";
401 public const string _whileCont = "whilecont_";
402 public const string _whileLoop = "whileloop_";
403 public const string _xmrinst = "__xmrinst";
404 public const string _xmrinstlocal = "__xmrinst$";
405
406 private const string INDENT = " ";
407 private const string LABELINDENT = " ";
408
409 private static Dictionary<string,string> typeTranslator = InitTypeTranslator ();
410 private static Dictionary<string,string> InitTypeTranslator ()
411 {
412 Dictionary<string,string> d = new Dictionary<string,string> ();
413 d["Boolean"] = "integer";
414 d["bool"] = "integer";
415 d["Double"] = "float";
416 d["double"] = "float";
417 d["Int32"] = "integer";
418 d["int"] = "integer";
419 d["htlist"] = "list";
420 d["htobject"] = "object";
421 d["htstring"] = "string";
422 d["lslfloat"] = "float";
423 d["lslint"] = "integer";
424 d["lsllist"] = "list";
425 d["lslrot"] = "rotation";
426 d["lslstr"] = "string";
427 d["lslvec"] = "vector";
428 d["Quaternion"] = "rotation";
429 d["String"] = "string";
430 d["Vector3"] = "vector";
431 return d;
432 }
433
434 private Dictionary<int,OTLocal> eharglist;
435 private Dictionary<int,OTLabel> labels;
436 private Dictionary<int,OTLocal> locals;
437 private Dictionary<string,string[]> methargnames;
438 private LinkedList<OTCilInstr> cilinstrs;
439 private OTStmtBlock topBlock;
440 private Stack<OTOpnd> opstack;
441 private Stack<OTStmtBegExcBlk> trystack;
442 private Stack<OTStmtBlock> blockstack;
443
444 private int dupNo;
445 private DynamicMethod method;
446 private string laststate;
447 private TextWriter twout;
448
449 public OTDecompile (ScriptObjCode scriptObjCode, TextWriter twout) : base (scriptObjCode)
450 {
451 this.twout = twout;
452 twout.Write ("xmroption dollarsigns;");
453 methargnames = new Dictionary<string,string[]> ();
454 }
455
456 public override void Close ()
457 {
458 if (laststate != null) {
459 twout.Write ("\n}");
460 laststate = null;
461 }
462 twout.Write ('\n');
463 }
464
465 /**
466 * About to generate object code for this method.
467 */
468 public override void BegMethod (DynamicMethod method)
469 {
470 this.method = method;
471
472 eharglist = new Dictionary<int,OTLocal> ();
473 labels = new Dictionary<int,OTLabel> ();
474 locals = new Dictionary<int,OTLocal> ();
475 cilinstrs = new LinkedList<OTCilInstr> ();
476 opstack = new Stack<OTOpnd> ();
477 trystack = new Stack<OTStmtBegExcBlk> ();
478 blockstack = new Stack<OTStmtBlock> ();
479
480 dupNo = 0;
481 }
482
483 /**
484 * Dump out reconstructed source for this method.
485 */
486 public override void EndMethod ()
487 {
488 /*
489 * Convert CIL code to primitive statements.
490 * There are a bunch of labels and internal code such as call stack save restore.
491 */
492 topBlock = new OTStmtBlock ();
493 blockstack.Push (topBlock);
494 for (LinkedListNode<OTCilInstr> link = cilinstrs.First; link != null; link = link.Next) {
495 link.Value.BuildStatements (this, link);
496 }
497
498 /*
499 * Strip out stuff we don't want, such as references to callMode.
500 * This strips out stack frame capture and restore code.
501 */
502 topBlock.StripStuff (null);
503
504 // including a possible final return statement
505 // - delete if void return value
506 // - delete if returning __retval cuz we converted all __retval assignments to return statements
507 if ((topBlock.blkstmts.Last != null) && (topBlock.blkstmts.Last.Value is OTStmtRet)) {
508 OTStmtRet finalret = (OTStmtRet) topBlock.blkstmts.Last.Value;
509 if ((finalret.value == null) ||
510 ((finalret.value is OTOpndLocal) &&
511 ((OTOpndLocal) finalret.value).local.name.StartsWith (_retval))) {
512 topBlock.blkstmts.RemoveLast ();
513 }
514 }
515
516 /**
517 * At this point, all behind-the-scenes references are removed except
518 * that the do/for/if/while blocks are represented by OTStmtCont-style
519 * if/jumps. So try to convert them to the higher-level structures.
520 */
521 topBlock.DetectDoForIfWhile (null);
522
523 /*
524 * Final strip to get rid of unneeded @forbreak_<suffix>; labels and the like.
525 */
526 topBlock.StripStuff (null);
527
528 /*
529 * Build reference counts so we don't output unneeded declarations,
530 * especially temps and internal variables.
531 */
532 foreach (OTLocal local in locals.Values) {
533 local.nlclreads = 0;
534 local.nlclwrites = 0;
535 }
536 topBlock.CountRefs ();
537 for (IEnumerator<int> localenum = locals.Keys.GetEnumerator (); localenum.MoveNext ();) {
538 OTLocal local = locals[localenum.Current];
539 if (((local.nlclreads | local.nlclwrites) == 0) || local.name.StartsWith (_xmrinstlocal)) {
540 locals.Remove (localenum.Current);
541 localenum = locals.Keys.GetEnumerator ();
542 }
543 }
544
545 /*
546 * Strip the $n off of local vars that are not ambiguous.
547 * Make sure they don't mask globals and arguments as well.
548 */
549 Dictionary<string,int> namecounts = new Dictionary<string,int> ();
550 foreach (Dictionary<int,string> varnames in scriptObjCode.globalVarNames.Values) {
551 foreach (string varname in varnames.Values) {
552 int count;
553 if (!namecounts.TryGetValue (varname, out count)) count = 0;
554 namecounts[varname] = count + 1;
555 }
556 }
557 if (methargnames.ContainsKey (method.Name)) {
558 foreach (string argname in methargnames[method.Name]) {
559 int count;
560 if (!namecounts.TryGetValue (argname, out count)) count = 0;
561 namecounts[argname] = count + 1;
562 }
563 }
564 foreach (OTLocal local in locals.Values) {
565 int i = local.name.LastIndexOf ('$');
566 string name = local.name.Substring (0, i);
567 int count;
568 if (!namecounts.TryGetValue (name, out count)) count = 0;
569 namecounts[name] = count + 1;
570 }
571 foreach (OTLocal local in locals.Values) {
572 int i = local.name.LastIndexOf ('$');
573 string name = local.name.Substring (0, i);
574 int count = namecounts[name];
575 if (count == 1) local.name = name;
576 }
577
578 /*
579 * Print out result.
580 */
581 if (method.Name == _globalvarinit) {
582 GlobalsDump ();
583 } else {
584 MethodDump ();
585 }
586 }
587
588 /**
589 * Add instructions to stream.
590 */
591 public override void DefineLabel (int number, string name)
592 {
593 labels.Add (number, new OTLabel (number, name));
594 }
595 public override void DefineLocal (int number, string name, string type, Type syType)
596 {
597 locals.Add (number, new OTLocal (number, name, type));
598 }
599 public override void DefineMethod (string methName, Type retType, Type[] argTypes, string[] argNames)
600 {
601 methargnames[methName] = argNames;
602 }
603 public override void MarkLabel (int offset, int number)
604 {
605 OTCilInstr label = labels[number];
606 label.offset = offset;
607 cilinstrs.AddLast (label);
608 }
609 public override void BegExcBlk (int offset)
610 {
611 cilinstrs.AddLast (new OTCilBegExcBlk (offset));
612 }
613 public override void BegCatBlk (int offset, Type excType)
614 {
615 cilinstrs.AddLast (new OTCilBegCatBlk (offset, excType));
616 }
617 public override void BegFinBlk (int offset)
618 {
619 cilinstrs.AddLast (new OTCilBegFinBlk (offset));
620 }
621 public override void EndExcBlk (int offset)
622 {
623 cilinstrs.AddLast (new OTCilEndExcBlk (offset));
624 }
625 public override void EmitNull (int offset, OpCode opCode)
626 {
627 cilinstrs.AddLast (new OTCilNull (offset, opCode));
628 }
629 public override void EmitField (int offset, OpCode opCode, FieldInfo field)
630 {
631 cilinstrs.AddLast (new OTCilField (offset, opCode, field));
632 }
633 public override void EmitLocal (int offset, OpCode opCode, int number)
634 {
635 cilinstrs.AddLast (new OTCilLocal (offset, opCode, locals[number]));
636 }
637 public override void EmitType (int offset, OpCode opCode, Type type)
638 {
639 cilinstrs.AddLast (new OTCilType (offset, opCode, type));
640 }
641 public override void EmitLabel (int offset, OpCode opCode, int number)
642 {
643 cilinstrs.AddLast (new OTCilLabel (offset, opCode, labels[number]));
644 }
645 public override void EmitLabels (int offset, OpCode opCode, int[] numbers)
646 {
647 OTLabel[] labelarray = new OTLabel[numbers.Length];
648 for (int i = 0; i < numbers.Length; i ++) {
649 labelarray[i] = labels[numbers[i]];
650 }
651 cilinstrs.AddLast (new OTCilLabels (offset, opCode, labelarray));
652 }
653 public override void EmitMethod (int offset, OpCode opCode, MethodInfo method)
654 {
655 cilinstrs.AddLast (new OTCilMethod (offset, opCode, method));
656 }
657 public override void EmitCtor (int offset, OpCode opCode, ConstructorInfo ctor)
658 {
659 cilinstrs.AddLast (new OTCilCtor (offset, opCode, ctor));
660 }
661 public override void EmitDouble (int offset, OpCode opCode, double value)
662 {
663 cilinstrs.AddLast (new OTCilDouble (offset, opCode, value));
664 }
665 public override void EmitFloat (int offset, OpCode opCode, float value)
666 {
667 cilinstrs.AddLast (new OTCilFloat (offset, opCode, value));
668 }
669 public override void EmitInteger (int offset, OpCode opCode, int value)
670 {
671 cilinstrs.AddLast (new OTCilInteger (offset, opCode, value));
672 }
673 public override void EmitString (int offset, OpCode opCode, string value)
674 {
675 cilinstrs.AddLast (new OTCilString (offset, opCode, value));
676 }
677
678 /**
679 * Add the given statement to the end of the currently open block.
680 */
681 public void AddLastStmt (OTStmt stmt)
682 {
683 blockstack.Peek ().blkstmts.AddLast (stmt);
684 }
685
686 /**
687 * Generate output for $globalvarinit() function.
688 * Also outputs declarations for global variables.
689 */
690 private void GlobalsDump ()
691 {
692 /*
693 * Scan $globalvarinit(). It should only have global var assignments in it.
694 * Also gather up list of variables it initializes.
695 */
696 bool badinit = false;
697 Dictionary<string,string> inittypes = new Dictionary<string,string> ();
698 foreach (OTStmt stmt in topBlock.blkstmts) {
699 if (!(stmt is OTStmtStore)) {
700 badinit = true;
701 break;
702 }
703 OTStmtStore store = (OTStmtStore) stmt;
704 if (!(store.varwr is OTOpndGlobal)) {
705 badinit = true;
706 break;
707 }
708 OTOpndGlobal globalop = (OTOpndGlobal) store.varwr;
709 inittypes[globalop.PrintableString] = "";
710 }
711
712 /*
713 * Scan through list of all global variables in the script.
714 * Output declarations for those what don't have any init statement for them.
715 * Save the type for those that do have init statements.
716 */
717 bool first = true;
718 foreach (string iartypename in scriptObjCode.globalVarNames.Keys) {
719 Dictionary<int,string> varnames = scriptObjCode.globalVarNames[iartypename];
720 string typename = iartypename.ToLowerInvariant ();
721 if (typename.StartsWith ("iar")) typename = typename.Substring (3);
722 if (typename.EndsWith ("s")) typename = typename.Substring (0, typename.Length - 1);
723 foreach (string varname in varnames.Values) {
724 if (!badinit && inittypes.ContainsKey (varname)) {
725 inittypes[varname] = typename;
726 } else {
727 if (first) twout.Write ('\n');
728 twout.Write ('\n' + typename + ' ' + varname + ';');
729 first = false;
730 }
731 }
732 }
733
734 /*
735 * If $globalvarinit() has anything bad in it, output it as a function.
736 * Otherwise, output it as a series of global declarations with init values.
737 */
738 if (badinit) {
739 MethodDump ();
740 } else {
741 foreach (OTStmt stmt in topBlock.blkstmts) {
742 OTStmtStore store = (OTStmtStore) stmt;
743 OTOpndGlobal globalop = (OTOpndGlobal) store.varwr;
744 string name = globalop.PrintableString;
745 if (first) twout.Write ('\n');
746 twout.Write ('\n' + inittypes[name] + ' ');
747 store.PrintStmt (twout, "");
748 first = false;
749 }
750 }
751 }
752
753 /**
754 * Generate output for other functions.
755 */
756 private void MethodDump ()
757 {
758 string indent;
759
760 /*
761 * Event handlers don't have an argument list as such in the original
762 * code. Instead they have a series of assignments from ehargs[] to
763 * local variables. So make those local variables look like they are
764 * an argument list.
765 */
766 int i = method.Name.IndexOf (' ');
767 if (i >= 0) {
768
769 /*
770 * Maybe we have to output the state name.
771 */
772 string statename = method.Name.Substring (0, i);
773 string eventname = method.Name.Substring (++ i);
774
775 if (laststate != statename) {
776 if (laststate != null) twout.Write ("\n}");
777 if (statename == "default") {
778 twout.Write ("\n\ndefault {");
779 } else {
780 twout.Write ("\n\nstate " + statename + " {");
781 }
782 laststate = statename;
783 } else {
784 twout.Write ('\n');
785 }
786
787 /*
788 * Output event name and argument list.
789 * Remove from locals list so they don't print below.
790 */
791 twout.Write ('\n' + INDENT + eventname + " (");
792 MethodInfo meth = typeof (IEventHandlers).GetMethod (eventname);
793 i = 0;
794 foreach (ParameterInfo pi in meth.GetParameters ()) {
795 // skip the first param cuz it's the XMRInstance arg
796 if (i > 0) twout.Write (", ");
797 OTLocal local;
798 if (eharglist.TryGetValue (i, out local) && locals.ContainsKey (local.number)) {
799 twout.Write (local.DumpString ());
800 locals.Remove (local.number);
801 } else {
802 // maybe the assignment was removed
803 // eg, because the local was write-only (not referenced)
804 // so substitute in placeholder that won't be referenced
805 twout.Write (AbbrType (pi.ParameterType) + " arg$" + (i + 1));
806 }
807 i ++;
808 }
809 twout.Write (')');
810
811 /*
812 * Indent method body by 4 spaces.
813 */
814 indent = INDENT;
815 } else {
816
817 /*
818 * Maybe need to close out previous state.
819 */
820 if (laststate != null) {
821 twout.Write ("\n}");
822 laststate = null;
823 }
824
825 /*
826 * Output blank line and return type (if any).
827 */
828 twout.Write ("\n\n");
829 if (method.ReturnType != typeof (void)) {
830 twout.Write (AbbrType (method.ReturnType) + ' ');
831 }
832
833 /*
834 * Output method name and argument list.
835 */
836 int j = method.Name.IndexOf ('(');
837 if (j < 0) {
838 twout.Write (method.Name);
839 } else {
840 twout.Write (method.Name.Substring (0, j) + " (");
841 bool first = true;
842 j = 0;
843 foreach (ParameterInfo pi in method.GetParameters ()) {
844 if (j > 0) { // skip the XMRInstance arg$0 parameter
845 if (!first) twout.Write (", ");
846 twout.Write (AbbrType (pi.ParameterType) + ' ' + MethArgName (j));
847 first = false;
848 }
849 j ++;
850 }
851 twout.Write (')');
852 }
853
854 /*
855 * Don't indent method body at all.
856 */
857 indent = "";
858 }
859
860 /*
861 * Output local variable declarations.
862 */
863 twout.Write ('\n' + indent + '{');
864 bool didOne = false;
865 foreach (OTLocal local in locals.Values) {
866 twout.Write ('\n' + indent + INDENT + local.DumpString () + "; // r:" + local.nlclreads + " w:" + local.nlclwrites);
867 didOne = true;
868 }
869 if (didOne) twout.Write ('\n');
870
871 /*
872 * Output statements.
873 */
874 if (topBlock.blkstmts.Count == 0) {
875 twout.Write (" }");
876 } else {
877 topBlock.PrintBodyAndEnd (twout, indent);
878 }
879 }
880
881 /**
882 * Get abbreviated type string.
883 */
884 public static string AbbrType (Type type)
885 {
886 if (type == null) return "null";
887 return AbbrType (type.Name);
888 }
889 public static string AbbrType (string type)
890 {
891 if (type.StartsWith ("OpenSim.Region.ScriptEngine.XMREngine.")) {
892 type = type.Substring (38);
893 int i = type.IndexOf (',');
894 if (i > 0) type = type.Substring (0, i);
895 }
896 if (typeTranslator.ContainsKey (type)) {
897 type = typeTranslator[type];
898 }
899 return type;
900 }
901
902 /**
903 * Get current method's argument name.
904 */
905 public string MethArgName (int index)
906 {
907 string[] argnames;
908 if (methargnames.TryGetValue (method.Name, out argnames) && (index < argnames.Length)) {
909 return argnames[index];
910 }
911 return "arg$" + index;
912 }
913
914 /**
915 * Strip svperflvovs (float) cast from rotation/vector values.
916 */
917 public static OTOpnd StripFloatCast (OTOpnd op)
918 {
919 if (op is OTOpndCast) {
920 OTOpndCast opcast = (OTOpndCast) op;
921 if ((opcast.type == typeof (double)) && (opcast.value is OTOpndInt)) {
922 return opcast.value;
923 }
924 }
925 return op;
926 }
927
928 /**
929 * Strip svperflvovs Brtrues so we don't end up with stuff like 'if (!! someint) ...'.
930 */
931 public static OTOpnd StripBrtrue (OTOpnd op)
932 {
933 if (op is OTOpndUnOp) {
934 OTOpndUnOp opunop = (OTOpndUnOp) op;
935 if (opunop.opCode == MyOp.Brtrue) return opunop.value;
936 }
937 return op;
938 }
939
940 /*
941 * Local variable declaration.
942 */
943 private class OTLocal {
944 public int number;
945 public string name;
946 public string type;
947
948 public int nlclreads;
949 public int nlclwrites;
950
951 public OTLocal (int number, string name, string type)
952 {
953 this.number = number;
954 this.name = name.StartsWith ("tmp$") ? name : name + "$" + number;
955 this.type = type;
956 }
957
958 public string DumpString ()
959 {
960 return AbbrType (type) + ' ' + name;
961 }
962 }
963
964 /***********************************************\
965 * Tokens that are one-for-one with CIL code *
966 \***********************************************/
967
968 /*
969 * Part of instruction stream.
970 */
971 public abstract class OTCilInstr {
972 public int offset; // cil offset
973
974 public OTCilInstr (int offset)
975 {
976 this.offset = offset;
977 }
978
979 public abstract string DumpString ();
980 public abstract void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link);
981
982 protected void CheckEmptyStack (OTDecompile decompile, string opMnemonic)
983 {
984 if (decompile.opstack.Count > 0) {
985 Console.Error.WriteLine ("CheckEmptyStack: " + decompile.method.Name + " 0x" + offset.ToString ("X") + ": " +
986 opMnemonic + " stack depth " + decompile.opstack.Count);
987 }
988 }
989 }
990
991 /*
992 * Label mark point.
993 */
994 private class OTLabel : OTCilInstr {
995 public int number;
996 public string name;
997
998 public int lbljumps;
999
1000 public OTLabel (int number, string name) : base (-1)
1001 {
1002 this.number = number;
1003 this.name = name;
1004 }
1005
1006 public string PrintableName {
1007 get {
1008 if (name.StartsWith (_doBreak)) return _doBreak + "$" + number;
1009 if (name.StartsWith (_doCont)) return _doCont + "$" + number;
1010 if (name.StartsWith (_forBreak)) return _forBreak + "$" + number;
1011 if (name.StartsWith (_forCont)) return _forCont + "$" + number;
1012 if (name.StartsWith (_whileBreak)) return _whileBreak + "$" + number;
1013 if (name.StartsWith (_whileCont)) return _whileCont + "$" + number;
1014 return name;
1015 }
1016 }
1017
1018 public override string DumpString ()
1019 {
1020 return name + ":";
1021 }
1022
1023 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1024 {
1025 OTStmtLabel.AddLast (decompile, this);
1026 }
1027 }
1028
1029 /*
1030 * 'try {'
1031 */
1032 private class OTCilBegExcBlk : OTCilInstr {
1033 public LinkedList<OTCilBegCatBlk> catches = new LinkedList<OTCilBegCatBlk> ();
1034
1035 public OTCilBegExcBlk (int offset) : base (offset)
1036 { }
1037
1038 public override string DumpString ()
1039 {
1040 return "try {";
1041 }
1042
1043 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1044 {
1045 CheckEmptyStack (decompile, "try");
1046
1047 // link the try itself onto outer block
1048 OTStmtBegExcBlk trystmt = new OTStmtBegExcBlk ();
1049 decompile.AddLastStmt (trystmt);
1050
1051 // subsequent statements go to the try block
1052 trystmt.tryblock = new OTStmtBlock ();
1053 decompile.trystack.Push (trystmt);
1054 decompile.blockstack.Push (trystmt.tryblock);
1055 }
1056 }
1057
1058 /*
1059 * '} catch (...) {'
1060 */
1061 private class OTCilBegCatBlk : OTCilInstr {
1062 public Type excType;
1063
1064 public OTCilBegCatBlk (int offset, Type excType) : base (offset)
1065 {
1066 this.excType = excType;
1067 }
1068
1069 public override string DumpString ()
1070 {
1071 return "} catch (" + AbbrType (excType) + ") {";
1072 }
1073
1074 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1075 {
1076 CheckEmptyStack (decompile, "catch");
1077
1078 // link the catch itself onto the try statement
1079 OTStmtBegExcBlk trystmt = decompile.trystack.Peek ();
1080 OTStmtBegCatBlk catstmt = new OTStmtBegCatBlk (excType);
1081 trystmt.catches.AddLast (catstmt);
1082
1083 // start capturing statements into the catch block
1084 catstmt.tryblock = trystmt;
1085 catstmt.catchblock = new OTStmtBlock ();
1086 decompile.blockstack.Pop ();
1087 decompile.blockstack.Push (catstmt.catchblock);
1088
1089 // fill the stack slot with something for the exception argument
1090 OTOpndDup dup = new OTOpndDup (++ decompile.dupNo);
1091 decompile.opstack.Push (dup);
1092 }
1093 }
1094
1095 /*
1096 * '} finally {'
1097 */
1098 private class OTCilBegFinBlk : OTCilInstr {
1099 public OTCilBegFinBlk (int offset) : base (offset)
1100 { }
1101
1102 public override string DumpString ()
1103 {
1104 return "} finally {";
1105 }
1106
1107 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1108 {
1109 CheckEmptyStack (decompile, "finally");
1110
1111 // link the finally itself to the try statement
1112 OTStmtBegExcBlk trystmt = decompile.trystack.Peek ();
1113 OTStmtBegFinBlk finstmt = new OTStmtBegFinBlk ();
1114 trystmt.finblock = finstmt;
1115
1116 // start capturing statements into the finally block
1117 finstmt.tryblock = trystmt;
1118 finstmt.finblock = new OTStmtBlock ();
1119 decompile.blockstack.Pop ();
1120 decompile.blockstack.Push (finstmt.finblock);
1121 }
1122 }
1123
1124 /*
1125 * '}' end of try
1126 */
1127 private class OTCilEndExcBlk : OTCilInstr {
1128 public OTCilEndExcBlk (int offset) : base (offset)
1129 { }
1130
1131 public override string DumpString ()
1132 {
1133 return "} // end try";
1134 }
1135
1136 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1137 {
1138 CheckEmptyStack (decompile, "endtry");
1139
1140 // pop the try/catch/finally blocks from stacks
1141 decompile.blockstack.Pop ();
1142 decompile.trystack.Pop ();
1143
1144 // subsequent statements collect following the try
1145 }
1146 }
1147
1148 /*
1149 * Actual opcodes (instructions).
1150 */
1151 private class OTCilNull : OTCilInstr {
1152 public MyOp opCode;
1153
1154 public OTCilNull (int offset, OpCode opCode) : base (offset)
1155 {
1156 this.opCode = MyOp.GetByName (opCode.Name);
1157 }
1158
1159 public override string DumpString ()
1160 {
1161 return opCode.ToString ();
1162 }
1163
1164 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1165 {
1166 switch (opCode.ToString ()) {
1167 case "conv.i1":
1168 case "conv.i2":
1169 case "conv.i4":
1170 case "conv.i8": {
1171 OTOpnd value = decompile.opstack.Pop ();
1172 decompile.opstack.Push (new OTOpndCast (typeof (int), value));
1173 break;
1174 }
1175 case "conv.r4":
1176 case "conv.r8": {
1177 OTOpnd value = decompile.opstack.Pop ();
1178 decompile.opstack.Push (new OTOpndCast (typeof (double), value));
1179 break;
1180 }
1181 case "dup": {
1182 OTOpnd value = decompile.opstack.Pop ();
1183 if (!(value is OTOpndDup)) {
1184 OTOpndDup dup = new OTOpndDup (++ decompile.dupNo);
1185 OTStmtStore.AddLast (decompile, dup, value);
1186 value = dup;
1187 }
1188 decompile.opstack.Push (value);
1189 decompile.opstack.Push (value);
1190 break;
1191 }
1192 case "endfinally": break;
1193 case "ldarg.0": { decompile.opstack.Push (new OTOpndArg (0, false, decompile)); break; }
1194 case "ldarg.1": { decompile.opstack.Push (new OTOpndArg (1, false, decompile)); break; }
1195 case "ldarg.2": { decompile.opstack.Push (new OTOpndArg (2, false, decompile)); break; }
1196 case "ldarg.3": { decompile.opstack.Push (new OTOpndArg (3, false, decompile)); break; }
1197 case "ldc.i4.0": { decompile.opstack.Push (new OTOpndInt (0)); break; }
1198 case "ldc.i4.1": { decompile.opstack.Push (new OTOpndInt (1)); break; }
1199 case "ldc.i4.2": { decompile.opstack.Push (new OTOpndInt (2)); break; }
1200 case "ldc.i4.3": { decompile.opstack.Push (new OTOpndInt (3)); break; }
1201 case "ldc.i4.4": { decompile.opstack.Push (new OTOpndInt (4)); break; }
1202 case "ldc.i4.5": { decompile.opstack.Push (new OTOpndInt (5)); break; }
1203 case "ldc.i4.6": { decompile.opstack.Push (new OTOpndInt (6)); break; }
1204 case "ldc.i4.7": { decompile.opstack.Push (new OTOpndInt (7)); break; }
1205 case "ldc.i4.8": { decompile.opstack.Push (new OTOpndInt (8)); break; }
1206 case "ldc.i4.m1": { decompile.opstack.Push (new OTOpndInt (-1)); break; }
1207 case "ldelem.i4":
1208 case "ldelem.r4":
1209 case "ldelem.r8":
1210 case "ldelem.ref": {
1211 OTOpnd index = decompile.opstack.Pop ();
1212 OTOpnd array = decompile.opstack.Pop ();
1213 decompile.opstack.Push (OTOpndArrayElem.Make (array, index, false, decompile));
1214 break;
1215 }
1216 case "ldnull": {
1217 decompile.opstack.Push (new OTOpndNull ());
1218 break;
1219 }
1220 case "neg":
1221 case "not": {
1222 OTOpnd value = decompile.opstack.Pop ();
1223 decompile.opstack.Push (OTOpndUnOp.Make (opCode, value));
1224 break;
1225 }
1226 case "pop": {
1227 OTStmtVoid.AddLast (decompile, decompile.opstack.Pop ());
1228 break;
1229 }
1230 case "ret": {
1231 OTOpnd value = null;
1232 if (decompile.method.ReturnType != typeof (void)) {
1233 value = decompile.opstack.Pop ();
1234 }
1235 CheckEmptyStack (decompile);
1236 decompile.AddLastStmt (new OTStmtRet (value));
1237 break;
1238 }
1239 case "stelem.i4":
1240 case "stelem.r8":
1241 case "stelem.ref": {
1242 OTOpnd value = decompile.opstack.Pop ();
1243 OTOpnd index = decompile.opstack.Pop ();
1244 OTOpnd array = decompile.opstack.Pop ();
1245 OTStmtStore.AddLast (decompile, OTOpndArrayElem.Make (array, index, false, decompile), value);
1246 break;
1247 }
1248 case "throw": {
1249 OTOpnd value = decompile.opstack.Pop ();
1250 CheckEmptyStack (decompile);
1251 decompile.AddLastStmt (new OTStmtThrow (value, decompile));
1252 break;
1253 }
1254 case "add":
1255 case "and":
1256 case "ceq":
1257 case "cgt":
1258 case "cgt.un":
1259 case "clt":
1260 case "clt.un":
1261 case "div":
1262 case "div.un":
1263 case "mul":
1264 case "or":
1265 case "rem":
1266 case "rem.un":
1267 case "shl":
1268 case "shr":
1269 case "shr.un":
1270 case "sub":
1271 case "xor": {
1272 OTOpnd rite = decompile.opstack.Pop ();
1273 OTOpnd left = decompile.opstack.Pop ();
1274 decompile.opstack.Push (OTOpndBinOp.Make (left, opCode, rite));
1275 break;
1276 }
1277 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1278 }
1279 }
1280
1281 protected void CheckEmptyStack (OTDecompile decompile)
1282 {
1283 CheckEmptyStack (decompile, opCode.ToString ());
1284 }
1285 }
1286
1287 private class OTCilField : OTCilNull {
1288 public FieldInfo field;
1289
1290 public OTCilField (int offset, OpCode opCode, FieldInfo field) : base (offset, opCode)
1291 {
1292 this.field = field;
1293 }
1294
1295 public override string DumpString ()
1296 {
1297 return opCode.ToString () + ' ' + field.Name;
1298 }
1299
1300 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1301 {
1302 switch (opCode.ToString ()) {
1303 case "ldfld": {
1304 OTOpnd obj = decompile.opstack.Pop ();
1305 decompile.opstack.Push (OTOpndField.Make (obj, field));
1306 break;
1307 }
1308 case "ldsfld": {
1309 decompile.opstack.Push (new OTOpndSField (field));
1310 break;
1311 }
1312 case "stfld": {
1313 OTOpnd val = decompile.opstack.Pop ();
1314 OTOpnd obj = decompile.opstack.Pop ();
1315 OTStmtStore.AddLast (decompile, OTOpndField.Make (obj, field), val);
1316 break;
1317 }
1318 case "stsfld": {
1319 OTOpnd val = decompile.opstack.Pop ();
1320 OTStmtStore.AddLast (decompile, new OTOpndSField (field), val);
1321 break;
1322 }
1323 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1324 }
1325 }
1326 }
1327
1328 private class OTCilLocal : OTCilNull {
1329 public OTLocal local;
1330
1331 public OTCilLocal (int offset, OpCode opCode, OTLocal local) : base (offset, opCode)
1332 {
1333 this.local = local;
1334 }
1335
1336 public override string DumpString ()
1337 {
1338 return opCode.ToString () + ' ' + local.name;
1339 }
1340
1341 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1342 {
1343 switch (opCode.ToString ()) {
1344 case "ldloc": {
1345 decompile.opstack.Push (new OTOpndLocal (local));
1346 break;
1347 }
1348 case "ldloca": {
1349 decompile.opstack.Push (new OTOpndLocalRef (local));
1350 break;
1351 }
1352 case "stloc": {
1353 OTOpnd val = decompile.opstack.Pop ();
1354 OTStmtStore.AddLast (decompile, new OTOpndLocal (local), val);
1355 break;
1356 }
1357 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1358 }
1359 }
1360 }
1361
1362 private class OTCilType : OTCilNull {
1363 public Type type;
1364
1365 public OTCilType (int offset, OpCode opCode, Type type) : base (offset, opCode)
1366 {
1367 this.type = type;
1368 }
1369
1370 public override string DumpString ()
1371 {
1372 return opCode.ToString () + ' ' + AbbrType (type);
1373 }
1374
1375 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1376 {
1377 switch (opCode.ToString ()) {
1378 case "box": {
1379 break;
1380 }
1381 case "castclass":
1382 case "unbox.any": {
1383 OTOpnd value = decompile.opstack.Pop ();
1384 decompile.opstack.Push (new OTOpndCast (type, value));
1385 break;
1386 }
1387 case "ldelem": {
1388 OTOpnd index = decompile.opstack.Pop ();
1389 OTOpnd array = decompile.opstack.Pop ();
1390 decompile.opstack.Push (OTOpndArrayElem.Make (array, index, false, decompile));
1391 break;
1392 }
1393 case "ldelema": {
1394 OTOpnd index = decompile.opstack.Pop ();
1395 OTOpnd array = decompile.opstack.Pop ();
1396 decompile.opstack.Push (OTOpndArrayElem.Make (array, index, true, decompile));
1397 break;
1398 }
1399 case "newarr": {
1400 OTOpnd index = decompile.opstack.Pop ();
1401 decompile.opstack.Push (new OTOpndNewarr (type, index));
1402 break;
1403 }
1404 case "stelem": {
1405 OTOpnd value = decompile.opstack.Pop ();
1406 OTOpnd index = decompile.opstack.Pop ();
1407 OTOpnd array = decompile.opstack.Pop ();
1408 OTStmtStore.AddLast (decompile, OTOpndArrayElem.Make (array, index, false, decompile), value);
1409 break;
1410 }
1411 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1412 }
1413 }
1414 }
1415
1416 private class OTCilLabel : OTCilNull {
1417 public OTLabel label;
1418
1419 public OTCilLabel (int offset, OpCode opCode, OTLabel label) : base (offset, opCode)
1420 {
1421 this.label = label;
1422 }
1423
1424 public override string DumpString ()
1425 {
1426 return opCode.ToString () + ' ' + label.name;
1427 }
1428
1429 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1430 {
1431 switch (opCode.ToString ()) {
1432
1433 /*
1434 * We don't handle non-empty stack at branch points.
1435 *
1436 * So handle this case specially:
1437 *
1438 * dup
1439 * ldc.i4.0
1440 * bge.s llAbstemp << we are here
1441 * neg
1442 * llAbstemp:
1443 *
1444 * becomes:
1445 *
1446 * call llAbs
1447 */
1448 case "bge.s": {
1449 OTOpnd rite = decompile.opstack.Pop (); // alleged zero
1450 OTOpnd left = decompile.opstack.Pop (); // alleged dup
1451
1452 if ((label.name == _llAbstemp) && (decompile.opstack.Count > 0)) {
1453 LinkedListNode<OTCilInstr> linkneg = link.Next;
1454 if ((left is OTOpndDup) && (rite is OTOpndInt) &&
1455 (linkneg != null) && (linkneg.Value is OTCilNull) &&
1456 (((OTCilNull) linkneg.Value).opCode == MyOp.Neg)) {
1457 OTOpndInt riteint = (OTOpndInt) rite;
1458 LinkedListNode<OTCilInstr> linklbl = linkneg.Next;
1459 if ((riteint.value == 0) && (linklbl != null) && (linklbl.Value is OTLabel) &&
1460 (((OTLabel) linklbl.Value) == label)) {
1461 linkneg.List.Remove (linkneg);
1462 linklbl.List.Remove (linklbl);
1463 MethodInfo method = typeof (ScriptBaseClass).GetMethod ("llAbs");
1464 OTOpnd[] args = new OTOpnd[] { new OTOpndNull (), decompile.opstack.Pop () };
1465 OTOpndCall.AddLast (decompile, method, args);
1466 break;
1467 }
1468 }
1469 }
1470
1471 CheckEmptyStack (decompile);
1472 OTOpnd valu = OTOpndBinOp.Make (left, opCode, rite);
1473 OTStmt jump = OTStmtJump.Make (label);
1474 decompile.AddLastStmt (new OTStmtCond (valu, jump));
1475 break;
1476 }
1477
1478 case "beq":
1479 case "bge":
1480 case "bgt":
1481 case "ble":
1482 case "blt":
1483 case "bne.un":
1484 case "beq.s":
1485 case "bgt.s":
1486 case "ble.s":
1487 case "blt.s":
1488 case "bne.un.s": {
1489 OTOpnd rite = decompile.opstack.Pop ();
1490 OTOpnd left = decompile.opstack.Pop ();
1491 CheckEmptyStack (decompile);
1492 OTOpnd valu = OTOpndBinOp.Make (left, opCode, rite);
1493 OTStmt jump = OTStmtJump.Make (label);
1494 decompile.AddLastStmt (new OTStmtCond (valu, jump));
1495 break;
1496 }
1497 case "brfalse":
1498 case "brfalse.s":
1499 case "brtrue":
1500 case "brtrue.s": {
1501 OTOpnd value = decompile.opstack.Pop ();
1502 CheckEmptyStack (decompile);
1503 OTOpnd valu = OTOpndUnOp.Make (opCode, value);
1504 OTStmt jump = OTStmtJump.Make (label);
1505 decompile.AddLastStmt (new OTStmtCond (valu, jump));
1506 break;
1507 }
1508 case "br":
1509 case "br.s":
1510 case "leave": {
1511 CheckEmptyStack (decompile);
1512 OTStmt jump = OTStmtJump.Make (label);
1513 decompile.AddLastStmt (jump);
1514 break;
1515 }
1516 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1517 }
1518 }
1519 }
1520
1521 private class OTCilLabels : OTCilNull {
1522 public OTLabel[] labels;
1523
1524 public OTCilLabels (int offset, OpCode opCode, OTLabel[] labels) : base (offset, opCode)
1525 {
1526 this.labels = labels;
1527 }
1528
1529 public override string DumpString ()
1530 {
1531 StringBuilder sb = new StringBuilder ();
1532 sb.Append (opCode.ToString ());
1533 foreach (OTLabel label in labels) {
1534 sb.Append (' ');
1535 sb.Append (label.name);
1536 }
1537 return sb.ToString ();
1538 }
1539
1540 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1541 {
1542 switch (opCode.ToString ()) {
1543 case "switch": {
1544 OTOpnd value = decompile.opstack.Pop ();
1545 CheckEmptyStack (decompile);
1546 decompile.AddLastStmt (new OTStmtSwitch (value, labels));
1547 break;
1548 }
1549 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1550 }
1551 }
1552 }
1553
1554 private class OTCilMethod : OTCilNull {
1555 public MethodInfo method;
1556
1557 public OTCilMethod (int offset, OpCode opCode, MethodInfo method) : base (offset, opCode)
1558 {
1559 this.method = method;
1560 }
1561
1562 public override string DumpString ()
1563 {
1564 return opCode.ToString () + ' ' + method.Name;
1565 }
1566
1567 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1568 {
1569 switch (opCode.ToString ()) {
1570 case "call":
1571 case "callvirt": {
1572 int nargs = method.GetParameters ().Length;
1573 if (!method.IsStatic) nargs ++;
1574 OTOpnd[] args = new OTOpnd[nargs];
1575 for (int i = nargs; -- i >= 0;) {
1576 args[i] = decompile.opstack.Pop ();
1577 }
1578 OTOpndCall.AddLast (decompile, method, args);
1579 break;
1580 }
1581 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1582 }
1583 }
1584 }
1585
1586 private class OTCilCtor : OTCilNull {
1587 public ConstructorInfo ctor;
1588
1589 public OTCilCtor (int offset, OpCode opCode, ConstructorInfo ctor) : base (offset, opCode)
1590 {
1591 this.ctor = ctor;
1592 }
1593
1594 public override string DumpString ()
1595 {
1596 return opCode.ToString () + ' ' + AbbrType (ctor.DeclaringType);
1597 }
1598
1599 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1600 {
1601 switch (opCode.ToString ()) {
1602 case "newobj": {
1603 int nargs = ctor.GetParameters ().Length;
1604 OTOpnd[] args = new OTOpnd[nargs];
1605 for (int i = nargs; -- i >= 0;) {
1606 args[i] = decompile.opstack.Pop ();
1607 }
1608 decompile.opstack.Push (OTOpndNewobj.Make (ctor, args));
1609 break;
1610 }
1611 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1612 }
1613 }
1614 }
1615
1616 private class OTCilDouble : OTCilNull {
1617 public double value;
1618
1619 public OTCilDouble (int offset, OpCode opCode, double value) : base (offset, opCode)
1620 {
1621 this.value = value;
1622 }
1623
1624 public override string DumpString ()
1625 {
1626 return opCode.ToString () + ' ' + value;
1627 }
1628
1629 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1630 {
1631 switch (opCode.ToString ()) {
1632 case "ldc.r8": {
1633 decompile.opstack.Push (new OTOpndDouble (value));
1634 break;
1635 }
1636 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1637 }
1638 }
1639 }
1640
1641 private class OTCilFloat : OTCilNull {
1642 public float value;
1643
1644 public OTCilFloat (int offset, OpCode opCode, float value) : base (offset, opCode)
1645 {
1646 this.value = value;
1647 }
1648
1649 public override string DumpString ()
1650 {
1651 return opCode.ToString () + ' ' + value;
1652 }
1653
1654 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1655 {
1656 switch (opCode.ToString ()) {
1657 case "ldc.r4": {
1658 decompile.opstack.Push (new OTOpndFloat (value));
1659 break;
1660 }
1661 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1662 }
1663 }
1664 }
1665
1666 private class OTCilInteger : OTCilNull {
1667 public int value;
1668
1669 public OTCilInteger (int offset, OpCode opCode, int value) : base (offset, opCode)
1670 {
1671 this.value = value;
1672 }
1673
1674 public override string DumpString ()
1675 {
1676 return opCode.ToString () + ' ' + value;
1677 }
1678
1679 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1680 {
1681 switch (opCode.ToString ()) {
1682 case "ldarg":
1683 case "ldarg.s": {
1684 decompile.opstack.Push (new OTOpndArg (value, false, decompile));
1685 break;
1686 }
1687 case "ldarga":
1688 case "ldarga.s": {
1689 decompile.opstack.Push (new OTOpndArg (value, true, decompile));
1690 break;
1691 }
1692 case "ldc.i4":
1693 case "ldc.i4.s": {
1694 decompile.opstack.Push (new OTOpndInt (value));
1695 break;
1696 }
1697 case "starg": {
1698 OTOpnd val = decompile.opstack.Pop ();
1699 OTStmtStore.AddLast (decompile, new OTOpndArg (value, false, decompile), val);
1700 break;
1701 }
1702 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1703 }
1704 }
1705 }
1706
1707 private class OTCilString : OTCilNull {
1708 public string value;
1709
1710 public OTCilString (int offset, OpCode opCode, string value) : base (offset, opCode)
1711 {
1712 this.value = value;
1713 }
1714
1715 public override string DumpString ()
1716 {
1717 StringBuilder sb = new StringBuilder ();
1718 sb.Append (opCode.ToString ());
1719 sb.Append (' ');
1720 TokenDeclInline.PrintParamString (sb, value);
1721 return sb.ToString ();
1722 }
1723
1724 public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
1725 {
1726 switch (opCode.ToString ()) {
1727 case "ldstr": {
1728 decompile.opstack.Push (new OTOpndString (value));
1729 break;
1730 }
1731 default: throw new Exception ("unknown opcode " + opCode.ToString ());
1732 }
1733 }
1734 }
1735
1736 /***************************************\
1737 * Tokens what are on operand stack. *
1738 \***************************************/
1739
1740 public abstract class OTOpnd {
1741
1742 /**
1743 * See if it possibly has any side effects.
1744 */
1745 public abstract bool HasSideEffects { get; }
1746
1747 /**
1748 * Increment reference counts.
1749 */
1750 public virtual void CountRefs (bool writing)
1751 { }
1752
1753 /**
1754 * If this operand is a 'by reference' operand,
1755 * return the corresponding 'by value' operand.
1756 */
1757 public virtual OTOpnd GetNonByRefOpnd ()
1758 {
1759 return this;
1760 }
1761
1762 /**
1763 * If this operand is same as oldopnd, replace it with newopnd.
1764 *
1765 * This default just does a shallow search which is ok if this operand does not have any sub-operands.
1766 * But it must be overridden for a deep search if this operand has any sub-operands.
1767 */
1768 public virtual OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
1769 {
1770 if (SameAs (oldopnd)) {
1771 rc = true;
1772 return newopnd;
1773 }
1774 return this;
1775 }
1776
1777 /**
1778 * See if the two operands are the same value.
1779 * Note that calls might have side-effects so are never the same.
1780 */
1781 public abstract bool SameAs (OTOpnd other);
1782
1783 /**
1784 * Get a printable string representation of the operand.
1785 */
1786 public abstract string PrintableString { get; }
1787 }
1788
1789 /**
1790 * Argument variable.
1791 */
1792 private class OTOpndArg : OTOpnd {
1793 public int index;
1794 public bool byref;
1795
1796 private OTDecompile decompile;
1797
1798 public OTOpndArg (int index, bool byref, OTDecompile decompile)
1799 {
1800 this.index = index;
1801 this.byref = byref;
1802 this.decompile = decompile;
1803 }
1804
1805 public override bool HasSideEffects {
1806 get {
1807 return false;
1808 }
1809 }
1810
1811 public override OTOpnd GetNonByRefOpnd ()
1812 {
1813 if (!byref) return this;
1814 return new OTOpndArg (index, false, decompile);
1815 }
1816
1817 public override bool SameAs (OTOpnd other)
1818 {
1819 if (!(other is OTOpndArg)) return false;
1820 return (((OTOpndArg) other).byref == byref) && (((OTOpndArg) other).index == index);
1821 }
1822
1823 public override string PrintableString {
1824 get {
1825 string argname = decompile.MethArgName (index);
1826 return byref ? ("ref " + argname) : argname;
1827 }
1828 }
1829 }
1830
1831 /**
1832 * Element of an array.
1833 */
1834 private class OTOpndArrayElem : OTOpnd {
1835 public bool byref;
1836 public OTOpnd array;
1837 public OTOpnd index;
1838
1839 public static OTOpnd Make (OTOpnd array, OTOpnd index, bool byref, OTDecompile decompile)
1840 {
1841 /*
1842 * arg$0.glblVars.iar<type>[<intconst>] is a reference to a global variable
1843 * likewise so is __xmrinst.glblVars.iar<type>[<intconst>]
1844 */
1845 if ((array is OTOpndField) && (index is OTOpndInt)) {
1846
1847 /*
1848 * arrayfield = (arg$0.glblVars).iar<type>
1849 * arrayfieldobj = arg$0.glblVars
1850 * iartypename = iar<type>
1851 */
1852 OTOpndField arrayfield = (OTOpndField) array;
1853 OTOpnd arrayfieldobj = arrayfield.obj;
1854 string iartypename = arrayfield.field.Name;
1855
1856 /*
1857 * See if they are what they are supposed to be.
1858 */
1859 if ((arrayfieldobj is OTOpndField) && iartypename.StartsWith ("iar")) {
1860
1861 /*
1862 * arrayfieldobjfield = arg$0.glblVars
1863 */
1864 OTOpndField arrayfieldobjfield = (OTOpndField) arrayfieldobj;
1865
1866 /*
1867 * See if the parts are what they are supposed to be.
1868 */
1869 if (IsArg0OrXMRInst (arrayfieldobjfield.obj) && (arrayfieldobjfield.field.Name == "glblVars")) {
1870
1871 /*
1872 * Everything matches up, make a global variable instead of an array reference.
1873 */
1874 return new OTOpndGlobal (iartypename, ((OTOpndInt) index).value, byref, decompile.scriptObjCode);
1875 }
1876 }
1877 }
1878
1879 /*
1880 * Other array reference.
1881 */
1882 OTOpndArrayElem it = new OTOpndArrayElem ();
1883 it.array = array;
1884 it.index = index;
1885 it.byref = byref;
1886 return it;
1887 }
1888
1889 private OTOpndArrayElem () { }
1890
1891 public override bool HasSideEffects {
1892 get {
1893 return array.HasSideEffects || index.HasSideEffects;
1894 }
1895 }
1896
1897 public override void CountRefs (bool writing)
1898 {
1899 array.CountRefs (false);
1900 index.CountRefs (false);
1901 }
1902
1903 public override OTOpnd GetNonByRefOpnd ()
1904 {
1905 if (!byref) return this;
1906 OTOpndArrayElem it = new OTOpndArrayElem ();
1907 it.array = array;
1908 it.index = index;
1909 return it;
1910 }
1911
1912 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
1913 {
1914 if (SameAs (oldopnd)) {
1915 rc = true;
1916 return newopnd;
1917 }
1918 array = array.ReplaceOperand (oldopnd, newopnd, ref rc);
1919 index = index.ReplaceOperand (oldopnd, newopnd, ref rc);
1920 return this;
1921 }
1922
1923 public override bool SameAs (OTOpnd other)
1924 {
1925 if (!(other is OTOpndArrayElem)) return false;
1926 OTOpndArrayElem otherae = (OTOpndArrayElem) other;
1927 return array.SameAs (otherae.array) && index.SameAs (otherae.index);
1928 }
1929
1930 public override string PrintableString {
1931 get {
1932 return (byref ? "ref " : "") + array.PrintableString + "[" + index.PrintableString + "]";
1933 }
1934 }
1935
1936 /**
1937 * See if the argument is a reference to arg$0 or __xmrinst
1938 */
1939 public static bool IsArg0OrXMRInst (OTOpnd obj)
1940 {
1941 if (obj is OTOpndArg) {
1942 OTOpndArg objarg = (OTOpndArg) obj;
1943 return objarg.index == 0;
1944 }
1945 if (obj is OTOpndLocal) {
1946 OTOpndLocal objlcl = (OTOpndLocal) obj;
1947 return objlcl.local.name.StartsWith (_xmrinstlocal);
1948 }
1949 return false;
1950 }
1951 }
1952
1953 /**
1954 * Binary operator.
1955 */
1956 private class OTOpndBinOp : OTOpnd {
1957 public OTOpnd left;
1958 public MyOp opCode;
1959 public OTOpnd rite;
1960
1961 private static Dictionary<string,string> xor1ops = InitXor1Ops ();
1962
1963 private static Dictionary<string,string> InitXor1Ops ()
1964 {
1965 Dictionary<string,string> d = new Dictionary<string,string> ();
1966 d["ceq"] = "cne";
1967 d["cge"] = "clt";
1968 d["cgt"] = "cle";
1969 d["cle"] = "cgt";
1970 d["clt"] = "cge";
1971 d["cne"] = "ceq";
1972 return d;
1973 }
1974
1975 public static OTOpnd Make (OTOpnd left, MyOp opCode, OTOpnd rite)
1976 {
1977 // ((x clt y) xor 1) => (x cge y) etc
1978 string xor1op;
1979 if ((left is OTOpndBinOp) && xor1ops.TryGetValue (((OTOpndBinOp) left).opCode.name, out xor1op) &&
1980 (opCode == MyOp.Xor) &&
1981 (rite is OTOpndInt) && (((OTOpndInt) rite).value == 1)) {
1982 opCode = MyOp.GetByName (xor1op);
1983 }
1984
1985 // handle strcmp() cases (see OTOpndStrCmp)
1986 if (left is OTOpndStrCmp) {
1987 OTOpnd strcmp = ((OTOpndStrCmp) left).MakeBinOp (opCode, rite);
1988 if (strcmp != null) return strcmp;
1989 }
1990
1991 // nothing special, make as is
1992 OTOpndBinOp it = new OTOpndBinOp ();
1993 it.left = left;
1994 it.opCode = opCode;
1995 it.rite = rite;
1996 return it;
1997 }
1998
1999 private OTOpndBinOp () { }
2000
2001 public override bool HasSideEffects {
2002 get {
2003 return left.HasSideEffects || rite.HasSideEffects;
2004 }
2005 }
2006
2007 public override void CountRefs (bool writing)
2008 {
2009 left.CountRefs (false);
2010 rite.CountRefs (false);
2011 }
2012
2013 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
2014 {
2015 if (SameAs (oldopnd)) {
2016 rc = true;
2017 return newopnd;
2018 }
2019 left = left.ReplaceOperand (oldopnd, newopnd, ref rc);
2020 rite = rite.ReplaceOperand (oldopnd, newopnd, ref rc);
2021 return this;
2022 }
2023
2024 public override bool SameAs (OTOpnd other)
2025 {
2026 if (!(other is OTOpndBinOp)) return false;
2027 OTOpndBinOp otherbo = (OTOpndBinOp) other;
2028 return left.SameAs (otherbo.left) && (opCode.ToString () == otherbo.opCode.ToString ()) && rite.SameAs (otherbo.rite);
2029 }
2030
2031 public override string PrintableString {
2032 get {
2033 StringBuilder sb = new StringBuilder ();
2034
2035 bool leftneedsparen = ItNeedsParentheses (left, true);
2036 if (leftneedsparen) sb.Append ('(');
2037 sb.Append (left.PrintableString);
2038 if (leftneedsparen) sb.Append (')');
2039
2040 sb.Append (' ');
2041 sb.Append (opCode.source);
2042 sb.Append (' ');
2043
2044 bool riteneedsparen = ItNeedsParentheses (rite, false);
2045 if (riteneedsparen) sb.Append ('(');
2046 sb.Append (rite.PrintableString);
2047 if (riteneedsparen) sb.Append (')');
2048
2049 return sb.ToString ();
2050 }
2051 }
2052
2053 /**
2054 * See if source code representation requires parentheses around the given operand.
2055 * @param it = the other operand to decide about
2056 * @param itleft = true: 'it' is on the left of this operand (A $ B) # C
2057 * false: 'it' is on the right of this operand A $ (B # C)
2058 */
2059 private bool ItNeedsParentheses (OTOpnd it, bool itleft)
2060 {
2061 if (!(it is OTOpndBinOp)) return false;
2062 string itop = ((OTOpndBinOp) it).opCode.source;
2063 string myop = opCode.source;
2064
2065 // find them in table. higher number is for *, lower is for +.
2066 int itpi, mypi;
2067 if (!precedence.TryGetValue (itop, out itpi)) return true;
2068 if (!precedence.TryGetValue (myop, out mypi)) return true;
2069 int itpiabs = Math.Abs (itpi);
2070 int mypiabs = Math.Abs (mypi);
2071
2072 // if its precedence is lower (eg +) than my precedence (eg *), it needs parentheses
2073 if (itpiabs < mypiabs) return true;
2074
2075 // if its precedence is higher (eg *) than my precedence (eg +), it doesn't needs parentheses
2076 if (itpiabs > mypiabs) return false;
2077
2078 // if (A $ B) # C, we can safely go without the parentheses
2079 if (itleft) return false;
2080
2081 // my it
2082 // A $ (B # C) only works without parentheses for commutative $
2083 // A - (B + C) and A - (B - C) require parentheses
2084 // A + (B - C) does not
2085 return mypi < 0; // neg: things like -, /, etc require parentheses
2086 // pos: things like +, *, etc do not need parens
2087 }
2088
2089 // see MMRScriptReduce.PrecedenceInit()
2090 private static Dictionary<string,int> precedence = InitPrecedence ();
2091 private static Dictionary<string,int> InitPrecedence ()
2092 {
2093 Dictionary<string,int> d = new Dictionary<string,int> ();
2094 d["|"] = 140;
2095 d["^"] = 160;
2096 d["&"] = 180;
2097 d["<<"] = -260;
2098 d[">>"] = -260;
2099 d["+"] = 280;
2100 d["-"] = -280;
2101 d["*"] = 320;
2102 d["/"] = -320;
2103 d["%"] = -320;
2104 return d;
2105 }
2106 }
2107
2108 /**
2109 * Call with or without return value.
2110 */
2111 private class OTOpndCall : OTOpnd {
2112 private static Dictionary<string,MethodInfo> mathmeths = InitMathMeths ();
2113 private static Dictionary<string,MethodInfo> InitMathMeths ()
2114 {
2115 Dictionary<string,MethodInfo> d = new Dictionary<string,MethodInfo> ();
2116 d["Acos"] = typeof (ScriptBaseClass).GetMethod ("llAcos");
2117 d["Asin"] = typeof (ScriptBaseClass).GetMethod ("llAsin");
2118 d["Atan"] = typeof (ScriptBaseClass).GetMethod ("llAtan");
2119 d["Cos"] = typeof (ScriptBaseClass).GetMethod ("llCos");
2120 d["Abs"] = typeof (ScriptBaseClass).GetMethod ("llFabs");
2121 d["Log"] = typeof (ScriptBaseClass).GetMethod ("llLog");
2122 d["Log10"] = typeof (ScriptBaseClass).GetMethod ("llLog10");
2123 d["Round"] = typeof (ScriptBaseClass).GetMethod ("llRound");
2124 d["Sin"] = typeof (ScriptBaseClass).GetMethod ("llSin");
2125 d["Sqrt"] = typeof (ScriptBaseClass).GetMethod ("llSqrt");
2126 d["Tan"] = typeof (ScriptBaseClass).GetMethod ("llTan");
2127 return d;
2128 }
2129
2130 public MethodInfo method;
2131 public OTOpnd[] args;
2132
2133 // pushes on stack for return-value functions
2134 // pushes to end of instruction stream for return-void functions
2135 public static void AddLast (OTDecompile decompile, MethodInfo method, OTOpnd[] args)
2136 {
2137 int nargs = args.Length;
2138
2139 // heap tracker push is just the single arg value as far as we're concerned
2140 if ((nargs == 1) && (method.Name == _heapTrackerPush) && method.DeclaringType.Name.StartsWith ("HeapTracker")) {
2141 decompile.opstack.Push (args[0]);
2142 return;
2143 }
2144
2145 // heap tracker pop is just a store as far as we're concerned
2146 if ((nargs == 2) && (method.Name == _heapTrackerPop) && method.DeclaringType.Name.StartsWith ("HeapTracker")) {
2147 OTStmtStore.AddLast (decompile, args[0], args[1]);
2148 return;
2149 }
2150
2151 // string.Compare() is its own thing cuz it has to decompile many ways
2152 if ((nargs == 2) && (method.DeclaringType == typeof (string)) && (method.Name == "Compare")) {
2153 decompile.opstack.Push (new OTOpndStrCmp (args[0], args[1]));
2154 return;
2155 }
2156
2157 // ObjectToString, etc, should appear as casts
2158 if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToBool")) {
2159 MethodInfo meth = typeof (XMRInstAbstract).GetMethod ("xmr" + method.Name);
2160 AddLast (decompile, meth, new OTOpnd[] { new OTOpndNull (), args[0] });
2161 return;
2162 }
2163 if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToFloat")) {
2164 decompile.opstack.Push (new OTOpndCast (typeof (double), args[0]));
2165 return;
2166 }
2167 if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToInteger")) {
2168 decompile.opstack.Push (new OTOpndCast (typeof (int), args[0]));
2169 return;
2170 }
2171 if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToList")) {
2172 decompile.opstack.Push (new OTOpndCast (typeof (LSL_List), args[0]));
2173 return;
2174 }
2175 if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToRotation")) {
2176 decompile.opstack.Push (new OTOpndCast (typeof (LSL_Rotation), args[0]));
2177 return;
2178 }
2179 if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToString")) {
2180 decompile.opstack.Push (new OTOpndCast (typeof (string), args[0]));
2181 return;
2182 }
2183 if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToVector")) {
2184 decompile.opstack.Push (new OTOpndCast (typeof (LSL_Vector), args[0]));
2185 return;
2186 }
2187
2188 if ((method.DeclaringType == typeof (XMRInstAbstract)) && (method.Name == "xmrHeapLeft")) {
2189 AddLast (decompile, typeof (ScriptBaseClass).GetMethod ("llGetFreeMemory"), new OTOpnd[] { new OTOpndNull () });
2190 return;
2191 }
2192
2193 // pop to entry in the list/object/string array
2194 if (PopToGlobalArray (decompile, method, args)) return;
2195
2196 // strip off event handler argument unwrapper calls
2197 if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.StartsWith ("EHArgUnwrap")) {
2198 decompile.opstack.Push (args[0]);
2199 return;
2200 }
2201
2202 // translate Math method to ll method
2203 MethodInfo mathmeth;
2204 if ((method.DeclaringType == typeof (Math)) && mathmeths.TryGetValue (method.Name, out mathmeth)) {
2205 AddLast (decompile, mathmeth, new OTOpnd[] { new OTOpndNull (), args[0] });
2206 return;
2207 }
2208 if ((method.DeclaringType == typeof (Math)) && (method.Name == "Atan2")) {
2209 AddLast (decompile, typeof (ScriptBaseClass).GetMethod ("llAtan2"), new OTOpnd[] { new OTOpndNull (), args[0], args[1] });
2210 return;
2211 }
2212 if ((method.DeclaringType == typeof (Math)) && (method.Name == "Pow")) {
2213 AddLast (decompile, typeof (ScriptBaseClass).GetMethod ("llPow"), new OTOpnd[] { new OTOpndNull (), args[0], args[1] });
2214 return;
2215 }
2216
2217 // string concat should be a bunch of adds
2218 if ((method.Name == "Concat") && (method.DeclaringType == typeof (string))) {
2219 int k = args.Length;
2220 while (k > 1) {
2221 int j = 0;
2222 int i;
2223 for (i = 0; i + 2 <= k; i += 2) {
2224 args[j++] = OTOpndBinOp.Make (args[i+0], MyOp.Add, args[i+1]);
2225 }
2226 while (i < k) args[j++] = args[i++];
2227 k = j;
2228 }
2229 if (k > 0) decompile.opstack.Push (args[0]);
2230 return;
2231 }
2232
2233 // bunch of calls for rotation and vector arithmetic
2234 if ((method.DeclaringType == typeof (BinOpStr)) && BinOpStrCall (decompile, method, args)) return;
2235 if ((method.DeclaringType == typeof (ScriptCodeGen)) && (method.Name == "LSLRotationNegate")) {
2236 decompile.opstack.Push (OTOpndUnOp.Make (MyOp.Neg, args[0]));
2237 return;
2238 }
2239 if ((method.DeclaringType == typeof (ScriptCodeGen)) && (method.Name == "LSLVectorNegate")) {
2240 decompile.opstack.Push (OTOpndUnOp.Make (MyOp.Neg, args[0]));
2241 return;
2242 }
2243
2244 // otherwise process it as a call
2245 OTOpndCall call = new OTOpndCall ();
2246 call.method = method;
2247 call.args = args;
2248 if (method.ReturnType == typeof (void)) {
2249 OTStmtVoid.AddLast (decompile, call);
2250 } else {
2251 decompile.opstack.Push (call);
2252 }
2253 }
2254
2255 public override bool HasSideEffects {
2256 get {
2257 return true;
2258 }
2259 }
2260
2261 /**
2262 * Handle a call to XMRInstArrays.Pop<List,Object,String>
2263 * by converting it to a store directly into the array.
2264 */
2265 private static bool PopToGlobalArray (OTDecompile decompile, MethodInfo method, OTOpnd[] args)
2266 {
2267 if (method.DeclaringType != typeof (XMRInstArrays)) return false;
2268 if (args.Length != 3) return false;
2269
2270 string array = null;
2271 if (method.Name == "PopList") array = "iarLists";
2272 if (method.Name == "PopObject") array = "iarObjects";
2273 if (method.Name == "PopString") array = "iarStrings";
2274 if (array == null) return false;
2275
2276 // make token that points to the iar<whatever> array
2277 FieldInfo field = typeof (XMRInstArrays).GetField (array);
2278 OTOpnd arrayfield = OTOpndField.Make (args[0], field);
2279
2280 // make token that points to the element to be popped to
2281 OTOpnd element = OTOpndArrayElem.Make (arrayfield, args[1], false, decompile);
2282
2283 // make a statement to store value in that element
2284 OTStmtStore.AddLast (decompile, element, args[2]);
2285
2286 return true;
2287 }
2288
2289 /**
2290 * BinOpStr has a bunch of calls to do funky arithmetic.
2291 * Instead of generating a call, put back the original source.
2292 */
2293 private static bool BinOpStrCall (OTDecompile decompile, MethodInfo method, OTOpnd[] args)
2294 {
2295 switch (method.Name) {
2296 case "MethFloatAddList":
2297 case "MethIntAddList":
2298 case "MethKeyAddList":
2299 case "MethListAddFloat":
2300 case "MethListAddInt":
2301 case "MethListAddKey":
2302 case "MethListAddList":
2303 case "MethListAddObj":
2304 case "MethListAddRot":
2305 case "MethListAddStr":
2306 case "MethListAddVec":
2307 case "MethObjAddList":
2308 case "MethRotAddList":
2309 case "MethRotAddRot":
2310 case "MethStrAddList":
2311 case "MethVecAddList":
2312 case "MethVecAddVec": {
2313 decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Add, args[1]));
2314 return true;
2315 }
2316
2317 case "MethListEqList":
2318 case "MethRotEqRot":
2319 case "MethVecEqVec": {
2320 decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Ceq, args[1]));
2321 return true;
2322 }
2323
2324 case "MethListNeList":
2325 case "MethRotNeRot":
2326 case "MethVecNeVec": {
2327 decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Cne, args[1]));
2328 return true;
2329 }
2330
2331 case "MethRotSubRot":
2332 case "MethVecSubVec": {
2333 decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Sub, args[1]));
2334 return true;
2335 }
2336
2337 case "MethFloatMulVec":
2338 case "MethIntMulVec":
2339 case "MethRotMulRot":
2340 case "MethVecMulFloat":
2341 case "MethVecMulInt":
2342 case "MethVecMulRot":
2343 case "MethVecMulVec": {
2344 decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Mul, args[1]));
2345 return true;
2346 }
2347
2348 case "MethRotDivRot":
2349 case "MethVecDivFloat":
2350 case "MethVecDivInt":
2351 case "MethVecDivRot": {
2352 decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Div, args[1]));
2353 return true;
2354 }
2355
2356 default: return false;
2357 }
2358 }
2359
2360 private OTOpndCall () { }
2361
2362 public override void CountRefs (bool writing)
2363 {
2364 foreach (OTOpnd arg in args) {
2365 arg.CountRefs (false);
2366 }
2367 }
2368
2369 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
2370 {
2371 for (int i = 0; i < args.Length; i ++) {
2372 args[i] = args[i].ReplaceOperand (oldopnd, newopnd, ref rc);
2373 }
2374 return this;
2375 }
2376
2377 public override bool SameAs (OTOpnd other)
2378 {
2379 return false;
2380 }
2381
2382 public override string PrintableString {
2383 get {
2384 StringBuilder sb = new StringBuilder ();
2385
2386 // GetByKey(a,i) => a[i]
2387 if ((method.DeclaringType == typeof (XMR_Array)) && (method.Name == "GetByKey") && (args.Length == 2)) {
2388 sb.Append (args[0].PrintableString);
2389 sb.Append ('[');
2390 sb.Append (args[1].PrintableString);
2391 sb.Append (']');
2392 return sb.ToString ();
2393 }
2394
2395 // SetByKey(a,i,v) => a[i] = v
2396 if ((method.DeclaringType == typeof (XMR_Array)) && (method.Name == "SetByKey") && (args.Length == 3)) {
2397 sb.Append (args[0].PrintableString);
2398 sb.Append ('[');
2399 sb.Append (args[1].PrintableString);
2400 sb.Append ("] = ");
2401 sb.Append (args[2].PrintableString);
2402 return sb.ToString ();
2403 }
2404
2405 // CompValuListEl.GetElementFromList accesses list elements like an array.
2406 if ((method.DeclaringType == typeof (CompValuListEl)) && (method.Name == "GetElementFromList")) {
2407 sb.Append (args[0].PrintableString);
2408 sb.Append ('[');
2409 sb.Append (args[1].PrintableString);
2410 sb.Append (']');
2411 return sb.ToString ();
2412 }
2413
2414 // methods that are part of ScriptBaseClass are LSL functions such as llSay()
2415 // so we want to skip outputting "arg$0," as it is the hidden "this" argument.
2416 // and there are also XMRInstAbstract functions such as xmrEventDequeue().
2417 int starti = 0;
2418 if ((method.DeclaringType == typeof (ScriptBaseClass)) && !method.IsStatic) starti = 1;
2419 if ((method.DeclaringType == typeof (XMRInstAbstract)) && !method.IsStatic) starti = 1;
2420
2421 // likewise, method that have null as the declaring type are script-defined
2422 // dynamic methods which have a hidden "this" argument passed as "arg$0".
2423 if (method.DeclaringType == null) starti = 1;
2424
2425 // all others we want to show the type name (such as Math.Abs, String.Compare, etc)
2426 if (starti == 0) {
2427 sb.Append (AbbrType (method.DeclaringType));
2428 sb.Append ('.');
2429 }
2430
2431 // script-defined functions have the param types as part of their name
2432 // so strip them off here so they don't clutter things up
2433 int i = method.Name.IndexOf ('(');
2434 if (i < 0) sb.Append (method.Name);
2435 else sb.Append (method.Name.Substring (0, i));
2436
2437 // now add the call arguments
2438 sb.Append (" (");
2439 bool first = true;
2440 foreach (OTOpnd arg in args) {
2441 if (-- starti < 0) {
2442 if (!first) sb.Append (", ");
2443 sb.Append (arg.PrintableString);
2444 first = false;
2445 }
2446 }
2447 sb.Append (')');
2448 return sb.ToString ();
2449 }
2450 }
2451 }
2452
2453 /**
2454 * Cast value to the given type.
2455 */
2456 private class OTOpndCast : OTOpnd {
2457 public Type type;
2458 public OTOpnd value;
2459
2460 public OTOpndCast (Type type, OTOpnd value)
2461 {
2462 this.type = type;
2463 this.value = value;
2464 }
2465
2466 public override bool HasSideEffects {
2467 get {
2468 return value.HasSideEffects;
2469 }
2470 }
2471
2472 public override void CountRefs (bool writing)
2473 {
2474 value.CountRefs (false);
2475 }
2476
2477 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
2478 {
2479 if (SameAs (oldopnd)) {
2480 rc = true;
2481 return newopnd;
2482 }
2483 value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
2484 return this;
2485 }
2486
2487 public override bool SameAs (OTOpnd other)
2488 {
2489 if (!(other is OTOpndCast)) return false;
2490 OTOpndCast othercast = (OTOpndCast) other;
2491 return (type == othercast.type) && value.SameAs (othercast.value);
2492 }
2493
2494 public override string PrintableString {
2495 get {
2496 StringBuilder sb = new StringBuilder ();
2497 sb.Append ('(');
2498 sb.Append (AbbrType (type));
2499 sb.Append (") ");
2500 if (value is OTOpndBinOp) sb.Append ('(');
2501 sb.Append (value.PrintableString);
2502 if (value is OTOpndBinOp) sb.Append (')');
2503 return sb.ToString ();
2504 }
2505 }
2506 }
2507
2508 /**
2509 * Duplicate stack value without re-performing computation.
2510 * Semantics just like local var except it doesn't have a declaration.
2511 */
2512 private class OTOpndDup : OTOpnd {
2513 public int index;
2514 public int ndupreads;
2515
2516 public OTOpndDup (int index)
2517 {
2518 this.index = index;
2519 }
2520
2521 public override bool HasSideEffects {
2522 get {
2523 return false;
2524 }
2525 }
2526
2527 public override void CountRefs (bool writing)
2528 {
2529 if (!writing) ndupreads ++;
2530 }
2531
2532 public override bool SameAs (OTOpnd other)
2533 {
2534 if (!(other is OTOpndDup)) return false;
2535 return ((OTOpndDup) other).index == index;
2536 }
2537
2538 public override string PrintableString { get { return "dup$" + index; } }
2539 }
2540
2541 /**
2542 * Field of an object.
2543 */
2544 private class OTOpndField : OTOpnd {
2545 public OTOpnd obj;
2546 public FieldInfo field;
2547
2548 public static OTOpnd Make (OTOpnd obj, FieldInfo field)
2549 {
2550 // LSL_Float.value => the object itself
2551 if ((field.DeclaringType == typeof (LSL_Float)) && (field.Name == "value")) {
2552 return obj;
2553 }
2554
2555 // LSL_Integer.value => the object itself
2556 if ((field.DeclaringType == typeof (LSL_Integer)) && (field.Name == "value")) {
2557 return obj;
2558 }
2559
2560 // LSL_String.m_string => the object itself
2561 if ((field.DeclaringType == typeof (LSL_String)) && (field.Name == "m_string")) {
2562 return obj;
2563 }
2564
2565 // some other field, output code to access it
2566 // sometimes the object comes as by reference (value types), so we might need to deref it first
2567 OTOpndField it = new OTOpndField ();
2568 it.obj = obj.GetNonByRefOpnd ();
2569 it.field = field;
2570 return it;
2571 }
2572
2573 private OTOpndField () { }
2574
2575 public override bool HasSideEffects {
2576 get {
2577 return obj.HasSideEffects;
2578 }
2579 }
2580
2581 public override void CountRefs (bool writing)
2582 {
2583 // the field may be getting written to, but the object is being read
2584 obj.CountRefs (false);
2585 }
2586
2587 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
2588 {
2589 if (SameAs (oldopnd)) {
2590 rc = true;
2591 return newopnd;
2592 }
2593 obj = obj.ReplaceOperand (oldopnd, newopnd, ref rc);
2594 return this;
2595 }
2596
2597 public override bool SameAs (OTOpnd other)
2598 {
2599 if (!(other is OTOpndField)) return false;
2600 OTOpndField otherfield = (OTOpndField) other;
2601 return (field.Name == otherfield.field.Name) && obj.SameAs (otherfield.obj);
2602 }
2603
2604 public override string PrintableString {
2605 get {
2606 StringBuilder sb = new StringBuilder ();
2607 if (obj is OTOpndBinOp) sb.Append ('(');
2608 sb.Append (obj.PrintableString);
2609 if (obj is OTOpndBinOp) sb.Append (')');
2610 sb.Append ('.');
2611 sb.Append (field.Name);
2612 return sb.ToString ();
2613 }
2614 }
2615 }
2616
2617 /**
2618 * Script-level global variable.
2619 */
2620 private class OTOpndGlobal : OTOpnd {
2621 public string iartypename;
2622 public int iararrayidx;
2623 public bool byref;
2624 public ScriptObjCode scriptObjCode;
2625
2626 public OTOpndGlobal (string iartypename, int iararrayidx, bool byref, ScriptObjCode scriptObjCode)
2627 {
2628 this.iartypename = iartypename;
2629 this.iararrayidx = iararrayidx;
2630 this.byref = byref;
2631 this.scriptObjCode = scriptObjCode;
2632 }
2633
2634 public override bool HasSideEffects {
2635 get {
2636 return false;
2637 }
2638 }
2639
2640 public override OTOpnd GetNonByRefOpnd ()
2641 {
2642 if (!byref) return this;
2643 return new OTOpndGlobal (iartypename, iararrayidx, false, scriptObjCode);
2644 }
2645
2646 public override bool SameAs (OTOpnd other)
2647 {
2648 if (!(other is OTOpndGlobal)) return false;
2649 OTOpndGlobal otherglobal = (OTOpndGlobal) other;
2650 return (iartypename == otherglobal.iartypename) && (iararrayidx == otherglobal.iararrayidx);
2651 }
2652
2653 public override string PrintableString {
2654 get {
2655 return (byref ? "ref " : "") + scriptObjCode.globalVarNames[iartypename][iararrayidx];
2656 }
2657 }
2658 }
2659
2660 /**
2661 * List initialization.
2662 */
2663 private class OTOpndListIni : OTOpnd {
2664 public OTOpnd[] values;
2665
2666 /**
2667 * Try to detect list initialization building idiom:
2668 * dup$<n> = newarr object[<m>] << link points here
2669 * dup$<n>[0] = bla
2670 * dup$<n>[1] = bla
2671 * ...
2672 * ... newobj list (dup$<n>) ...
2673 */
2674 public static bool Detect (LinkedListNode<OTStmt> link)
2675 {
2676 if (link == null) return false;
2677
2678 /*
2679 * Check for 'dup$<n> = newarr object[<m>]' and get listsize from <m>.
2680 */
2681 OTStmtStore store = (OTStmtStore) link.Value;
2682 if (!(store.varwr is OTOpndDup)) return false;
2683 if (!(store.value is OTOpndNewarr)) return false;
2684 OTOpndDup storevar = (OTOpndDup) store.varwr;
2685 OTOpndNewarr storeval = (OTOpndNewarr) store.value;
2686 if (storeval.type != typeof (object)) return false;
2687 if (!(storeval.index is OTOpndInt)) return false;
2688 int listsize = ((OTOpndInt) storeval.index).value;
2689
2690 /*
2691 * Good chance of having list initializer, malloc an object to hold it.
2692 */
2693 OTOpndListIni it = new OTOpndListIni ();
2694 it.values = new OTOpnd[listsize];
2695
2696 /*
2697 * There should be exactly listsize statements following that of the form:
2698 * dup$<n>[<i>] = bla
2699 * If so, save the bla values in the values[] array.
2700 */
2701 LinkedListNode<OTStmt> vallink = link;
2702 for (int i = 0; i < listsize; i ++) {
2703 vallink = vallink.Next;
2704 if (vallink == null) return false;
2705 if (!(vallink.Value is OTStmtStore)) return false;
2706 OTStmtStore valstore = (OTStmtStore) vallink.Value;
2707 if (!(valstore.varwr is OTOpndArrayElem)) return false;
2708 OTOpndArrayElem varelem = (OTOpndArrayElem) valstore.varwr;
2709 if (varelem.array != storevar) return false;
2710 if (!(varelem.index is OTOpndInt)) return false;
2711 if (((OTOpndInt) varelem.index).value != i) return false;
2712 it.values[i] = valstore.value;
2713 }
2714
2715 /*
2716 * The next statement should have a 'newobj list (dup$<n>)' in it somewhere
2717 * that we want to replace with 'it'.
2718 */
2719 ConstructorInfo protoctor = typeof (LSL_List).GetConstructor (new Type[] { typeof (object[]) });
2720 OTOpnd[] protoargs = new OTOpnd[] { storevar };
2721 OTOpnd proto = OTOpndNewobj.Make (protoctor, protoargs);
2722
2723 vallink = vallink.Next;
2724 bool rc = vallink.Value.ReplaceOperand (proto, it);
2725
2726 /*
2727 * If successful, delete 'dup$n =' and all 'dup$n[i] =' statements.
2728 */
2729 if (rc) {
2730 do {
2731 LinkedListNode<OTStmt> nextlink = link.Next;
2732 link.List.Remove (link);
2733 link = nextlink;
2734 } while (link != vallink);
2735 }
2736
2737 return rc;
2738 }
2739
2740 public override bool HasSideEffects {
2741 get {
2742 foreach (OTOpnd value in values) {
2743 if (value.HasSideEffects) return true;
2744 }
2745 return false;
2746 }
2747 }
2748
2749 public override void CountRefs (bool writing)
2750 {
2751 foreach (OTOpnd value in values) {
2752 value.CountRefs (false);
2753 }
2754 }
2755
2756 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
2757 {
2758 if (SameAs (oldopnd)) {
2759 rc = true;
2760 return newopnd;
2761 }
2762 for (int i = 0; i < values.Length; i ++) {
2763 values[i] = values[i].ReplaceOperand (oldopnd, newopnd, ref rc);
2764 }
2765 return this;
2766 }
2767
2768 public override bool SameAs (OTOpnd other)
2769 {
2770 if (!(other is OTOpndListIni)) return false;
2771 OTOpndListIni otherli = (OTOpndListIni) other;
2772 if (otherli.values.Length != values.Length) return false;
2773 for (int i = 0; i < values.Length; i ++) {
2774 if (!values[i].SameAs (otherli.values[i])) return false;
2775 }
2776 return true;
2777 }
2778
2779 public override string PrintableString {
2780 get {
2781 StringBuilder sb = new StringBuilder ();
2782 sb.Append ('[');
2783 for (int i = 0; i < values.Length; i ++) {
2784 if (i > 0) sb.Append (',');
2785 sb.Append (' ');
2786 sb.Append (values[i].PrintableString);
2787 }
2788 sb.Append (" ]");
2789 return sb.ToString ();
2790 }
2791 }
2792 }
2793
2794 /**
2795 * Local variable.
2796 */
2797 private class OTOpndLocal : OTOpnd {
2798 public OTLocal local;
2799
2800 public OTOpndLocal (OTLocal local)
2801 {
2802 this.local = local;
2803 }
2804
2805 public override bool HasSideEffects {
2806 get {
2807 return false;
2808 }
2809 }
2810
2811 public override void CountRefs (bool writing)
2812 {
2813 if (writing) local.nlclwrites ++;
2814 else local.nlclreads ++;
2815 }
2816
2817 public override bool SameAs (OTOpnd other)
2818 {
2819 if (!(other is OTOpndLocal)) return false;
2820 OTOpndLocal otherlocal = (OTOpndLocal) other;
2821 return local == otherlocal.local;
2822 }
2823
2824 public override string PrintableString {
2825 get {
2826 return local.name;
2827 }
2828 }
2829 }
2830 private class OTOpndLocalRef : OTOpnd {
2831 public OTLocal local;
2832
2833 public OTOpndLocalRef (OTLocal local)
2834 {
2835 this.local = local;
2836 }
2837
2838 public override bool HasSideEffects {
2839 get {
2840 return true;
2841 }
2842 }
2843
2844 public override void CountRefs (bool writing)
2845 {
2846 local.nlclreads ++;
2847 local.nlclwrites ++;
2848 }
2849
2850 public override OTOpnd GetNonByRefOpnd ()
2851 {
2852 return new OTOpndLocal (local);
2853 }
2854
2855 public override bool SameAs (OTOpnd other)
2856 {
2857 if (!(other is OTOpndLocal)) return false;
2858 OTOpndLocal otherlocal = (OTOpndLocal) other;
2859 return local == otherlocal.local;
2860 }
2861
2862 public override string PrintableString { get { return "ref " + local.name; } }
2863 }
2864
2865 /**
2866 * New C#-level array.
2867 */
2868 private class OTOpndNewarr : OTOpnd {
2869 public Type type;
2870 public OTOpnd index;
2871
2872 public OTOpndNewarr (Type type, OTOpnd index)
2873 {
2874 this.type = type;
2875 this.index = index;
2876 }
2877
2878 public override bool HasSideEffects {
2879 get {
2880 return index.HasSideEffects;
2881 }
2882 }
2883
2884 public override void CountRefs (bool writing)
2885 {
2886 index.CountRefs (false);
2887 }
2888
2889 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
2890 {
2891 if (SameAs (oldopnd)) {
2892 rc = true;
2893 return newopnd;
2894 }
2895 index = index.ReplaceOperand (oldopnd, newopnd, ref rc);
2896 return this;
2897 }
2898
2899 public override bool SameAs (OTOpnd other)
2900 {
2901 return false;
2902 }
2903
2904 public override string PrintableString { get { return "newarr " + type.Name + "[" + index.PrintableString + "]"; } }
2905 }
2906
2907 /**
2908 * New C#-level object.
2909 */
2910 private class OTOpndNewobj : OTOpnd {
2911 public ConstructorInfo ctor;
2912 public OTOpnd[] args;
2913
2914 public static OTOpnd Make (ConstructorInfo ctor, OTOpnd[] args)
2915 {
2916 // newobj LSL_Float (x) => x
2917 if ((ctor.DeclaringType == typeof (LSL_Float)) && (args.Length == 1)) {
2918 Type ptype = ctor.GetParameters ()[0].ParameterType;
2919 if (ptype == typeof (string)) {
2920 return new OTOpndCast (typeof (double), args[0]);
2921 }
2922 return args[0];
2923 }
2924
2925 // newobj LSL_Integer (x) => x
2926 if ((ctor.DeclaringType == typeof (LSL_Integer)) && (args.Length == 1)) {
2927 Type ptype = ctor.GetParameters ()[0].ParameterType;
2928 if (ptype == typeof (string)) {
2929 return new OTOpndCast (typeof (int), args[0]);
2930 }
2931 return args[0];
2932 }
2933
2934 // newobj LSL_String (x) => x
2935 if ((ctor.DeclaringType == typeof (LSL_String)) && (args.Length == 1)) {
2936 return args[0];
2937 }
2938
2939 // newobj LSL_Rotation (x, y, z, w) => <x, y, z, w>
2940 if ((ctor.DeclaringType == typeof (LSL_Rotation)) && (args.Length == 4)) {
2941 return new OTOpndRot (args[0], args[1], args[2], args[3]);
2942 }
2943
2944 // newobj LSL_Vector (x, y, z) => <x, y, z>
2945 if ((ctor.DeclaringType == typeof (LSL_Vector)) && (args.Length == 3)) {
2946 return new OTOpndVec (args[0], args[1], args[2]);
2947 }
2948
2949 // newobj LSL_Rotation (string) => (rotation) string
2950 if ((ctor.DeclaringType == typeof (LSL_Rotation)) && (args.Length == 1)) {
2951 return new OTOpndCast (typeof (LSL_Rotation), args[0]);
2952 }
2953
2954 // newobj LSL_Vector (string) => (rotation) string
2955 if ((ctor.DeclaringType == typeof (LSL_Vector)) && (args.Length == 1)) {
2956 return new OTOpndCast (typeof (LSL_Vector), args[0]);
2957 }
2958
2959 // newobj LSL_List (newarr object[0]) => [ ]
2960 if ((ctor.DeclaringType == typeof (LSL_List)) && (args.Length == 1) && (args[0] is OTOpndNewarr)) {
2961 OTOpndNewarr arg0 = (OTOpndNewarr) args[0];
2962 if ((arg0.type == typeof (object)) && (arg0.index is OTOpndInt) && (((OTOpndInt) arg0.index).value == 0)) {
2963 OTOpndListIni listini = new OTOpndListIni ();
2964 listini.values = new OTOpnd[0];
2965 return listini;
2966 }
2967 }
2968
2969 // something else, output as is
2970 OTOpndNewobj it = new OTOpndNewobj ();
2971 it.ctor = ctor;
2972 it.args = args;
2973 return it;
2974 }
2975
2976 private OTOpndNewobj () { }
2977
2978 public override bool HasSideEffects {
2979 get {
2980 foreach (OTOpnd arg in args) {
2981 if (arg.HasSideEffects) return true;
2982 }
2983 return false;
2984 }
2985 }
2986
2987 public override void CountRefs (bool writing)
2988 {
2989 foreach (OTOpnd arg in args) {
2990 arg.CountRefs (false);
2991 }
2992 }
2993
2994 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
2995 {
2996 if (SameAs (oldopnd)) {
2997 rc = true;
2998 return newopnd;
2999 }
3000 for (int i = 0; i < args.Length; i ++) {
3001 args[i] = args[i].ReplaceOperand (oldopnd, newopnd, ref rc);
3002 }
3003 return this;
3004 }
3005
3006 public override bool SameAs (OTOpnd other)
3007 {
3008 if (!(other is OTOpndNewobj)) return false;
3009 OTOpndNewobj otherno = (OTOpndNewobj) other;
3010 if (otherno.ctor.DeclaringType != ctor.DeclaringType) return false;
3011 if (otherno.args.Length != args.Length) return false;
3012 for (int i = 0; i < args.Length; i ++) {
3013 if (!args[i].SameAs (otherno.args[i])) return false;
3014 }
3015 return true;
3016 }
3017
3018 public override string PrintableString {
3019 get {
3020 StringBuilder sb = new StringBuilder ();
3021 sb.Append ("newobj ");
3022 sb.Append (ctor.DeclaringType.Name);
3023 sb.Append (" (");
3024 bool first = true;
3025 foreach (OTOpnd arg in args) {
3026 if (!first) sb.Append (", ");
3027 sb.Append (arg.PrintableString);
3028 first = false;
3029 }
3030 sb.Append (')');
3031 return sb.ToString ();
3032 }
3033 }
3034 }
3035
3036 /**
3037 * Rotation value.
3038 */
3039 private class OTOpndRot : OTOpnd {
3040 private OTOpnd x, y, z, w;
3041
3042 public OTOpndRot (OTOpnd x, OTOpnd y, OTOpnd z, OTOpnd w)
3043 {
3044 this.x = StripFloatCast (x);
3045 this.y = StripFloatCast (y);
3046 this.z = StripFloatCast (z);
3047 this.w = StripFloatCast (w);
3048 }
3049
3050 public override bool HasSideEffects {
3051 get {
3052 return x.HasSideEffects || y.HasSideEffects || z.HasSideEffects || w.HasSideEffects;
3053 }
3054 }
3055
3056 public override void CountRefs (bool writing)
3057 {
3058 x.CountRefs (false);
3059 y.CountRefs (false);
3060 z.CountRefs (false);
3061 w.CountRefs (false);
3062 }
3063
3064 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
3065 {
3066 if (SameAs (oldopnd)) {
3067 rc = true;
3068 return newopnd;
3069 }
3070 x = x.ReplaceOperand (oldopnd, newopnd, ref rc);
3071 y = y.ReplaceOperand (oldopnd, newopnd, ref rc);
3072 z = z.ReplaceOperand (oldopnd, newopnd, ref rc);
3073 w = w.ReplaceOperand (oldopnd, newopnd, ref rc);
3074 return this;
3075 }
3076
3077 public override bool SameAs (OTOpnd other)
3078 {
3079 if (!(other is OTOpndRot)) return false;
3080 OTOpndRot otherv = (OTOpndRot) other;
3081 return otherv.x.SameAs (x) && otherv.y.SameAs (y) && otherv.z.SameAs (z) && otherv.w.SameAs (w);
3082 }
3083
3084 public override string PrintableString {
3085 get {
3086 return "<" + x.PrintableString + ", " + y.PrintableString + ", " + z.PrintableString + ", " + w.PrintableString + ">";
3087 }
3088 }
3089 }
3090
3091 /**
3092 * Static field.
3093 */
3094 private class OTOpndSField : OTOpnd {
3095 private FieldInfo field;
3096
3097 public OTOpndSField (FieldInfo field)
3098 {
3099 this.field = field;
3100 }
3101
3102 public override bool HasSideEffects {
3103 get {
3104 return false;
3105 }
3106 }
3107
3108 public override bool SameAs (OTOpnd other)
3109 {
3110 if (!(other is OTOpndSField)) return false;
3111 OTOpndSField othersfield = (OTOpndSField) other;
3112 return (field.Name == othersfield.field.Name) && (field.DeclaringType == othersfield.field.DeclaringType);
3113 }
3114
3115 public override string PrintableString {
3116 get {
3117 if (field.DeclaringType == typeof (ScriptBaseClass)) return field.Name;
3118 return field.DeclaringType.Name + "." + field.Name;
3119 }
3120 }
3121 }
3122
3123 /**
3124 * Call to string.Compare().
3125 * See use cases in BinOpStr:
3126 * strcmp (a, b) ceq 0
3127 * (strcmp (a, b) ceq 0) xor 1 => we translate to: strcmp (a, b) cne 0
3128 * strcmp (a, b) clt 0
3129 * strcmp (a, b) clt 1 // <=
3130 * strcmp (a, b) cgt 0
3131 * strcmp (a, b) cgt -1 // >=
3132 * ...but then optimized by ScriptCollector if followed by br{false,true}:
3133 * ceq + xor 1 + brtrue => bne.un
3134 * ceq + xor 1 + brfalse => beq
3135 * ceq + brtrue => beq
3136 * ceq + brfalse => bne.un
3137 * cgt + brtrue => bgt
3138 * cgt + brfalse => ble
3139 * clt + brtrue => blt
3140 * clt + brfalse => bge
3141 * So we end up with these cases:
3142 * strcmp (a, b) ceq 0
3143 * strcmp (a, b) cne 0
3144 * strcmp (a, b) clt 0
3145 * strcmp (a, b) clt 1
3146 * strcmp (a, b) cgt 0
3147 * strcmp (a, b) cgt -1
3148 * strcmp (a, b) beq 0
3149 * strcmp (a, b) bne.un 0
3150 * strcmp (a, b) bgt 0
3151 * strcmp (a, b) ble 0
3152 * strcmp (a, b) bgt -1
3153 * strcmp (a, b) ble -1
3154 * strcmp (a, b) blt 0
3155 * strcmp (a, b) bge 0
3156 * strcmp (a, b) blt 1
3157 * strcmp (a, b) bge 1
3158 * ... so we pretty them up in OTOpndBinOp
3159 */
3160 private class OTOpndStrCmp : OTOpnd {
3161 private static Dictionary<string,string> binops = InitBinops ();
3162 private static Dictionary<string,string> InitBinops ()
3163 {
3164 Dictionary<string,string> d = new Dictionary<string,string> ();
3165 d["ceq 0"] = "ceq";
3166 d["cne 0"] = "cne";
3167 d["clt 0"] = "clt";
3168 d["clt 1"] = "cle";
3169 d["cgt 0"] = "cgt";
3170 d["cgt -1"] = "cge";
3171 d["beq 0"] = "ceq";
3172 d["bne.un 0"] = "cne";
3173 d["bgt 0"] = "cgt";
3174 d["ble 0"] = "cle";
3175 d["bgt -1"] = "cge";
3176 d["ble -1"] = "clt";
3177 d["blt 0"] = "clt";
3178 d["bge 0"] = "cge";
3179 d["blt 1"] = "cle";
3180 d["bge 1"] = "cgt";
3181 return d;
3182 }
3183
3184 private OTOpnd arg0;
3185 private OTOpnd arg1;
3186
3187 public OTOpndStrCmp (OTOpnd arg0, OTOpnd arg1)
3188 {
3189 this.arg0 = arg0;
3190 this.arg1 = arg1;
3191 }
3192
3193 /**
3194 * Try to make something a script writer would recognize.
3195 * If we can't, then we leave it as a call to xmrStringCompare().
3196 * this = some strcmp(a,b)
3197 * opCode = hopefully some cxx or bxx from above table
3198 * rite = hopefully some constant from above table
3199 */
3200 public OTOpnd MakeBinOp (MyOp opCode, OTOpnd rite)
3201 {
3202 if (!(rite is OTOpndInt)) return null;
3203 int riteint = ((OTOpndInt) rite).value;
3204 string key = opCode.name + ' ' + riteint;
3205 string cxxopname;
3206 if (!binops.TryGetValue (key, out cxxopname)) return null;
3207 return OTOpndBinOp.Make (arg0, MyOp.GetByName (cxxopname), arg1);
3208 }
3209 public OTOpnd MakeUnOp (MyOp opCode)
3210 {
3211 if (opCode == MyOp.Brfalse) return OTOpndBinOp.Make (arg0, MyOp.Ceq, arg1);
3212 if (opCode == MyOp.Brtrue) return OTOpndBinOp.Make (arg0, MyOp.Cne, arg1);
3213 return null;
3214 }
3215
3216 public override bool HasSideEffects {
3217 get {
3218 return false;
3219 }
3220 }
3221
3222 public override void CountRefs (bool writing)
3223 {
3224 arg0.CountRefs (writing);
3225 arg1.CountRefs (writing);
3226 }
3227
3228 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
3229 {
3230 if (SameAs (oldopnd)) {
3231 rc = true;
3232 return newopnd;
3233 }
3234 arg0 = arg0.ReplaceOperand (oldopnd, newopnd, ref rc);
3235 arg1 = arg1.ReplaceOperand (oldopnd, newopnd, ref rc);
3236 return this;
3237 }
3238
3239 public override bool SameAs (OTOpnd other)
3240 {
3241 if (!(other is OTOpndStrCmp)) return false;
3242 return arg0.SameAs (((OTOpndStrCmp) other).arg0) && arg1.SameAs (((OTOpndStrCmp) other).arg1);
3243 }
3244
3245 public override string PrintableString {
3246 get {
3247 return "xmrStringCompare (" + arg0.PrintableString + ", " + arg1.PrintableString + ")";
3248 }
3249 }
3250 }
3251
3252 /**
3253 * Unary operator.
3254 */
3255 private class OTOpndUnOp : OTOpnd {
3256 public MyOp opCode;
3257 public OTOpnd value;
3258
3259 private static Dictionary<string,string> brfops = InitBrfOps ();
3260 private static Dictionary<string,string> InitBrfOps ()
3261 {
3262 Dictionary<string,string> d = new Dictionary<string,string> ();
3263 d["beq"] = "cne";
3264 d["bge"] = "clt";
3265 d["bgt"] = "cle";
3266 d["ble"] = "cgt";
3267 d["blt"] = "cge";
3268 d["bne.un"] = "ceq";
3269 d["ceq"] = "cne";
3270 d["cge"] = "clt";
3271 d["cgt"] = "cle";
3272 d["cle"] = "cgt";
3273 d["clt"] = "cge";
3274 d["cne"] = "ceq";
3275 return d;
3276 }
3277
3278 public static OTOpnd Make (MyOp opCode, OTOpnd value)
3279 {
3280 // (brfalse (brfalse (x))) => (brtrue (x))
3281 if ((opCode == MyOp.Brfalse) && (value is OTOpndUnOp) && (((OTOpndUnOp) value).opCode == MyOp.Brfalse)) {
3282 ((OTOpndUnOp) value).opCode = MyOp.Brtrue;
3283 return value;
3284 }
3285
3286 // (brfalse (brtrue (x))) => (brfalse (x))
3287 if ((opCode == MyOp.Brfalse) && (value is OTOpndUnOp) && (((OTOpndUnOp) value).opCode == MyOp.Brtrue)) {
3288 ((OTOpndUnOp) value).opCode = MyOp.Brfalse;
3289 return value;
3290 }
3291
3292 // (brtrue (brfalse (x))) => (brfalse (x))
3293 if ((opCode == MyOp.Brtrue) && (value is OTOpndUnOp) && (((OTOpndUnOp) value).opCode == MyOp.Brfalse)) {
3294 return value;
3295 }
3296
3297 // (brtrue (brtrue (x))) => (brtrue (x))
3298 if ((opCode == MyOp.Brtrue) && (value is OTOpndUnOp) && (((OTOpndUnOp) value).opCode == MyOp.Brtrue)) {
3299 return value;
3300 }
3301
3302 // (brfalse (x beq y)) => (x bne y) etc
3303 string brfop;
3304 if ((opCode == MyOp.Brfalse) && (value is OTOpndBinOp) && brfops.TryGetValue (((OTOpndBinOp) value).opCode.name, out brfop)) {
3305 ((OTOpndBinOp) value).opCode = MyOp.GetByName (brfop);
3306 return value;
3307 }
3308
3309 // (brtrue (x beq y)) => (x beq y) etc
3310 if ((opCode == MyOp.Brtrue) && (value is OTOpndBinOp) && brfops.ContainsKey (((OTOpndBinOp) value).opCode.name)) {
3311 return value;
3312 }
3313
3314 // strcmp() can be a special case
3315 if (value is OTOpndStrCmp) {
3316 OTOpnd strcmp = ((OTOpndStrCmp) value).MakeUnOp (opCode);
3317 if (strcmp != null) return strcmp;
3318 }
3319
3320 // nothing special, save opcode and value
3321 OTOpndUnOp it = new OTOpndUnOp ();
3322 it.opCode = opCode;
3323 it.value = value;
3324 return it;
3325 }
3326
3327 private OTOpndUnOp () { }
3328
3329 public override bool HasSideEffects {
3330 get {
3331 return value.HasSideEffects;
3332 }
3333 }
3334
3335 public override void CountRefs (bool writing)
3336 {
3337 value.CountRefs (false);
3338 }
3339
3340 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
3341 {
3342 if (SameAs (oldopnd)) {
3343 rc = true;
3344 return newopnd;
3345 }
3346 value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
3347 return this;
3348 }
3349
3350 public override bool SameAs (OTOpnd other)
3351 {
3352 if (!(other is OTOpndUnOp)) return false;
3353 OTOpndUnOp otherop = (OTOpndUnOp) other;
3354 return (opCode.ToString () == otherop.opCode.ToString ()) && value.SameAs (otherop.value);
3355 }
3356
3357 public override string PrintableString {
3358 get {
3359 StringBuilder sb = new StringBuilder ();
3360 sb.Append (opCode.source);
3361 sb.Append (' ');
3362 if (value is OTOpndBinOp) sb.Append ('(');
3363 sb.Append (value.PrintableString);
3364 if (value is OTOpndBinOp) sb.Append (')');
3365 return sb.ToString ();
3366 }
3367 }
3368 }
3369
3370 /**
3371 * Vector value.
3372 */
3373 private class OTOpndVec : OTOpnd {
3374 private OTOpnd x, y, z;
3375
3376 public OTOpndVec (OTOpnd x, OTOpnd y, OTOpnd z)
3377 {
3378 this.x = StripFloatCast (x);
3379 this.y = StripFloatCast (y);
3380 this.z = StripFloatCast (z);
3381 }
3382
3383 public override bool HasSideEffects {
3384 get {
3385 return x.HasSideEffects || y.HasSideEffects || z.HasSideEffects;
3386 }
3387 }
3388
3389 public override void CountRefs (bool writing)
3390 {
3391 x.CountRefs (false);
3392 y.CountRefs (false);
3393 z.CountRefs (false);
3394 }
3395
3396 public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
3397 {
3398 if (SameAs (oldopnd)) {
3399 rc = true;
3400 return newopnd;
3401 }
3402 x = x.ReplaceOperand (oldopnd, newopnd, ref rc);
3403 y = y.ReplaceOperand (oldopnd, newopnd, ref rc);
3404 z = z.ReplaceOperand (oldopnd, newopnd, ref rc);
3405 return this;
3406 }
3407
3408 public override bool SameAs (OTOpnd other)
3409 {
3410 if (!(other is OTOpndVec)) return false;
3411 OTOpndVec otherv = (OTOpndVec) other;
3412 return otherv.x.SameAs (x) && otherv.y.SameAs (y) && otherv.z.SameAs (z);
3413 }
3414
3415 public override string PrintableString {
3416 get {
3417 return "<" + x.PrintableString + ", " + y.PrintableString + ", " + z.PrintableString + ">";
3418 }
3419 }
3420 }
3421
3422 /**
3423 * Constants.
3424 */
3425 private class OTOpndDouble : OTOpnd {
3426 public double value;
3427 public OTOpndDouble (double value) { this.value = value; }
3428 public override bool HasSideEffects { get { return false; } }
3429 public override bool SameAs (OTOpnd other)
3430 {
3431 if (!(other is OTOpndDouble)) return false;
3432 return ((OTOpndDouble) other).value == value;
3433 }
3434 public override string PrintableString {
3435 get {
3436 string s = value.ToString ();
3437 long i;
3438 if (long.TryParse (s, out i)) {
3439 s += ".0";
3440 }
3441 return s;
3442 }
3443 }
3444 }
3445 private class OTOpndFloat : OTOpnd {
3446 public float value;
3447 public OTOpndFloat (float value) { this.value = value; }
3448 public override bool HasSideEffects { get { return false; } }
3449 public override bool SameAs (OTOpnd other)
3450 {
3451 if (!(other is OTOpndFloat)) return false;
3452 return ((OTOpndFloat) other).value == value;
3453 }
3454 public override string PrintableString {
3455 get {
3456 string s = value.ToString ();
3457 long i;
3458 if (long.TryParse (s, out i)) {
3459 s += ".0";
3460 }
3461 return s;
3462 }
3463 }
3464 }
3465 private class OTOpndInt : OTOpnd {
3466 public int value;
3467 public OTOpndInt (int value) { this.value = value; }
3468 public override bool HasSideEffects { get { return false; } }
3469 public override bool SameAs (OTOpnd other)
3470 {
3471 if (!(other is OTOpndInt)) return false;
3472 return ((OTOpndInt) other).value == value;
3473 }
3474 public override string PrintableString { get { return value.ToString (); } }
3475 }
3476 private class OTOpndNull : OTOpnd {
3477 public override bool HasSideEffects { get { return false; } }
3478 public override bool SameAs (OTOpnd other)
3479 {
3480 return other is OTOpndNull;
3481 }
3482 public override string PrintableString { get { return "undef"; } }
3483 }
3484 private class OTOpndString : OTOpnd {
3485 public string value;
3486 public OTOpndString (string value) { this.value = value; }
3487 public override bool HasSideEffects { get { return false; } }
3488 public override bool SameAs (OTOpnd other)
3489 {
3490 if (!(other is OTOpndString)) return false;
3491 return ((OTOpndString) other).value == value;
3492 }
3493 public override string PrintableString {
3494 get {
3495 StringBuilder sb = new StringBuilder ();
3496 TokenDeclInline.PrintParamString (sb, value);
3497 return sb.ToString ();
3498 }
3499 }
3500 }
3501
3502 /****************************************\
3503 * Tokens what are in statement list. *
3504 \****************************************/
3505
3506 public abstract class OTStmt {
3507
3508 /**
3509 * Increment reference counts.
3510 */
3511 public abstract void CountRefs ();
3512
3513 /**
3514 * Strip out any of the behind-the-scenes code such as stack capture/restore.
3515 * By default, there is no change.
3516 */
3517 public virtual bool StripStuff (LinkedListNode<OTStmt> link)
3518 {
3519 return false;
3520 }
3521
3522 /**
3523 * Replace the oldopnd operand with the newopnd operand if it is present.
3524 * Return whether or not it was found and replaced.
3525 */
3526 public abstract bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd);
3527
3528 /**
3529 * Detect and modify for do/for/if/while structures.
3530 */
3531 public virtual bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
3532 {
3533 return false;
3534 }
3535
3536 /**
3537 * If this statement is the old statement, replace it with the given new statement.
3538 * Also search any sub-ordinate statements.
3539 * **NOTE**: minimally implemented to replace a Jump with a Break or Continue
3540 */
3541 public abstract OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt);
3542
3543 /**
3544 * Print the statement out on the given printer with the given indenting.
3545 * The first line is already indented, subsequent lines must be indented as given.
3546 * This method should leave the printer at the end of the line.
3547 */
3548 public abstract void PrintStmt (TextWriter twout, string indent);
3549
3550 /**
3551 * Strip all statements following this statement
3552 * because this statement jumps somewhere.
3553 */
3554 protected bool StripStuffForTerminal (LinkedListNode<OTStmt> link)
3555 {
3556 // strip all statements following jump until seeing some label
3557 bool rc = false;
3558 if (link != null) {
3559 LinkedListNode<OTStmt> nextlink;
3560 while ((nextlink = link.Next) != null) {
3561 if (nextlink.Value is OTStmtLabel) break;
3562 nextlink.List.Remove (nextlink);
3563 rc = true;
3564 }
3565 }
3566 return rc;
3567 }
3568 }
3569
3570 /**************************\
3571 * Primitive statements *
3572 \**************************/
3573
3574 /**
3575 * Begin catch block (catch).
3576 */
3577 private class OTStmtBegCatBlk : OTStmt {
3578 public OTStmtBegExcBlk tryblock;
3579 public OTStmtBlock catchblock;
3580
3581 private Type excType;
3582
3583 public OTStmtBegCatBlk (Type excType)
3584 {
3585 this.excType = excType;
3586 }
3587
3588 public override void CountRefs ()
3589 {
3590 catchblock.CountRefs ();
3591 }
3592
3593 public override bool StripStuff (LinkedListNode<OTStmt> link)
3594 {
3595 return catchblock.StripStuff (null);
3596 }
3597
3598 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
3599 {
3600 return catchblock.ReplaceOperand (oldopnd, newopnd);
3601 }
3602
3603 public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
3604 {
3605 return catchblock.DetectDoForIfWhile (link);
3606 }
3607
3608 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
3609 {
3610 catchblock = (OTStmtBlock) catchblock.ReplaceStatement (oldstmt, newstmt);
3611 return this;
3612 }
3613
3614 /**
3615 * Print out the catch block including its enclosed statements.
3616 */
3617 public override void PrintStmt (TextWriter twout, string indent)
3618 {
3619 twout.Write ("catch (" + excType.Name + ") ");
3620 catchblock.PrintStmt (twout, indent);
3621 }
3622 }
3623
3624 /**
3625 * Begin exception block (try).
3626 */
3627 private class OTStmtBegExcBlk : OTStmt {
3628
3629 // statements within the try { } not including any catch or finally
3630 public OTStmtBlock tryblock;
3631
3632 // list of all catch { } blocks associated with this try { }
3633 public LinkedList<OTStmtBegCatBlk> catches = new LinkedList<OTStmtBegCatBlk> ();
3634
3635 // possible single finally { } associated with this try
3636 public OTStmtBegFinBlk finblock; // might be null
3637
3638 public override void CountRefs ()
3639 {
3640 tryblock.CountRefs ();
3641 foreach (OTStmtBegCatBlk catblock in catches) {
3642 catblock.CountRefs ();
3643 }
3644 if (finblock != null) finblock.CountRefs ();
3645 }
3646
3647 /**
3648 * Strip behind-the-scenes info from all the sub-blocks.
3649 */
3650 public override bool StripStuff (LinkedListNode<OTStmt> link)
3651 {
3652 // strip behind-the-scenes info from all the sub-blocks.
3653 bool rc = tryblock.StripStuff (null);
3654 foreach (OTStmtBegCatBlk catblk in catches) {
3655 rc |= catblk.StripStuff (null);
3656 }
3657 if (finblock != null) rc |= finblock.StripStuff (null);
3658 if (rc) return true;
3659
3660 // change:
3661 // try {
3662 // ...
3663 // }
3664 // to:
3665 // {
3666 // ...
3667 // }
3668 // note that an empty catch () { } has meaning so can't be stripped
3669 // empty finally { } blocks strips itself from the try
3670 if ((catches.Count == 0) && (finblock == null) && (link != null)) {
3671 link.List.AddAfter (link, tryblock);
3672 tryblock = null;
3673 link.List.Remove (link);
3674 return true;
3675 }
3676
3677 return false;
3678 }
3679
3680 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
3681 {
3682 bool rc = tryblock.ReplaceOperand (oldopnd, newopnd);
3683 foreach (OTStmtBegCatBlk catblk in catches) {
3684 rc |= catblk.ReplaceOperand (oldopnd, newopnd);
3685 }
3686 if (finblock != null) rc |= finblock.ReplaceOperand (oldopnd, newopnd);
3687 return rc;
3688 }
3689
3690 public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
3691 {
3692 bool rc = tryblock.DetectDoForIfWhile (link);
3693 foreach (OTStmtBegCatBlk catblk in catches) {
3694 rc |= catblk.DetectDoForIfWhile (link);
3695 }
3696 if (finblock != null) rc |= finblock.DetectDoForIfWhile (link);
3697 return rc;
3698 }
3699
3700 /**
3701 * Assume we will never try to replace the try block itself.
3702 * But go through all our sub-ordinates statements.
3703 */
3704 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
3705 {
3706 tryblock = (OTStmtBlock) tryblock.ReplaceStatement (oldstmt, newstmt);
3707 for (LinkedListNode<OTStmtBegCatBlk> catlink = catches.First; catlink != null; catlink = catlink.Next) {
3708 catlink.Value = (OTStmtBegCatBlk) catlink.Value.ReplaceStatement (oldstmt, newstmt);
3709 }
3710 if (finblock != null) finblock = (OTStmtBegFinBlk) finblock.ReplaceStatement (oldstmt, newstmt);
3711 return this;
3712 }
3713
3714 /**
3715 * Print out the try block including its enclosed statements.
3716 * And since the try is the only thing pushed to the outer block,
3717 * we also print out all the catch and finally blocks.
3718 */
3719 public override void PrintStmt (TextWriter twout, string indent)
3720 {
3721 twout.Write ("try ");
3722 tryblock.PrintStmt (twout, indent);
3723 foreach (OTStmtBegCatBlk catblk in catches) {
3724 twout.Write (' ');
3725 catblk.PrintStmt (twout, indent);
3726 }
3727 if (finblock != null) {
3728 twout.Write (' ');
3729 finblock.PrintStmt (twout, indent);
3730 }
3731 }
3732 }
3733
3734 /**
3735 * Begin finally block (finally).
3736 */
3737 private class OTStmtBegFinBlk : OTStmt {
3738 public OTStmtBegExcBlk tryblock;
3739 public OTStmtBlock finblock;
3740
3741 public override void CountRefs ()
3742 {
3743 finblock.CountRefs ();
3744 }
3745
3746 /**
3747 * Strip behind-the-scene parts from the finally block.
3748 */
3749 public override bool StripStuff (LinkedListNode<OTStmt> link)
3750 {
3751 // strip behind-the-scenes parts from finally block itself
3752 if (finblock.StripStuff (null)) return true;
3753
3754 // if finblock is empty, delete the finally from the try
3755 if (finblock.blkstmts.Count == 0) {
3756 tryblock.finblock = null;
3757 return true;
3758 }
3759
3760 return false;
3761 }
3762
3763 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
3764 {
3765 return finblock.ReplaceOperand (oldopnd, newopnd);
3766 }
3767
3768 public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
3769 {
3770 return finblock.DetectDoForIfWhile (link);
3771 }
3772
3773 /**
3774 * Assume we will never try to replace the finally block itself.
3775 * But go through all our sub-ordinates statements.
3776 */
3777 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
3778 {
3779 finblock = (OTStmtBlock) finblock.ReplaceStatement (oldstmt, newstmt);
3780 return this;
3781 }
3782
3783 /**
3784 * Print out the finally block including its enclosed statements.
3785 */
3786 public override void PrintStmt (TextWriter twout, string indent)
3787 {
3788 twout.Write ("finally ");
3789 finblock.PrintStmt (twout, indent);
3790 }
3791 }
3792
3793 /**
3794 * Simple if jump/break/continue statement.
3795 */
3796 private class OTStmtCond : OTStmt {
3797 public OTOpnd valu;
3798 public OTStmt stmt; // jump, break, continue only
3799
3800 public OTStmtCond (OTOpnd valu, OTStmt stmt)
3801 {
3802 this.valu = valu;
3803 this.stmt = stmt;
3804 }
3805
3806 public override void CountRefs ()
3807 {
3808 valu.CountRefs (false);
3809 stmt.CountRefs ();
3810 }
3811
3812 public override bool StripStuff (LinkedListNode<OTStmt> link)
3813 {
3814 // we assume that callMode is always CallMode_NORMAL, ie, not doing a stack capture or restore
3815 // so the 'if (arg$0.callMode bne.un 0) ...' is deleted
3816 // and the 'if (arg$0.callMode bne.un 1) ...' becomes unconditional
3817 // it can also be __xmrinst.callMode instead of arg$0
3818 if (valu is OTOpndBinOp) {
3819 OTOpndBinOp binop = (OTOpndBinOp) valu;
3820 if ((binop.left is OTOpndField) && (binop.opCode.ToString () == "bne.un") && (binop.rite is OTOpndInt)) {
3821 OTOpndField leftfield = (OTOpndField) binop.left;
3822 if (leftfield.field.Name == _callMode) {
3823 bool ok = false;
3824 if (leftfield.obj is OTOpndArg) {
3825 ok = ((OTOpndArg) leftfield.obj).index == 0;
3826 }
3827 if (leftfield.obj is OTOpndLocal) {
3828 ok = ((OTOpndLocal) leftfield.obj).local.name.StartsWith (_xmrinstlocal);
3829 }
3830 if (ok) {
3831 OTOpndInt riteint = (OTOpndInt) binop.rite;
3832
3833 // delete 'if ((arg$0).callMode bne.un 0) ...'
3834 if (riteint.value == XMRInstAbstract.CallMode_NORMAL) {
3835 link.List.Remove (link);
3836 return true;
3837 }
3838
3839 // make 'if ((arg$0).callMode bne.un 1) ...' unconditional
3840 if (riteint.value == XMRInstAbstract.CallMode_SAVE) {
3841 link.Value = stmt;
3842 return true;
3843 }
3844 }
3845 }
3846 }
3847 }
3848
3849 // similarly we assume that doGblInit is always 0 to eliminate the code at beginning of default state_entry()
3850 // so the 'if (brfalse __xmrinst.doGblInit) ...' is made unconditional
3851 if (valu is OTOpndUnOp) {
3852 OTOpndUnOp unop = (OTOpndUnOp) valu;
3853 if ((unop.opCode == MyOp.Brfalse) && (unop.value is OTOpndField)) {
3854 OTOpndField valuefield = (OTOpndField) unop.value;
3855 if (valuefield.field.Name == _doGblInit) {
3856 bool ok = false;
3857 if (valuefield.obj is OTOpndLocal) {
3858 ok = ((OTOpndLocal) valuefield.obj).local.name.StartsWith (_xmrinstlocal);
3859 }
3860 if (ok) {
3861
3862 // make 'if (brfalse __xmrinst.doGblInit) ...' unconditional
3863 link.Value = stmt;
3864 return true;
3865 }
3866 }
3867 }
3868 }
3869
3870 return false;
3871 }
3872
3873 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
3874 {
3875 bool rc = stmt.ReplaceOperand (oldopnd, newopnd);
3876 valu = valu.ReplaceOperand (oldopnd, newopnd, ref rc);
3877 return rc;
3878 }
3879
3880 /**
3881 * Maybe this simple if statement is part of a script-level if/then/else statement.
3882 */
3883 public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
3884 {
3885 return OTStmtIf.Detect (link);
3886 }
3887
3888 /**
3889 * Assume we won't replace the if statement itself.
3890 * But search all our sub-ordinate statements.
3891 */
3892 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
3893 {
3894 stmt = stmt.ReplaceStatement (oldstmt, newstmt);
3895 return this;
3896 }
3897
3898 public override void PrintStmt (TextWriter twout, string indent)
3899 {
3900 twout.Write ("if (" + StripBrtrue (valu).PrintableString + ") ");
3901 stmt.PrintStmt (twout, indent);
3902 }
3903
3904 /**
3905 * Scan forward for a given label definition.
3906 * Put intervening statements in a statement block.
3907 * @param link = start scanning after this statement
3908 * @param label = look for this label definition
3909 * @param block = where to return intervening statement block
3910 * @returns null: label definition not found
3911 * else: label definition statement
3912 */
3913 private static LinkedListNode<OTStmt> ScanForLabel (LinkedListNode<OTStmt> link,
3914 OTLabel label, out OTStmtBlock block)
3915 {
3916 block = new OTStmtBlock ();
3917 while ((link = link.Next) != null) {
3918 if (link.Value is OTStmtLabel) {
3919 if (((OTStmtLabel) link.Value).label == label) break;
3920 }
3921 block.blkstmts.AddLast (link.Value);
3922 }
3923 return link;
3924 }
3925
3926 /**
3927 * Strip statements after link up to and including donelink.
3928 */
3929 private static void StripInterveningStatements (LinkedListNode<OTStmt> link, LinkedListNode<OTStmt> donelink)
3930 {
3931 LinkedListNode<OTStmt> striplink;
3932 do {
3933 striplink = link.Next;
3934 striplink.List.Remove (striplink);
3935 } while (striplink != donelink);
3936 }
3937 }
3938
3939 /**
3940 * Jump to a label.
3941 */
3942 private class OTStmtJump : OTStmt {
3943 public OTLabel label;
3944
3945 public static OTStmt Make (OTLabel label)
3946 {
3947 // jumps to __retlbl are return statements
3948 // note that is is safe to say it is a valueless return because
3949 // valued returns are done with this construct:
3950 // __retval = ....;
3951 // jump __retlbl;
3952 // and those __retval = statements have been changed to return statements already
3953 if (label.name.StartsWith (_retlbl)) return new OTStmtRet (null);
3954
3955 // other jumps are really jumps
3956 OTStmtJump it = new OTStmtJump ();
3957 it.label = label;
3958 return it;
3959 }
3960
3961 private OTStmtJump () { }
3962
3963 public override void CountRefs ()
3964 {
3965 label.lbljumps ++;
3966 }
3967
3968 public override bool StripStuff (LinkedListNode<OTStmt> link)
3969 {
3970 if (link == null) return false;
3971
3972 // strip statements following unconditional jump until next label
3973 bool rc = StripStuffForTerminal (link);
3974
3975 // if we (now) have:
3976 // jump label;
3977 // @label;
3978 // ... delete this jump
3979 if (link.Next != null) {
3980 OTStmtLabel nextlabel = (OTStmtLabel) link.Next.Value;
3981 if (nextlabel.label == label) {
3982 link.List.Remove (link);
3983 rc = true;
3984 }
3985 }
3986
3987 return rc;
3988 }
3989
3990 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
3991 {
3992 return false;
3993 }
3994
3995 /**
3996 * This is actually what ReplaceStatement() is currently used for.
3997 * It replaces a jump with a break or a continue.
3998 */
3999 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
4000 {
4001 if ((oldstmt is OTStmtJump) && (((OTStmtJump) oldstmt).label == label)) return newstmt;
4002 return this;
4003 }
4004
4005 public override void PrintStmt (TextWriter twout, string indent)
4006 {
4007 twout.Write ("jump " + label.PrintableName + ';');
4008 }
4009 }
4010
4011 /**
4012 * Label definition point.
4013 */
4014 private class OTStmtLabel : OTStmt {
4015 public OTLabel label;
4016
4017 private OTDecompile decompile;
4018
4019 public static void AddLast (OTDecompile decompile, OTLabel label)
4020 {
4021 OTStmtLabel it = new OTStmtLabel ();
4022 it.label = label;
4023 it.decompile = decompile;
4024 decompile.AddLastStmt (it);
4025 }
4026
4027 private OTStmtLabel () { }
4028
4029 public override void CountRefs ()
4030 {
4031 // don't increment label.lbljumps
4032 // cuz we don't want the positioning
4033 // to count as a reference, only jumps
4034 // to the label should count
4035 }
4036
4037 public override bool StripStuff (LinkedListNode<OTStmt> link)
4038 {
4039 // if label has nothing jumping to it, remove the label
4040 if (link != null) {
4041 label.lbljumps = 0;
4042 decompile.topBlock.CountRefs ();
4043 if (label.lbljumps == 0) {
4044 link.List.Remove (link);
4045 return true;
4046 }
4047 }
4048
4049 return false;
4050 }
4051
4052 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
4053 {
4054 return false;
4055 }
4056
4057 public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
4058 {
4059 if (OTStmtDo.Detect (link)) return true;
4060 if (OTStmtFor.Detect (link, true)) return true;
4061 if (OTStmtFor.Detect (link, false)) return true;
4062 return false;
4063 }
4064
4065 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
4066 {
4067 return this;
4068 }
4069
4070 public override void PrintStmt (TextWriter twout, string indent)
4071 {
4072 twout.Write ("@" + label.PrintableName + ';');
4073 }
4074 }
4075
4076 /**
4077 * Return with or without value.
4078 */
4079 private class OTStmtRet : OTStmt {
4080 public OTOpnd value; // might be null
4081
4082 public OTStmtRet (OTOpnd value)
4083 {
4084 this.value = value;
4085 }
4086
4087 public override void CountRefs ()
4088 {
4089 if (value != null) value.CountRefs (false);
4090 }
4091
4092 public override bool StripStuff (LinkedListNode<OTStmt> link)
4093 {
4094 return StripStuffForTerminal (link);
4095 }
4096
4097 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
4098 {
4099 bool rc = false;
4100 if (value != null) value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
4101 return rc;
4102 }
4103
4104 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
4105 {
4106 return this;
4107 }
4108
4109 public override void PrintStmt (TextWriter twout, string indent)
4110 {
4111 if (value == null) {
4112 twout.Write ("return;");
4113 } else {
4114 twout.Write ("return " + value.PrintableString + ';');
4115 }
4116 }
4117 }
4118
4119 /**
4120 * Store value in variable.
4121 */
4122 private class OTStmtStore : OTStmt {
4123 public OTOpnd varwr;
4124 public OTOpnd value;
4125
4126 private OTDecompile decompile;
4127
4128 public static void AddLast (OTDecompile decompile, OTOpnd varwr, OTOpnd value)
4129 {
4130 OTStmtStore it = new OTStmtStore (varwr, value, decompile);
4131 decompile.AddLastStmt (it);
4132 }
4133
4134 public OTStmtStore (OTOpnd varwr, OTOpnd value, OTDecompile decompile)
4135 {
4136 this.varwr = varwr;
4137 this.value = value;
4138 this.decompile = decompile;
4139 }
4140
4141 public override void CountRefs ()
4142 {
4143 varwr.CountRefs (true);
4144 value.CountRefs (false);
4145 }
4146
4147 public override bool StripStuff (LinkedListNode<OTStmt> link)
4148 {
4149 // strip out stores to __mainCallNo
4150 if (varwr is OTOpndLocal) {
4151 OTOpndLocal local = (OTOpndLocal) varwr;
4152 if (local.local.name.StartsWith (_mainCallNo)) {
4153 link.List.Remove (link);
4154 return true;
4155 }
4156 }
4157
4158 // strip out stores to local vars where the var is not read
4159 // but convert the value to an OTStmtVoid in case it is a call
4160 if (varwr is OTOpndLocal) {
4161 OTOpndLocal local = (OTOpndLocal) varwr;
4162 local.local.nlclreads = 0;
4163 decompile.topBlock.CountRefs ();
4164 if (local.local.nlclreads == 0) {
4165 OTStmt voidstmt = OTStmtVoid.Make (value);
4166 if (voidstmt == null) link.List.Remove (link);
4167 else link.Value = voidstmt;
4168 return true;
4169 }
4170 }
4171
4172 // strip out bla = newobj HeapTrackerList (...);
4173 if (value is OTOpndNewobj) {
4174 OTOpndNewobj valueno = (OTOpndNewobj) value;
4175 if (valueno.ctor.DeclaringType == typeof (HeapTrackerList)) {
4176 link.List.Remove (link);
4177 return true;
4178 }
4179 }
4180
4181 // strip out bla = newobj HeapTrackerObject (...);
4182 if (value is OTOpndNewobj) {
4183 OTOpndNewobj valueno = (OTOpndNewobj) value;
4184 if (valueno.ctor.DeclaringType == typeof (HeapTrackerObject)) {
4185 link.List.Remove (link);
4186 return true;
4187 }
4188 }
4189
4190 // strip out bla = newobj HeapTrackerString (...);
4191 if (value is OTOpndNewobj) {
4192 OTOpndNewobj valueno = (OTOpndNewobj) value;
4193 if (valueno.ctor.DeclaringType == typeof (HeapTrackerString)) {
4194 link.List.Remove (link);
4195 return true;
4196 }
4197 }
4198
4199 // convert tmp$n = bla bla;
4200 // .... tmp$n ....;
4201 // to
4202 // .... bla bla ....;
4203 // gets rid of vast majority of temps
4204 if (varwr is OTOpndLocal) {
4205 OTOpndLocal temp = (OTOpndLocal) varwr;
4206 if (temp.local.name.StartsWith ("tmp$")) {
4207 temp.local.nlclreads = 0;
4208 temp.local.nlclwrites = 0;
4209 decompile.topBlock.CountRefs ();
4210 if ((temp.local.nlclreads == 1) && (temp.local.nlclwrites == 1) && (link.Next != null)) {
4211 OTStmt nextstmt = link.Next.Value;
4212 if (!(nextstmt is OTStmtBlock)) {
4213 if (nextstmt.ReplaceOperand (varwr, value)) {
4214 link.List.Remove (link);
4215 return true;
4216 }
4217 }
4218 }
4219
4220 // also try to convert:
4221 // tmp$n = ... asdf ... << we are here (link)
4222 // lcl = tmp$n; << nextstore
4223 // ... qwer tmp$n ...
4224 // ... no further references to tmp$n
4225 // to:
4226 // lcl = ... asdf ...
4227 // ... qwer lcl ...
4228 if ((temp.local.nlclreads == 2) && (temp.local.nlclwrites == 1) &&
4229 (link.Next != null) && (link.Next.Value is OTStmtStore)) {
4230 OTStmtStore nextstore = (OTStmtStore) link.Next.Value;
4231 if ((nextstore.varwr is OTOpndLocal) && (nextstore.value is OTOpndLocal) && (link.Next.Next != null)) {
4232 OTOpndLocal localopnd = (OTOpndLocal) nextstore.varwr;
4233 OTOpndLocal tempopnd = (OTOpndLocal) nextstore.value;
4234 if (tempopnd.local == temp.local) {
4235 OTStmt finalstmt = link.Next.Next.Value;
4236 if (finalstmt.ReplaceOperand (tempopnd, localopnd)) {
4237 nextstore.value = value;
4238 link.List.Remove (link);
4239 return true;
4240 }
4241 }
4242 }
4243 }
4244 }
4245 }
4246
4247 // convert:
4248 // dup$n = ... asdf ... << we are here
4249 // lcl = dup$n;
4250 // ... qwer dup$n ...
4251 // ... no further references to dup$n
4252 // to:
4253 // lcl = ... asdf ...
4254 // ... qwer lcl ...
4255 if ((varwr is OTOpndDup) && (link != null)) {
4256 OTOpndDup vardup = (OTOpndDup) varwr;
4257 LinkedListNode<OTStmt> nextlink = link.Next;
4258 vardup.ndupreads = 0;
4259 decompile.topBlock.CountRefs ();
4260 if ((vardup.ndupreads == 2) && (nextlink != null) && (nextlink.Value is OTStmtStore)) {
4261
4262 // point to the supposed lcl = dup$n statement
4263 OTStmtStore nextstore = (OTStmtStore) nextlink.Value;
4264 LinkedListNode<OTStmt> nextlink2 = nextlink.Next;
4265 if ((nextstore.varwr is OTOpndLocal) && (nextstore.value == vardup) && (nextlink2 != null)) {
4266
4267 // get the local var being written and point to the ... qwer dup$n ... statement
4268 OTOpndLocal varlcl = (OTOpndLocal) nextstore.varwr;
4269 OTStmt nextstmt2 = nextlink2.Value;
4270
4271 // try to replace dup$n in qwer with lcl
4272 if (nextstmt2.ReplaceOperand (vardup, varlcl)) {
4273
4274 // successful, replace dup$n in asdf with lcl
4275 // and delete the lcl = dup$n statement
4276 varwr = varlcl;
4277 nextlink.List.Remove (nextlink);
4278 return true;
4279 }
4280 }
4281 }
4282 }
4283
4284 // convert:
4285 // dup$n = ... asdf ... << we are here
4286 // ... qwer dup$n ...
4287 // ... no further references to dup$n
4288 // to:
4289 // ... qwer ... asdf ... ...
4290 if ((varwr is OTOpndDup) && (link != null)) {
4291 OTOpndDup vardup = (OTOpndDup) varwr;
4292 LinkedListNode<OTStmt> nextlink = link.Next;
4293 vardup.ndupreads = 0;
4294 decompile.topBlock.CountRefs ();
4295 if ((vardup.ndupreads == 1) && (nextlink != null)) {
4296
4297 // point to the ... qwer dup$n ... statement
4298 OTStmt nextstmt = nextlink.Value;
4299
4300 // try to replace dup$n in qwer with ... asdf ...
4301 if (nextstmt.ReplaceOperand (vardup, value)) {
4302
4303 // successful, delete the dup$n = ... asdf ... statement
4304 link.List.Remove (link);
4305 return true;
4306 }
4307 }
4308 }
4309
4310 // look for list initialization [ ... ]
4311 if (OTOpndListIni.Detect (link)) return true;
4312
4313 // __xmrinst = (XMRInstAbstract) arg$0 indicates this is an event handler
4314 // so strip it out and set the flag
4315 if ((varwr is OTOpndLocal) && (value is OTOpndCast)) {
4316 OTOpndLocal lcl = (OTOpndLocal) varwr;
4317 OTOpndCast cast = (OTOpndCast) value;
4318 if (lcl.local.name.StartsWith (_xmrinstlocal) && (cast.value is OTOpndArg)) {
4319 link.List.Remove (link);
4320 return true;
4321 }
4322 }
4323
4324 // local = [ (optional cast) ] __xmrinst.ehArgs[n] is a definition of event handler arg #n
4325 // if found, make it event handler arg list definition
4326 OTOpnd valuenocast = value;
4327 if (valuenocast is OTOpndCast) valuenocast = ((OTOpndCast) value).value;
4328 if ((varwr is OTOpndLocal) && (valuenocast is OTOpndArrayElem)) {
4329 OTOpndArrayElem array = (OTOpndArrayElem) valuenocast;
4330 if ((array.array is OTOpndField) && (array.index is OTOpndInt)) {
4331 OTOpndField arrayfield = (OTOpndField) array.array;
4332 if ((arrayfield.obj is OTOpndLocal) &&
4333 ((OTOpndLocal) arrayfield.obj).local.name.StartsWith (_xmrinstlocal) &&
4334 (arrayfield.field.Name == _ehArgs)) {
4335 int index = ((OTOpndInt) array.index).value;
4336 decompile.eharglist[index] = ((OTOpndLocal) varwr).local;
4337 link.List.Remove (link);
4338 return true;
4339 }
4340 }
4341 }
4342
4343 // __retval$n = ...; => return ...;
4344 if (varwr is OTOpndLocal) {
4345 OTOpndLocal lcl = (OTOpndLocal) varwr;
4346 if (lcl.local.name.StartsWith (_retval)) {
4347 link.Value = new OTStmtRet (value);
4348 return true;
4349 }
4350 }
4351
4352 return false;
4353 }
4354
4355 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
4356 {
4357 bool rc = false;
4358 if (value != null) value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
4359 return rc;
4360 }
4361
4362 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
4363 {
4364 return this;
4365 }
4366
4367 public override void PrintStmt (TextWriter twout, string indent)
4368 {
4369 // print x = x + 1 as x += 1, but don't print x = x < 3 as x <= 3
4370 if (value is OTOpndBinOp) {
4371 OTOpndBinOp valuebo = (OTOpndBinOp) value;
4372 if (varwr.SameAs (valuebo.left) && " add and div mul or rem shl shr sub xor ".Contains (' ' + valuebo.opCode.name + ' ')) {
4373 twout.Write (varwr.PrintableString + ' ' + valuebo.opCode.source + "= " + valuebo.rite.PrintableString + ';');
4374 return;
4375 }
4376 }
4377
4378 twout.Write (varwr.PrintableString + " = " + value.PrintableString + ';');
4379 }
4380 }
4381
4382 /**
4383 * Dispatch to a table of labels.
4384 */
4385 private class OTStmtSwitch : OTStmt {
4386 private OTOpnd index;
4387 private OTLabel[] labels;
4388
4389 public OTStmtSwitch (OTOpnd index, OTLabel[] labels)
4390 {
4391 this.index = index;
4392 this.labels = labels;
4393 }
4394
4395 public override void CountRefs ()
4396 {
4397 index.CountRefs (false);
4398 foreach (OTLabel label in labels) {
4399 label.lbljumps ++;
4400 }
4401 }
4402
4403 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
4404 {
4405 bool rc = false;
4406 if (index != null) index = index.ReplaceOperand (oldopnd, newopnd, ref rc);
4407 return rc;
4408 }
4409
4410 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
4411 {
4412 return this;
4413 }
4414
4415 public override void PrintStmt (TextWriter twout, string indent)
4416 {
4417 twout.Write ("switch (" + index.PrintableString + ") {\n");
4418 for (int i = 0; i < labels.Length; i ++) {
4419 twout.Write (indent + INDENT + "case " + i + ": jump " + labels[i].name + ";\n");
4420 }
4421 twout.Write (indent + '}');
4422 }
4423 }
4424
4425 /**
4426 * Throw an exception.
4427 */
4428 private class OTStmtThrow : OTStmt {
4429 private OTOpnd value;
4430 private OTDecompile decompile;
4431
4432 public OTStmtThrow (OTOpnd value, OTDecompile decompile)
4433 {
4434 this.value = value;
4435 this.decompile = decompile;
4436 }
4437
4438 public override void CountRefs ()
4439 {
4440 value.CountRefs (false);
4441 }
4442
4443 public override bool StripStuff (LinkedListNode<OTStmt> link)
4444 {
4445 return StripStuffForTerminal (link);
4446 }
4447
4448 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
4449 {
4450 bool rc = false;
4451 if (value != null) value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
4452 return rc;
4453 }
4454
4455 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
4456 {
4457 return this;
4458 }
4459
4460 public override void PrintStmt (TextWriter twout, string indent)
4461 {
4462 // throw newobj ScriptUndefinedStateException ("x") => state x
4463 if (value is OTOpndNewobj) {
4464 OTOpndNewobj valueno = (OTOpndNewobj) value;
4465 if ((valueno.ctor.DeclaringType == typeof (ScriptUndefinedStateException)) &&
4466 (valueno.args.Length == 1) && (valueno.args[0] is OTOpndString)) {
4467 OTOpndString arg0 = (OTOpndString) valueno.args[0];
4468 twout.Write ("state " + arg0.value + "; /* throws undefined state exception */");
4469 return;
4470 }
4471 }
4472
4473 // throw newobj ScriptChangeStateException (n) => state n
4474 if (value is OTOpndNewobj) {
4475 OTOpndNewobj valueno = (OTOpndNewobj) value;
4476 if ((valueno.ctor.DeclaringType == typeof (ScriptChangeStateException)) &&
4477 (valueno.args.Length == 1) && (valueno.args[0] is OTOpndInt)) {
4478 OTOpndInt arg0 = (OTOpndInt) valueno.args[0];
4479 twout.Write ("state " + decompile.scriptObjCode.stateNames[arg0.value] + ';');
4480 return;
4481 }
4482 }
4483
4484 // throwing something else, output as is
4485 twout.Write ("throw " + value.PrintableString + ';');
4486 }
4487 }
4488
4489 /**
4490 * Call with void return, or really anything that we discard the value of after computing it.
4491 */
4492 private class OTStmtVoid : OTStmt {
4493 private OTOpnd value;
4494
4495 public static void AddLast (OTDecompile decompile, OTOpnd value)
4496 {
4497 OTStmt it = OTStmtVoid.Make (value);
4498 if (it != null) decompile.AddLastStmt (it);
4499 }
4500
4501 public static OTStmt Make (OTOpnd value)
4502 {
4503 if (!value.HasSideEffects) return null;
4504 OTStmtVoid it = new OTStmtVoid ();
4505 it.value = value;
4506 return it;
4507 }
4508
4509 private OTStmtVoid () { }
4510
4511 public override void CountRefs ()
4512 {
4513 value.CountRefs (false);
4514 }
4515
4516 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
4517 {
4518 bool rc = false;
4519 value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
4520 return rc;
4521 }
4522
4523 public override bool StripStuff (LinkedListNode<OTStmt> link)
4524 {
4525 // strip out calls to CheckRunQuick() and CheckRunStack()
4526 if (value is OTOpndCall) {
4527 OTOpndCall call = (OTOpndCall) value;
4528 MethodInfo method = call.method;
4529 if ((method.Name == _checkRunQuick) || (method.Name == _checkRunStack)) {
4530 link.List.Remove (link);
4531 return true;
4532 }
4533 }
4534
4535 return false;
4536 }
4537
4538 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
4539 {
4540 return this;
4541 }
4542
4543 public override void PrintStmt (TextWriter twout, string indent)
4544 {
4545 twout.Write (value.PrintableString + ';');
4546 }
4547 }
4548
4549 /***************************\
4550 * Structured statements *
4551 \***************************/
4552
4553 /**
4554 * Block of statements.
4555 */
4556 private class OTStmtBlock : OTStmt {
4557 public LinkedList<OTStmt> blkstmts = new LinkedList<OTStmt> ();
4558
4559 public override void CountRefs ()
4560 {
4561 foreach (OTStmt stmt in blkstmts) {
4562 stmt.CountRefs ();
4563 }
4564 }
4565
4566 /**
4567 * Scrub out all references to behind-the-scenes parts and simplify.
4568 */
4569 public override bool StripStuff (LinkedListNode<OTStmt> link)
4570 {
4571 // loop through all sub-statements to strip out behind-the-scenes references
4572 bool rc = false;
4573 loop:
4574 for (LinkedListNode<OTStmt> stmtlink = blkstmts.First; stmtlink != null; stmtlink = stmtlink.Next) {
4575 if (stmtlink.Value.StripStuff (stmtlink)) {
4576 rc = true;
4577 goto loop;
4578 }
4579 }
4580 if (rc) return true;
4581
4582 // try to merge this block into outer block
4583 // change:
4584 // {
4585 // ...
4586 // { << link points here
4587 // ...
4588 // }
4589 // ...
4590 // }
4591 // to:
4592 // {
4593 // ...
4594 // ...
4595 // ...
4596 // }
4597 if (link != null) {
4598 LinkedListNode<OTStmt> nextlink;
4599 while ((nextlink = blkstmts.Last) != null) {
4600 nextlink.List.Remove (nextlink);
4601 link.List.AddAfter (link, nextlink);
4602 }
4603 link.List.Remove (link);
4604 return true;
4605 }
4606
4607 return rc;
4608 }
4609
4610 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
4611 {
4612 bool rc = false;
4613 foreach (OTStmt stmt in blkstmts) {
4614 rc |= stmt.ReplaceOperand (oldopnd, newopnd);
4615 }
4616 return rc;
4617 }
4618
4619 /**
4620 * Check each statement in the block to see if it starts a do/for/if/while statement.
4621 */
4622 public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
4623 {
4624 bool rc = false;
4625 loop:
4626 for (link = blkstmts.First; link != null; link = link.Next) {
4627 if (link.Value.DetectDoForIfWhile (link)) {
4628 rc = true;
4629 goto loop;
4630 }
4631 }
4632 return rc;
4633 }
4634
4635 /**
4636 * Assume we will never try to replace the block itself.
4637 * But go through all our sub-ordinates statements.
4638 */
4639 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
4640 {
4641 for (LinkedListNode<OTStmt> childlink = blkstmts.First; childlink != null; childlink = childlink.Next) {
4642 childlink.Value = childlink.Value.ReplaceStatement (oldstmt, newstmt);
4643 }
4644 return this;
4645 }
4646
4647 /**
4648 * Print out the block including its enclosed statements.
4649 */
4650 public override void PrintStmt (TextWriter twout, string indent)
4651 {
4652 switch (blkstmts.Count) {
4653 case 0: {
4654 twout.Write ("{ }");
4655 break;
4656 }
4657 ////case 1: {
4658 //// blkstmts.First.Value.PrintStmt (twout, indent);
4659 //// break;
4660 ////}
4661 default: {
4662 twout.Write ('{');
4663 PrintBodyAndEnd (twout, indent);
4664 break;
4665 }
4666 }
4667 }
4668
4669 public void PrintBodyAndEnd (TextWriter twout, string indent)
4670 {
4671 string newindent = indent + INDENT;
4672 foreach (OTStmt stmt in blkstmts) {
4673 twout.Write ('\n' + indent);
4674 if (!(stmt is OTStmtLabel)) twout.Write (INDENT);
4675 else twout.Write (LABELINDENT);
4676 stmt.PrintStmt (twout, newindent);
4677 }
4678 twout.Write ('\n' + indent + '}');
4679 }
4680 }
4681
4682 /**
4683 * 'do' statement.
4684 */
4685 private class OTStmtDo : OTStmt {
4686 private OTOpnd dotest;
4687 private OTStmtBlock dobody;
4688
4689 /**
4690 * See if we have a do loop...
4691 * @doloop_<suffix>; << link points here
4692 * ... <dobody> ...
4693 * [ if (dotest) ] jump doloop_<suffix>;
4694 */
4695 public static bool Detect (LinkedListNode<OTStmt> link)
4696 {
4697 // see if we have label starting with 'doloop_'
4698 OTLabel looplabel = ((OTStmtLabel) link.Value).label;
4699 if (!looplabel.name.StartsWith (_doLoop)) return false;
4700
4701 // good chance we have a do loop
4702 OTStmtDo it = new OTStmtDo ();
4703
4704 // scan ahead looking for the terminating cond/jump loop
4705 // also gather up the statements for the do body block
4706 it.dobody = new OTStmtBlock ();
4707 LinkedListNode<OTStmt> nextlink;
4708 for (nextlink = link.Next; nextlink != null; nextlink = nextlink.Next) {
4709 OTStmt nextstmt = nextlink.Value;
4710
4711 // add statement to do body
4712 it.dobody.blkstmts.AddLast (nextlink.Value);
4713
4714 // check for something what jumps to loop label
4715 // that gives us the end of the loop
4716 OTStmt maybejump = nextstmt;
4717 if (nextstmt is OTStmtCond) {
4718 maybejump = ((OTStmtCond) nextstmt).stmt;
4719 }
4720 if ((maybejump is OTStmtJump) && (((OTStmtJump) maybejump).label == looplabel)) {
4721 break;
4722 }
4723 }
4724
4725 // make sure we found the jump back to the loop label
4726 if (nextlink == null) return false;
4727
4728 // remove all statements from caller's block including the continue label if any
4729 // but leave the break label alone it will be removed later if unreferenced
4730 // and leave the initial loop label intact for now
4731 for (LinkedListNode<OTStmt> remlink = null; (remlink = link.Next) != null;) {
4732 link.List.Remove (remlink);
4733 if (remlink == nextlink) break;
4734 }
4735
4736 // take test condition from last statement of body
4737 // it should be an cond/jump or just a jump to the loop label
4738 LinkedListNode<OTStmt> lastlink = it.dobody.blkstmts.Last;
4739 OTStmt laststmt = lastlink.Value;
4740 if (laststmt is OTStmtCond) {
4741 it.dotest = ((OTStmtCond) laststmt).valu;
4742 } else {
4743 it.dotest = new OTOpndInt (1);
4744 }
4745 lastlink.List.Remove (lastlink);
4746
4747 // finally replace the loop label with the whole do statement
4748 link.Value = it;
4749
4750 // tell caller we made a change
4751 return true;
4752 }
4753
4754 public override void CountRefs ()
4755 {
4756 if (dotest != null) dotest.CountRefs (false);
4757 if (dobody != null) dobody.CountRefs ();
4758 }
4759
4760 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
4761 {
4762 return dobody.ReplaceOperand (oldopnd, newopnd);
4763 }
4764
4765 public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
4766 {
4767 return dobody.DetectDoForIfWhile (link);
4768 }
4769
4770 /**
4771 * Assume we won't replace the do statement itself.
4772 * But search all our sub-ordinate statements.
4773 */
4774 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
4775 {
4776 dobody = (OTStmtBlock) dobody.ReplaceStatement (oldstmt, newstmt);
4777 return this;
4778 }
4779
4780 public override void PrintStmt (TextWriter twout, string indent)
4781 {
4782 // output do body
4783 twout.Write ("do ");
4784 dobody.PrintStmt (twout, indent);
4785
4786 // output while part
4787 twout.Write (" while (" + StripBrtrue (dotest).PrintableString + ");");
4788 }
4789 }
4790
4791 /**
4792 * 'for' or 'while' statement.
4793 */
4794 private class OTStmtFor : OTStmt {
4795 private bool iswhile;
4796 private OTOpnd fortest;
4797 private OTStmtBlock forbody;
4798 private OTStmt forinit;
4799 private OTStmt forstep;
4800
4801 /**
4802 * See if we have a for or while loop...
4803 * <forinit>
4804 * @forloop_<suffix>; << link points here
4805 * [ if (<fortest>) jump forbreak_<suffix>; ]
4806 * ... <forbody> ...
4807 * jump forloop_<suffix>;
4808 * [ @forbreak_<suffix>; ]
4809 */
4810 public static bool Detect (LinkedListNode<OTStmt> link, bool iswhile)
4811 {
4812 string loopname = iswhile ? _whileLoop : _forLoop;
4813 string breakname = iswhile ? _whileBreak : _forBreak;
4814
4815 // see if we have label starting with 'forloop_'
4816 OTLabel looplabel = ((OTStmtLabel) link.Value).label;
4817 if (!looplabel.name.StartsWith (loopname)) return false;
4818
4819 // good chance we have a for loop
4820 OTStmtFor it = new OTStmtFor ();
4821 it.iswhile = iswhile;
4822
4823 // all labels end with this suffix
4824 string suffix = looplabel.name.Substring (loopname.Length);
4825
4826 // scan ahead looking for the 'jump forloop_<suffix>;' statement
4827 // also gather up the statements for the for body block
4828 it.forbody = new OTStmtBlock ();
4829 LinkedListNode<OTStmt> lastlink;
4830 for (lastlink = link; (lastlink = lastlink.Next) != null;) {
4831
4832 // check for jump forloop that tells us where loop ends
4833 if (lastlink.Value is OTStmtJump) {
4834 OTStmtJump lastjump = (OTStmtJump) lastlink.Value;
4835 if (lastjump.label == looplabel) break;
4836 }
4837
4838 // add to body block
4839 it.forbody.blkstmts.AddLast (lastlink.Value);
4840 }
4841
4842 // make sure we found the 'jump forloop' where the for loop ends
4843 if (lastlink == null) return false;
4844
4845 // remove all statements from caller's block including final jump
4846 // but leave the loop label in place
4847 for (LinkedListNode<OTStmt> nextlink = null; (nextlink = link.Next) != null;) {
4848 link.List.Remove (nextlink);
4849 if (nextlink == lastlink) break;
4850 }
4851
4852 // if statement before loop label is an assignment, use it for the init statement
4853 if (!iswhile && (link.Previous != null) && (link.Previous.Value is OTStmtStore)) {
4854 it.forinit = link.Previous.Value;
4855 link.List.Remove (link.Previous);
4856 }
4857
4858 // if first statement of for body is 'if (...) jump breaklabel' use it for the test value
4859 if ((it.forbody.blkstmts.First != null) && (it.forbody.blkstmts.First.Value is OTStmtCond)) {
4860 OTStmtCond condstmt = (OTStmtCond) it.forbody.blkstmts.First.Value;
4861 if ((condstmt.stmt is OTStmtJump) && (((OTStmtJump) condstmt.stmt).label.name == breakname + suffix)) {
4862 it.fortest = OTOpndUnOp.Make (MyOp.Brfalse, condstmt.valu);
4863 it.forbody.blkstmts.RemoveFirst ();
4864 }
4865 }
4866
4867 // if last statement of body is an assigment,
4868 // use the assignment as the step statement
4869 if (!iswhile && (it.forbody.blkstmts.Last != null) &&
4870 (it.forbody.blkstmts.Last.Value is OTStmtStore)) {
4871 LinkedListNode<OTStmt> storelink = it.forbody.blkstmts.Last;
4872 storelink.List.Remove (storelink);
4873 it.forstep = storelink.Value;
4874 }
4875
4876 // finally replace the loop label with the whole for statement
4877 link.Value = it;
4878
4879 // tell caller we made a change
4880 return true;
4881 }
4882
4883 public override void CountRefs ()
4884 {
4885 if (fortest != null) fortest.CountRefs (false);
4886 if (forbody != null) forbody.CountRefs ();
4887 if (forinit != null) forinit.CountRefs ();
4888 if (forstep != null) forstep.CountRefs ();
4889 }
4890
4891 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
4892 {
4893 return forbody.ReplaceOperand (oldopnd, newopnd) |
4894 ((forinit != null) && forinit.ReplaceOperand (oldopnd, newopnd)) |
4895 ((forstep != null) && forstep.ReplaceOperand (oldopnd, newopnd));
4896 }
4897
4898 public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
4899 {
4900 return forbody.DetectDoForIfWhile (link) |
4901 ((forinit != null) && forinit.DetectDoForIfWhile (link)) |
4902 ((forstep != null) && forstep.DetectDoForIfWhile (link));
4903 }
4904
4905 /**
4906 * Assume we won't replace the for statement itself.
4907 * But search all our sub-ordinate statements.
4908 */
4909 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
4910 {
4911 forbody = (OTStmtBlock) forbody.ReplaceStatement (oldstmt, newstmt);
4912 if (forinit != null) forinit = forinit.ReplaceStatement (oldstmt, newstmt);
4913 if (forstep != null) forstep = forstep.ReplaceStatement (oldstmt, newstmt);
4914 return this;
4915 }
4916
4917 public override void PrintStmt (TextWriter twout, string indent)
4918 {
4919 if (iswhile) {
4920 twout.Write ("while (");
4921 if (fortest == null) {
4922 twout.Write ("TRUE");
4923 } else {
4924 twout.Write (StripBrtrue (fortest).PrintableString);
4925 }
4926 } else {
4927 twout.Write ("for (");
4928 if (forinit != null) {
4929 forinit.PrintStmt (twout, indent + INDENT);
4930 } else {
4931 twout.Write (';');
4932 }
4933 if (fortest != null) {
4934 twout.Write (' ' + StripBrtrue (fortest).PrintableString);
4935 }
4936 twout.Write (';');
4937 if (forstep != null) {
4938 StringWriter sw = new StringWriter ();
4939 sw.Write (' ');
4940 forstep.PrintStmt (sw, indent + INDENT);
4941 StringBuilder sb = sw.GetStringBuilder ();
4942 int sl = sb.Length;
4943 if ((sl > 0) && (sb[sl-1] == ';')) sb.Remove (-- sl, 1);
4944 twout.Write (sb.ToString ());
4945 }
4946 }
4947
4948 twout.Write (") ");
4949 forbody.PrintStmt (twout, indent);
4950 }
4951 }
4952
4953 /**
4954 * if/then/else block.
4955 */
4956 private class OTStmtIf : OTStmt {
4957 private OTOpnd testvalu;
4958 private OTStmt thenstmt;
4959 private OTStmt elsestmt; // might be null
4960
4961 /**
4962 * Try to detect a structured if statement.
4963 *
4964 * if (condition) jump ifdone_<suffix>; << link points here
4965 * ... then body ...
4966 * @ifdone_<suffix>;
4967 *
4968 * if (condition) jump ifelse_<suffix>;
4969 * ... then body ...
4970 * jump ifdone_<suffix>; << optional if true body doesn't fall through
4971 * @ifelse_<suffix>;
4972 * ... else body ...
4973 * @ifdone_<suffix>;
4974 */
4975 public static bool Detect (LinkedListNode<OTStmt> link)
4976 {
4977 OTStmtCond condstmt = (OTStmtCond) link.Value;
4978 if (!(condstmt.stmt is OTStmtJump)) return false;
4979
4980 OTStmtJump jumpstmt = (OTStmtJump) condstmt.stmt;
4981 if (jumpstmt.label.name.StartsWith (_ifDone)) {
4982
4983 // then-only if
4984
4985 // skip forward to find the ifdone_<suffix> label
4986 // also save the intervening statements for the then body
4987 OTStmtBlock thenbody;
4988 LinkedListNode<OTStmt> donelink = ScanForLabel (link, jumpstmt.label, out thenbody);
4989
4990 // make sure we found matching label
4991 if (donelink == null) return false;
4992
4993 // replace the jump ifdone_<suffix> with the <then body>
4994 OTStmtIf it = new OTStmtIf ();
4995 it.thenstmt = thenbody;
4996
4997 // replace the test value with the opposite
4998 it.testvalu = OTOpndUnOp.Make (MyOp.Brfalse, condstmt.valu);
4999 condstmt.valu = null;
5000
5001 // strip out the true body statements from the main code including the ifdone_<suffix> label
5002 StripInterveningStatements (link, donelink);
5003
5004 // replace the simple conditional with the if/then/else block
5005 link.Value = it;
5006
5007 // tell caller we changed something
5008 return true;
5009 }
5010
5011 if (jumpstmt.label.name.StartsWith (_ifElse)) {
5012 string suffix = jumpstmt.label.name.Substring (_ifElse.Length);
5013
5014 // if/then/else
5015 OTStmtIf it = new OTStmtIf ();
5016
5017 // skip forward to find the ifelse_<suffix> label
5018 // also save the intervening statements for the true body
5019 OTStmtBlock thenbody;
5020 LinkedListNode<OTStmt> elselink = ScanForLabel (link, jumpstmt.label, out thenbody);
5021
5022 // make sure we found matching label
5023 if (elselink != null) {
5024
5025 // the last statement of the then body might be a jump ifdone_<suffix>
5026 LinkedListNode<OTStmt> lastthenlink = thenbody.blkstmts.Last;
5027 if ((lastthenlink != null) && (lastthenlink.Value is OTStmtJump)) {
5028 OTStmtJump jumpifdone = (OTStmtJump) lastthenlink.Value;
5029 if (jumpifdone.label.name == _ifDone + suffix) {
5030
5031 lastthenlink.List.Remove (lastthenlink);
5032
5033 // skip forward to find the ifdone_<suffix> label
5034 // also save the intervening statements for the else body
5035 OTStmtBlock elsebody;
5036 LinkedListNode<OTStmt> donelink = ScanForLabel (elselink, jumpifdone.label, out elsebody);
5037 if (donelink != null) {
5038
5039 // replace the jump ifdone_<suffix> with the <true body>
5040 it.thenstmt = thenbody;
5041
5042 // save the else body as well
5043 it.elsestmt = elsebody;
5044
5045 // replace the test value with the opposite
5046 it.testvalu = OTOpndUnOp.Make (MyOp.Brfalse, condstmt.valu);
5047 condstmt.valu = null;
5048
5049 // strip out the true and else body statements from the main code including the ifdone_<suffix> label
5050 StripInterveningStatements (link, donelink);
5051
5052 // replace the simple conditional with the if/then/else block
5053 link.Value = it;
5054
5055 // tell caller we changed something
5056 return true;
5057 }
5058 }
5059 }
5060
5061 // missing the jump _ifDone_<suffix>, so make it a simple if/then
5062 // if (condition) jump ifelse_<suffix>; << link
5063 // ... then body ... << encapsulated in block thenbody
5064 // @ifelse_<suffix>; << elselink
5065 // ... else body ... << still inline and leave it there
5066 // @ifdone_<suffix>; << strip this out
5067
5068 // replace the jump ifelse_<suffix> with the <true body>
5069 it.thenstmt = thenbody;
5070
5071 // replace the test value with the opposite
5072 it.testvalu = OTOpndUnOp.Make (MyOp.Brfalse, condstmt.valu);
5073 condstmt.valu = null;
5074
5075 // strip out the then body statements from the main code including the ifelse_<suffix> label
5076 StripInterveningStatements (link, elselink);
5077
5078 // there's a dangling unused ifdone_<suffix> label ahead that has to be stripped
5079 for (LinkedListNode<OTStmt> donelink = link; (donelink = donelink.Next) != null;) {
5080 if ((donelink.Value is OTStmtLabel) && (((OTStmtLabel) donelink.Value).label.name == _ifDone + suffix)) {
5081 donelink.List.Remove (donelink);
5082 break;
5083 }
5084 }
5085
5086 // replace the simple conditional with the if/then/else block
5087 link.Value = it;
5088
5089 // tell caller we changed something
5090 return true;
5091 }
5092 }
5093
5094 return false;
5095 }
5096
5097 private OTStmtIf () { }
5098
5099 public override void CountRefs ()
5100 {
5101 if (testvalu != null) testvalu.CountRefs (false);
5102 if (thenstmt != null) thenstmt.CountRefs ();
5103 if (elsestmt != null) elsestmt.CountRefs ();
5104 }
5105
5106 public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
5107 {
5108 bool rc = thenstmt.ReplaceOperand (oldopnd, newopnd);
5109 testvalu = testvalu.ReplaceOperand (oldopnd, newopnd, ref rc);
5110 return rc;
5111 }
5112
5113 public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
5114 {
5115 return ((thenstmt != null) && thenstmt.DetectDoForIfWhile (link)) |
5116 ((elsestmt != null) && elsestmt.DetectDoForIfWhile (link));
5117 }
5118
5119 /**
5120 * Assume we won't replace the if statement itself.
5121 * But search all our sub-ordinate statements.
5122 */
5123 public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
5124 {
5125 thenstmt = thenstmt.ReplaceStatement (oldstmt, newstmt);
5126 if (elsestmt != null) elsestmt = elsestmt.ReplaceStatement (oldstmt, newstmt);
5127 return this;
5128 }
5129
5130 public override void PrintStmt (TextWriter twout, string indent)
5131 {
5132 twout.Write ("if (" + StripBrtrue (testvalu).PrintableString + ") ");
5133 OTStmt thenst = ReduceStmtBody (thenstmt, false);
5134 thenst.PrintStmt (twout, indent);
5135 if (elsestmt != null) {
5136 twout.Write ('\n' + indent + "else ");
5137 OTStmt elsest = ReduceStmtBody (elsestmt, true);
5138 elsest.PrintStmt (twout, indent);
5139 }
5140 }
5141
5142 // strip block off a single jump so it prints inline instead of with braces around it
5143 // also, if this is part of else, strip block for ifs to make else if statement
5144 private static OTStmt ReduceStmtBody (OTStmt statement, bool stripif)
5145 {
5146 OTStmt onestmt = statement;
5147 if ((onestmt is OTStmtBlock) && (((OTStmtBlock) onestmt).blkstmts.Count == 1)) {
5148 onestmt = ((OTStmtBlock) onestmt).blkstmts.First.Value;
5149 if ((onestmt is OTStmtJump) || (stripif && (onestmt is OTStmtIf))) {
5150 return onestmt;
5151 }
5152 }
5153 return statement;
5154 }
5155
5156 /**
5157 * Scan forward for a given label definition.
5158 * Put intervening statements in a statement block.
5159 * @param link = start scanning after this statement
5160 * @param label = look for this label definition
5161 * @param block = where to return intervening statement block
5162 * @returns null: label definition not found
5163 * else: label definition statement
5164 */
5165 private static LinkedListNode<OTStmt> ScanForLabel (LinkedListNode<OTStmt> link,
5166 OTLabel label, out OTStmtBlock block)
5167 {
5168 block = new OTStmtBlock ();
5169 while ((link = link.Next) != null) {
5170 if (link.Value is OTStmtLabel) {
5171 if (((OTStmtLabel) link.Value).label == label) break;
5172 }
5173 block.blkstmts.AddLast (link.Value);
5174 }
5175 return link;
5176 }
5177
5178 /**
5179 * Strip statements after link up to and including donelink.
5180 */
5181 private static void StripInterveningStatements (LinkedListNode<OTStmt> link, LinkedListNode<OTStmt> donelink)
5182 {
5183 LinkedListNode<OTStmt> striplink;
5184 do {
5185 striplink = link.Next;
5186 striplink.List.Remove (striplink);
5187 } while (striplink != donelink);
5188 }
5189 }
5190
5191 private class MyOp {
5192 public int index;
5193 public OpCode sysop;
5194 public string name;
5195 public string source;
5196
5197 private static Dictionary<string,MyOp> myopsbyname = new Dictionary<string,MyOp> ();
5198 private static int nextindex = 0;
5199
5200 public MyOp (OpCode sysop)
5201 {
5202 this.index = nextindex ++;
5203 this.sysop = sysop;
5204 this.name = sysop.Name;
5205 myopsbyname.Add (name, this);
5206 }
5207
5208 public MyOp (OpCode sysop, string source)
5209 {
5210 this.index = nextindex ++;
5211 this.sysop = sysop;
5212 this.name = sysop.Name;
5213 this.source = source;
5214 myopsbyname.Add (name, this);
5215 }
5216
5217 public MyOp (string name)
5218 {
5219 this.index = nextindex ++;
5220 this.name = name;
5221 myopsbyname.Add (name, this);
5222 }
5223
5224 public MyOp (string name, string source)
5225 {
5226 this.index = nextindex ++;
5227 this.name = name;
5228 this.source = source;
5229 myopsbyname.Add (name, this);
5230 }
5231
5232 public static MyOp GetByName (string name)
5233 {
5234 return myopsbyname[name];
5235 }
5236
5237 public override string ToString ()
5238 {
5239 return name;
5240 }
5241
5242 // these copied from OpCodes.cs
5243 public static readonly MyOp Nop = new MyOp (OpCodes.Nop);
5244 public static readonly MyOp Break = new MyOp (OpCodes.Break);
5245 public static readonly MyOp Ldarg_0 = new MyOp (OpCodes.Ldarg_0);
5246 public static readonly MyOp Ldarg_1 = new MyOp (OpCodes.Ldarg_1);
5247 public static readonly MyOp Ldarg_2 = new MyOp (OpCodes.Ldarg_2);
5248 public static readonly MyOp Ldarg_3 = new MyOp (OpCodes.Ldarg_3);
5249 public static readonly MyOp Ldloc_0 = new MyOp (OpCodes.Ldloc_0);
5250 public static readonly MyOp Ldloc_1 = new MyOp (OpCodes.Ldloc_1);
5251 public static readonly MyOp Ldloc_2 = new MyOp (OpCodes.Ldloc_2);
5252 public static readonly MyOp Ldloc_3 = new MyOp (OpCodes.Ldloc_3);
5253 public static readonly MyOp Stloc_0 = new MyOp (OpCodes.Stloc_0);
5254 public static readonly MyOp Stloc_1 = new MyOp (OpCodes.Stloc_1);
5255 public static readonly MyOp Stloc_2 = new MyOp (OpCodes.Stloc_2);
5256 public static readonly MyOp Stloc_3 = new MyOp (OpCodes.Stloc_3);
5257 public static readonly MyOp Ldarg_S = new MyOp (OpCodes.Ldarg_S);
5258 public static readonly MyOp Ldarga_S = new MyOp (OpCodes.Ldarga_S);
5259 public static readonly MyOp Starg_S = new MyOp (OpCodes.Starg_S);
5260 public static readonly MyOp Ldloc_S = new MyOp (OpCodes.Ldloc_S);
5261 public static readonly MyOp Ldloca_S = new MyOp (OpCodes.Ldloca_S);
5262 public static readonly MyOp Stloc_S = new MyOp (OpCodes.Stloc_S);
5263 public static readonly MyOp Ldnull = new MyOp (OpCodes.Ldnull);
5264 public static readonly MyOp Ldc_I4_M1 = new MyOp (OpCodes.Ldc_I4_M1);
5265 public static readonly MyOp Ldc_I4_0 = new MyOp (OpCodes.Ldc_I4_0);
5266 public static readonly MyOp Ldc_I4_1 = new MyOp (OpCodes.Ldc_I4_1);
5267 public static readonly MyOp Ldc_I4_2 = new MyOp (OpCodes.Ldc_I4_2);
5268 public static readonly MyOp Ldc_I4_3 = new MyOp (OpCodes.Ldc_I4_3);
5269 public static readonly MyOp Ldc_I4_4 = new MyOp (OpCodes.Ldc_I4_4);
5270 public static readonly MyOp Ldc_I4_5 = new MyOp (OpCodes.Ldc_I4_5);
5271 public static readonly MyOp Ldc_I4_6 = new MyOp (OpCodes.Ldc_I4_6);
5272 public static readonly MyOp Ldc_I4_7 = new MyOp (OpCodes.Ldc_I4_7);
5273 public static readonly MyOp Ldc_I4_8 = new MyOp (OpCodes.Ldc_I4_8);
5274 public static readonly MyOp Ldc_I4_S = new MyOp (OpCodes.Ldc_I4_S);
5275 public static readonly MyOp Ldc_I4 = new MyOp (OpCodes.Ldc_I4);
5276 public static readonly MyOp Ldc_I8 = new MyOp (OpCodes.Ldc_I8);
5277 public static readonly MyOp Ldc_R4 = new MyOp (OpCodes.Ldc_R4);
5278 public static readonly MyOp Ldc_R8 = new MyOp (OpCodes.Ldc_R8);
5279 public static readonly MyOp Dup = new MyOp (OpCodes.Dup);
5280 public static readonly MyOp Pop = new MyOp (OpCodes.Pop);
5281 public static readonly MyOp Jmp = new MyOp (OpCodes.Jmp);
5282 public static readonly MyOp Call = new MyOp (OpCodes.Call);
5283 public static readonly MyOp Calli = new MyOp (OpCodes.Calli);
5284 public static readonly MyOp Ret = new MyOp (OpCodes.Ret);
5285 public static readonly MyOp Br_S = new MyOp (OpCodes.Br_S);
5286 public static readonly MyOp Brfalse_S = new MyOp (OpCodes.Brfalse_S);
5287 public static readonly MyOp Brtrue_S = new MyOp (OpCodes.Brtrue_S);
5288 public static readonly MyOp Beq_S = new MyOp (OpCodes.Beq_S, "==");
5289 public static readonly MyOp Bge_S = new MyOp (OpCodes.Bge_S, ">=");
5290 public static readonly MyOp Bgt_S = new MyOp (OpCodes.Bgt_S, ">");
5291 public static readonly MyOp Ble_S = new MyOp (OpCodes.Ble_S, "<=");
5292 public static readonly MyOp Blt_S = new MyOp (OpCodes.Blt_S, "<");
5293 public static readonly MyOp Bne_Un_S = new MyOp (OpCodes.Bne_Un_S, "!=");
5294 public static readonly MyOp Bge_Un_S = new MyOp (OpCodes.Bge_Un_S);
5295 public static readonly MyOp Bgt_Un_S = new MyOp (OpCodes.Bgt_Un_S);
5296 public static readonly MyOp Ble_Un_S = new MyOp (OpCodes.Ble_Un_S);
5297 public static readonly MyOp Blt_Un_S = new MyOp (OpCodes.Blt_Un_S);
5298 public static readonly MyOp Br = new MyOp (OpCodes.Br);
5299 public static readonly MyOp Brfalse = new MyOp (OpCodes.Brfalse, "!");
5300 public static readonly MyOp Brtrue = new MyOp (OpCodes.Brtrue, "!!");
5301 public static readonly MyOp Beq = new MyOp (OpCodes.Beq, "==");
5302 public static readonly MyOp Bge = new MyOp (OpCodes.Bge, ">=");
5303 public static readonly MyOp Bgt = new MyOp (OpCodes.Bgt, ">");
5304 public static readonly MyOp Ble = new MyOp (OpCodes.Ble, "<=");
5305 public static readonly MyOp Blt = new MyOp (OpCodes.Blt, "<");
5306 public static readonly MyOp Bne_Un = new MyOp (OpCodes.Bne_Un, "!=");
5307 public static readonly MyOp Bge_Un = new MyOp (OpCodes.Bge_Un);
5308 public static readonly MyOp Bgt_Un = new MyOp (OpCodes.Bgt_Un);
5309 public static readonly MyOp Ble_Un = new MyOp (OpCodes.Ble_Un);
5310 public static readonly MyOp Blt_Un = new MyOp (OpCodes.Blt_Un);
5311 public static readonly MyOp Switch = new MyOp (OpCodes.Switch);
5312 public static readonly MyOp Ldind_I1 = new MyOp (OpCodes.Ldind_I1);
5313 public static readonly MyOp Ldind_U1 = new MyOp (OpCodes.Ldind_U1);
5314 public static readonly MyOp Ldind_I2 = new MyOp (OpCodes.Ldind_I2);
5315 public static readonly MyOp Ldind_U2 = new MyOp (OpCodes.Ldind_U2);
5316 public static readonly MyOp Ldind_I4 = new MyOp (OpCodes.Ldind_I4);
5317 public static readonly MyOp Ldind_U4 = new MyOp (OpCodes.Ldind_U4);
5318 public static readonly MyOp Ldind_I8 = new MyOp (OpCodes.Ldind_I8);
5319 public static readonly MyOp Ldind_I = new MyOp (OpCodes.Ldind_I);
5320 public static readonly MyOp Ldind_R4 = new MyOp (OpCodes.Ldind_R4);
5321 public static readonly MyOp Ldind_R8 = new MyOp (OpCodes.Ldind_R8);
5322 public static readonly MyOp Ldind_Ref = new MyOp (OpCodes.Ldind_Ref);
5323 public static readonly MyOp Stind_Ref = new MyOp (OpCodes.Stind_Ref);
5324 public static readonly MyOp Stind_I1 = new MyOp (OpCodes.Stind_I1);
5325 public static readonly MyOp Stind_I2 = new MyOp (OpCodes.Stind_I2);
5326 public static readonly MyOp Stind_I4 = new MyOp (OpCodes.Stind_I4);
5327 public static readonly MyOp Stind_I8 = new MyOp (OpCodes.Stind_I8);
5328 public static readonly MyOp Stind_R4 = new MyOp (OpCodes.Stind_R4);
5329 public static readonly MyOp Stind_R8 = new MyOp (OpCodes.Stind_R8);
5330 public static readonly MyOp Add = new MyOp (OpCodes.Add, "+");
5331 public static readonly MyOp Sub = new MyOp (OpCodes.Sub, "-");
5332 public static readonly MyOp Mul = new MyOp (OpCodes.Mul, "*");
5333 public static readonly MyOp Div = new MyOp (OpCodes.Div, "/");
5334 public static readonly MyOp Div_Un = new MyOp (OpCodes.Div_Un);
5335 public static readonly MyOp Rem = new MyOp (OpCodes.Rem, "%");
5336 public static readonly MyOp Rem_Un = new MyOp (OpCodes.Rem_Un);
5337 public static readonly MyOp And = new MyOp (OpCodes.And, "&");
5338 public static readonly MyOp Or = new MyOp (OpCodes.Or, "|");
5339 public static readonly MyOp Xor = new MyOp (OpCodes.Xor, "^");
5340 public static readonly MyOp Shl = new MyOp (OpCodes.Shl, "<<");
5341 public static readonly MyOp Shr = new MyOp (OpCodes.Shr, ">>");
5342 public static readonly MyOp Shr_Un = new MyOp (OpCodes.Shr_Un);
5343 public static readonly MyOp Neg = new MyOp (OpCodes.Neg, "-");
5344 public static readonly MyOp Not = new MyOp (OpCodes.Not, "~");
5345 public static readonly MyOp Conv_I1 = new MyOp (OpCodes.Conv_I1);
5346 public static readonly MyOp Conv_I2 = new MyOp (OpCodes.Conv_I2);
5347 public static readonly MyOp Conv_I4 = new MyOp (OpCodes.Conv_I4);
5348 public static readonly MyOp Conv_I8 = new MyOp (OpCodes.Conv_I8);
5349 public static readonly MyOp Conv_R4 = new MyOp (OpCodes.Conv_R4);
5350 public static readonly MyOp Conv_R8 = new MyOp (OpCodes.Conv_R8);
5351 public static readonly MyOp Conv_U4 = new MyOp (OpCodes.Conv_U4);
5352 public static readonly MyOp Conv_U8 = new MyOp (OpCodes.Conv_U8);
5353 public static readonly MyOp Callvirt = new MyOp (OpCodes.Callvirt);
5354 public static readonly MyOp Cpobj = new MyOp (OpCodes.Cpobj);
5355 public static readonly MyOp Ldobj = new MyOp (OpCodes.Ldobj);
5356 public static readonly MyOp Ldstr = new MyOp (OpCodes.Ldstr);
5357 public static readonly MyOp Newobj = new MyOp (OpCodes.Newobj);
5358 public static readonly MyOp Castclass = new MyOp (OpCodes.Castclass);
5359 public static readonly MyOp Isinst = new MyOp (OpCodes.Isinst);
5360 public static readonly MyOp Conv_R_Un = new MyOp (OpCodes.Conv_R_Un);
5361 public static readonly MyOp Unbox = new MyOp (OpCodes.Unbox);
5362 public static readonly MyOp Throw = new MyOp (OpCodes.Throw);
5363 public static readonly MyOp Ldfld = new MyOp (OpCodes.Ldfld);
5364 public static readonly MyOp Ldflda = new MyOp (OpCodes.Ldflda);
5365 public static readonly MyOp Stfld = new MyOp (OpCodes.Stfld);
5366 public static readonly MyOp Ldsfld = new MyOp (OpCodes.Ldsfld);
5367 public static readonly MyOp Ldsflda = new MyOp (OpCodes.Ldsflda);
5368 public static readonly MyOp Stsfld = new MyOp (OpCodes.Stsfld);
5369 public static readonly MyOp Stobj = new MyOp (OpCodes.Stobj);
5370 public static readonly MyOp Conv_Ovf_I1_Un = new MyOp (OpCodes.Conv_Ovf_I1_Un);
5371 public static readonly MyOp Conv_Ovf_I2_Un = new MyOp (OpCodes.Conv_Ovf_I2_Un);
5372 public static readonly MyOp Conv_Ovf_I4_Un = new MyOp (OpCodes.Conv_Ovf_I4_Un);
5373 public static readonly MyOp Conv_Ovf_I8_Un = new MyOp (OpCodes.Conv_Ovf_I8_Un);
5374 public static readonly MyOp Conv_Ovf_U1_Un = new MyOp (OpCodes.Conv_Ovf_U1_Un);
5375 public static readonly MyOp Conv_Ovf_U2_Un = new MyOp (OpCodes.Conv_Ovf_U2_Un);
5376 public static readonly MyOp Conv_Ovf_U4_Un = new MyOp (OpCodes.Conv_Ovf_U4_Un);
5377 public static readonly MyOp Conv_Ovf_U8_Un = new MyOp (OpCodes.Conv_Ovf_U8_Un);
5378 public static readonly MyOp Conv_Ovf_I_Un = new MyOp (OpCodes.Conv_Ovf_I_Un);
5379 public static readonly MyOp Conv_Ovf_U_Un = new MyOp (OpCodes.Conv_Ovf_U_Un);
5380 public static readonly MyOp Box = new MyOp (OpCodes.Box);
5381 public static readonly MyOp Newarr = new MyOp (OpCodes.Newarr);
5382 public static readonly MyOp Ldlen = new MyOp (OpCodes.Ldlen);
5383 public static readonly MyOp Ldelema = new MyOp (OpCodes.Ldelema);
5384 public static readonly MyOp Ldelem_I1 = new MyOp (OpCodes.Ldelem_I1);
5385 public static readonly MyOp Ldelem_U1 = new MyOp (OpCodes.Ldelem_U1);
5386 public static readonly MyOp Ldelem_I2 = new MyOp (OpCodes.Ldelem_I2);
5387 public static readonly MyOp Ldelem_U2 = new MyOp (OpCodes.Ldelem_U2);
5388 public static readonly MyOp Ldelem_I4 = new MyOp (OpCodes.Ldelem_I4);
5389 public static readonly MyOp Ldelem_U4 = new MyOp (OpCodes.Ldelem_U4);
5390 public static readonly MyOp Ldelem_I8 = new MyOp (OpCodes.Ldelem_I8);
5391 public static readonly MyOp Ldelem_I = new MyOp (OpCodes.Ldelem_I);
5392 public static readonly MyOp Ldelem_R4 = new MyOp (OpCodes.Ldelem_R4);
5393 public static readonly MyOp Ldelem_R8 = new MyOp (OpCodes.Ldelem_R8);
5394 public static readonly MyOp Ldelem_Ref = new MyOp (OpCodes.Ldelem_Ref);
5395 public static readonly MyOp Stelem_I = new MyOp (OpCodes.Stelem_I);
5396 public static readonly MyOp Stelem_I1 = new MyOp (OpCodes.Stelem_I1);
5397 public static readonly MyOp Stelem_I2 = new MyOp (OpCodes.Stelem_I2);
5398 public static readonly MyOp Stelem_I4 = new MyOp (OpCodes.Stelem_I4);
5399 public static readonly MyOp Stelem_I8 = new MyOp (OpCodes.Stelem_I8);
5400 public static readonly MyOp Stelem_R4 = new MyOp (OpCodes.Stelem_R4);
5401 public static readonly MyOp Stelem_R8 = new MyOp (OpCodes.Stelem_R8);
5402 public static readonly MyOp Stelem_Ref = new MyOp (OpCodes.Stelem_Ref);
5403 public static readonly MyOp Ldelem = new MyOp (OpCodes.Ldelem);
5404 public static readonly MyOp Stelem = new MyOp (OpCodes.Stelem);
5405 public static readonly MyOp Unbox_Any = new MyOp (OpCodes.Unbox_Any);
5406 public static readonly MyOp Conv_Ovf_I1 = new MyOp (OpCodes.Conv_Ovf_I1);
5407 public static readonly MyOp Conv_Ovf_U1 = new MyOp (OpCodes.Conv_Ovf_U1);
5408 public static readonly MyOp Conv_Ovf_I2 = new MyOp (OpCodes.Conv_Ovf_I2);
5409 public static readonly MyOp Conv_Ovf_U2 = new MyOp (OpCodes.Conv_Ovf_U2);
5410 public static readonly MyOp Conv_Ovf_I4 = new MyOp (OpCodes.Conv_Ovf_I4);
5411 public static readonly MyOp Conv_Ovf_U4 = new MyOp (OpCodes.Conv_Ovf_U4);
5412 public static readonly MyOp Conv_Ovf_I8 = new MyOp (OpCodes.Conv_Ovf_I8);
5413 public static readonly MyOp Conv_Ovf_U8 = new MyOp (OpCodes.Conv_Ovf_U8);
5414 public static readonly MyOp Refanyval = new MyOp (OpCodes.Refanyval);
5415 public static readonly MyOp Ckfinite = new MyOp (OpCodes.Ckfinite);
5416 public static readonly MyOp Mkrefany = new MyOp (OpCodes.Mkrefany);
5417 public static readonly MyOp Ldtoken = new MyOp (OpCodes.Ldtoken);
5418 public static readonly MyOp Conv_U2 = new MyOp (OpCodes.Conv_U2);
5419 public static readonly MyOp Conv_U1 = new MyOp (OpCodes.Conv_U1);
5420 public static readonly MyOp Conv_I = new MyOp (OpCodes.Conv_I);
5421 public static readonly MyOp Conv_Ovf_I = new MyOp (OpCodes.Conv_Ovf_I);
5422 public static readonly MyOp Conv_Ovf_U = new MyOp (OpCodes.Conv_Ovf_U);
5423 public static readonly MyOp Add_Ovf = new MyOp (OpCodes.Add_Ovf);
5424 public static readonly MyOp Add_Ovf_Un = new MyOp (OpCodes.Add_Ovf_Un);
5425 public static readonly MyOp Mul_Ovf = new MyOp (OpCodes.Mul_Ovf);
5426 public static readonly MyOp Mul_Ovf_Un = new MyOp (OpCodes.Mul_Ovf_Un);
5427 public static readonly MyOp Sub_Ovf = new MyOp (OpCodes.Sub_Ovf);
5428 public static readonly MyOp Sub_Ovf_Un = new MyOp (OpCodes.Sub_Ovf_Un);
5429 public static readonly MyOp Endfinally = new MyOp (OpCodes.Endfinally);
5430 public static readonly MyOp Leave = new MyOp (OpCodes.Leave);
5431 public static readonly MyOp Leave_S = new MyOp (OpCodes.Leave_S);
5432 public static readonly MyOp Stind_I = new MyOp (OpCodes.Stind_I);
5433 public static readonly MyOp Conv_U = new MyOp (OpCodes.Conv_U);
5434 public static readonly MyOp Prefix7 = new MyOp (OpCodes.Prefix7);
5435 public static readonly MyOp Prefix6 = new MyOp (OpCodes.Prefix6);
5436 public static readonly MyOp Prefix5 = new MyOp (OpCodes.Prefix5);
5437 public static readonly MyOp Prefix4 = new MyOp (OpCodes.Prefix4);
5438 public static readonly MyOp Prefix3 = new MyOp (OpCodes.Prefix3);
5439 public static readonly MyOp Prefix2 = new MyOp (OpCodes.Prefix2);
5440 public static readonly MyOp Prefix1 = new MyOp (OpCodes.Prefix1);
5441 public static readonly MyOp Prefixref = new MyOp (OpCodes.Prefixref);
5442 public static readonly MyOp Arglist = new MyOp (OpCodes.Arglist);
5443 public static readonly MyOp Ceq = new MyOp (OpCodes.Ceq, "==");
5444 public static readonly MyOp Cgt = new MyOp (OpCodes.Cgt, ">");
5445 public static readonly MyOp Cgt_Un = new MyOp (OpCodes.Cgt_Un);
5446 public static readonly MyOp Clt = new MyOp (OpCodes.Clt, "<");
5447 public static readonly MyOp Clt_Un = new MyOp (OpCodes.Clt_Un);
5448 public static readonly MyOp Ldftn = new MyOp (OpCodes.Ldftn);
5449 public static readonly MyOp Ldvirtftn = new MyOp (OpCodes.Ldvirtftn);
5450 public static readonly MyOp Ldarg = new MyOp (OpCodes.Ldarg);
5451 public static readonly MyOp Ldarga = new MyOp (OpCodes.Ldarga);
5452 public static readonly MyOp Starg = new MyOp (OpCodes.Starg);
5453 public static readonly MyOp Ldloc = new MyOp (OpCodes.Ldloc);
5454 public static readonly MyOp Ldloca = new MyOp (OpCodes.Ldloca);
5455 public static readonly MyOp Stloc = new MyOp (OpCodes.Stloc);
5456 public static readonly MyOp Localloc = new MyOp (OpCodes.Localloc);
5457 public static readonly MyOp Endfilter = new MyOp (OpCodes.Endfilter);
5458 public static readonly MyOp Unaligned = new MyOp (OpCodes.Unaligned);
5459 public static readonly MyOp Volatile = new MyOp (OpCodes.Volatile);
5460 public static readonly MyOp Tailcall = new MyOp (OpCodes.Tailcall);
5461 public static readonly MyOp Initobj = new MyOp (OpCodes.Initobj);
5462 public static readonly MyOp Constrained = new MyOp (OpCodes.Constrained);
5463 public static readonly MyOp Cpblk = new MyOp (OpCodes.Cpblk);
5464 public static readonly MyOp Initblk = new MyOp (OpCodes.Initblk);
5465 public static readonly MyOp Rethrow = new MyOp (OpCodes.Rethrow);
5466 public static readonly MyOp Sizeof = new MyOp (OpCodes.Sizeof);
5467 public static readonly MyOp Refanytype = new MyOp (OpCodes.Refanytype);
5468 public static readonly MyOp Readonly = new MyOp (OpCodes.Readonly);
5469
5470 // used internally
5471 public static readonly MyOp Cge = new MyOp ("cge", ">=");
5472 public static readonly MyOp Cle = new MyOp ("cle", "<=");
5473 public static readonly MyOp Cne = new MyOp ("cne", "!=");
5474 }
5475 }
5476}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRSDTypeClObj.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRSDTypeClObj.cs
new file mode 100644
index 0000000..cbb8f96
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRSDTypeClObj.cs
@@ -0,0 +1,259 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Reflection.Emit;
30
31using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
32using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
33using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
34using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
35using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
36using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
37using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
38
39namespace OpenSim.Region.ScriptEngine.XMREngine
40{
41 public class XMRSDTypeClObj
42 {
43 /*
44 * Which script instance we are part of so we can access
45 * the script's global variables and functions.
46 */
47 public XMRInstAbstract xmrInst;
48
49 /*
50 * What class we actually are in the hierarchy
51 * used for casting.
52 */
53 public TokenDeclSDTypeClass sdtcClass;
54
55 /*
56 * Our VTable array, used for calling virtual functions.
57 * And ITable array, used for calling our implementation of interface functions.
58 */
59 public Delegate[] sdtcVTable;
60 public Delegate[][] sdtcITable;
61
62 /*
63 * These arrays hold the insance variable values.
64 * The array lengths are determined by the script compilation,
65 * and are found in TokenDeclSDTypeClass.instSizes.
66 */
67 public XMRInstArrays instVars;
68
69 /**
70 * @brief Called by script's $new() to initialize a new object.
71 */
72 public XMRSDTypeClObj (XMRInstAbstract inst, int classindex)
73 {
74 Construct (inst, classindex);
75 instVars.AllocVarArrays (sdtcClass.instSizes);
76 }
77
78 /**
79 * @brief Set up everything except the instVars arrays.
80 * @param inst = script instance this object is part of
81 * @param classindex = which script-defined type class this object is an onstance of
82 * @returns with the vtables filled in
83 */
84 private void Construct (XMRInstAbstract inst, int classindex)
85 {
86 Delegate[] thisMid = null;
87 TokenDeclSDTypeClass clas = (TokenDeclSDTypeClass)inst.m_ObjCode.sdObjTypesIndx[classindex];
88
89 xmrInst = inst;
90 sdtcClass = clas;
91 instVars = new XMRInstArrays (inst);
92
93 /*
94 * VTable consists of delegates built from DynamicMethods and the 'this' pointer.
95 * Yes, yes, lots of shitty little mallocs.
96 */
97 DynamicMethod[] vDynMeths = clas.vDynMeths;
98 if (vDynMeths != null) {
99 int n = vDynMeths.Length;
100 Type[] vMethTypes = clas.vMethTypes;
101 sdtcVTable = new Delegate[n];
102 for (int i = 0; i < n; i ++) {
103 sdtcVTable[i] = vDynMeths[i].CreateDelegate (vMethTypes[i], this);
104 }
105 }
106
107 /*
108 * Fill in interface vtables.
109 * There is one array of delegates for each implemented interface.
110 * The array of delegates IS the interface's object, ie, the 'this' value of the interface.
111 * To cast from the class type to the interface type, just get the correct array from the table.
112 * To cast from the interface type to the class type, just get Target of entry 0.
113 *
114 * So we end up with this:
115 * sdtcITable[interfacenumber][methodofintfnumber] = delegate of this.ourimplementationofinterfacesmethod
116 */
117 if (clas.iDynMeths != null) {
118 int nIFaces = clas.iDynMeths.Length;
119 sdtcITable = new Delegate[nIFaces][];
120 for (int i = 0; i < nIFaces; i ++) {
121
122 // get vector of entrypoints of our instance methods that implement that interface
123 DynamicMethod[] iDynMeths = clas.iDynMeths[i];
124 Type[] iMethTypes = clas.iMethTypes[i];
125
126 // allocate an array with a slot for each method the interface defines
127 int nMeths = iDynMeths.Length;
128 Delegate[] ivec;
129 if (nMeths > 0) {
130 // fill in the array with delegates that reference back to this class instance
131 ivec = new Delegate[nMeths];
132 for (int j = 0; j < nMeths; j ++) {
133 ivec[j] = iDynMeths[j].CreateDelegate (iMethTypes[j], this);
134 }
135 } else {
136 // just a marker interface with no methods,
137 // allocate a one-element array and fill
138 // with a dummy entry. this will allow casting
139 // back to the original class instance (this)
140 // by reading Target of entry 0.
141 if (thisMid == null) {
142 thisMid = new Delegate[1];
143 thisMid[0] = markerInterfaceDummy.CreateDelegate (typeof (MarkerInterfaceDummy), this);
144 }
145 ivec = thisMid;
146 }
147
148 // save whatever we ended up allocating
149 sdtcITable[i] = ivec;
150 }
151 }
152 }
153
154 private delegate void MarkerInterfaceDummy ();
155 private static DynamicMethod markerInterfaceDummy = MakeMarkerInterfaceDummy ();
156 private static DynamicMethod MakeMarkerInterfaceDummy ()
157 {
158 DynamicMethod dm = new DynamicMethod ("XMRSDTypeClObj.MarkerInterfaceDummy", null, new Type[] { typeof (XMRSDTypeClObj) });
159 ILGenerator ilGen = dm.GetILGenerator ();
160 ilGen.Emit (OpCodes.Ret);
161 return dm;
162 }
163
164 /**
165 * @brief Perform runtime casting of script-defined interface object to
166 * its corresponding script-defined class object.
167 * @param da = interface object (array of delegates pointing to class's implementations of interface's methods)
168 * @param classindex = what class those implementations are supposedly part of
169 * @returns original script-defined class object
170 */
171 public static XMRSDTypeClObj CastIFace2Class (Delegate[] da, int classindex)
172 {
173 return CastClass2Class (da[0].Target, classindex);
174 }
175
176 /**
177 * @brief Perform runtime casting of XMRSDTypeClObj's.
178 * @param ob = XMRSDTypeClObj of unknown script-defined class to cast
179 * @param classindex = script-defined class to cast it to
180 * @returns ob is a valid instance of classindex; else exception thrown
181 */
182 public static XMRSDTypeClObj CastClass2Class (object ob, int classindex)
183 {
184 /*
185 * Let mono check to see if we at least have an XMRSDTypeClObj.
186 */
187 XMRSDTypeClObj ci = (XMRSDTypeClObj)ob;
188 if (ci != null) {
189
190 /*
191 * This is the target class, ie, what we are hoping the object can cast to.
192 */
193 TokenDeclSDTypeClass tc = (TokenDeclSDTypeClass)ci.xmrInst.m_ObjCode.sdObjTypesIndx[classindex];
194
195 /*
196 * Step from the object's actual class rootward.
197 * If we find the target class along the way, the cast is valid.
198 * If we run off the end of the root, the cast is not valid.
199 */
200 for (TokenDeclSDTypeClass ac = ci.sdtcClass; ac != tc; ac = ac.extends) {
201 if (ac == null) throw new InvalidCastException ("invalid cast from " + ci.sdtcClass.longName.val +
202 " to " + tc.longName.val);
203 }
204
205 /*
206 * The target class is at or rootward of the actual class,
207 * so the cast is valid.
208 */
209 }
210 return ci;
211 }
212
213 /**
214 * @brief Cast an arbitrary object to the given interface.
215 * @param ob = object to be cast of unknown type
216 * @returns ob cast to the interface type
217 */
218 public static Delegate[] CastObj2IFace (object ob, string ifacename)
219 {
220 if (ob == null) return null;
221
222 /*
223 * If it is already one of our interfaces, extract the script-defined class object from it.
224 */
225 if (ob is Delegate[]) {
226 Delegate[] da = (Delegate[])ob;
227 ob = da[0].Target;
228 }
229
230 /*
231 * Now that we have a presumed script-defined class object, cast that to the requested interface
232 * by picking the array of delegates that corresponds to the requested interface.
233 */
234 XMRSDTypeClObj ci = (XMRSDTypeClObj)ob;
235 int iFaceIndex = ci.sdtcClass.intfIndices[ifacename];
236 return ci.sdtcITable[iFaceIndex];
237 }
238
239 /**
240 * @brief Write the whole thing out to a stream.
241 */
242 public void Capture (XMRInstArrays.Sender sendValue)
243 {
244 sendValue (this.sdtcClass.sdTypeIndex);
245 this.instVars.SendArrays (sendValue);
246 }
247
248 /**
249 * @brief Read the whole thing in from a stream.
250 */
251 public XMRSDTypeClObj () { }
252 public void Restore (XMRInstAbstract inst, XMRInstArrays.Recver recvValue)
253 {
254 int classindex = (int)recvValue ();
255 Construct (inst, classindex);
256 this.instVars.RecvArrays (recvValue);
257 }
258 }
259}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptThread.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptThread.cs
new file mode 100644
index 0000000..933b478
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptThread.cs
@@ -0,0 +1,262 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using Mono.Tasklets;
29using OpenSim.Framework.Monitoring;
30using System;
31using System.Collections.Generic;
32using System.Threading;
33
34namespace OpenSim.Region.ScriptEngine.XMREngine
35{
36
37 /**
38 * @brief There are NUMSCRIPTHREADWKRS of these.
39 * Each sits in a loop checking the Start and Yield queues for
40 * a script to run and calls the script as a microthread.
41 */
42 public class XMRScriptThread {
43 private static int m_WakeUpOne = 0;
44 public static object m_WakeUpLock = new object();
45 private static Dictionary<Thread,XMRScriptThread> m_AllThreads = new Dictionary<Thread,XMRScriptThread> ();
46
47 /**
48 * @brief Something was just added to the Start or Yield queue so
49 * wake one of the XMRScriptThread instances to run it.
50 */
51 public static void WakeUpOne()
52 {
53 lock (m_WakeUpLock)
54 {
55 m_WakeUpOne ++;
56 Monitor.Pulse (m_WakeUpLock);
57 }
58 }
59
60 public static XMRScriptThread CurrentScriptThread ()
61 {
62 XMRScriptThread st;
63 lock (m_AllThreads) {
64 m_AllThreads.TryGetValue (Thread.CurrentThread, out st);
65 }
66 return st;
67 }
68
69 private bool m_Exiting = false;
70 private bool m_SuspendScriptThreadFlag = false;
71 private bool m_WakeUpThis = false;
72 private bool m_Continuations = false;
73 public DateTime m_LastRanAt = DateTime.MinValue;
74 public int m_ScriptThreadTID = 0;
75 public long m_ScriptExecTime = 0;
76 private Thread thd;
77 private XMREngine engine;
78 public XMRInstance m_RunInstance = null;
79
80 public XMRScriptThread(XMREngine eng)
81 {
82 engine = eng;
83 m_Continuations = engine.uThreadCtor.DeclaringType == typeof (ScriptUThread_Con);
84 thd = XMREngine.StartMyThread (RunScriptThread, "xmrengine script", ThreadPriority.BelowNormal);
85 lock (m_AllThreads) {
86 m_AllThreads.Add (thd, this);
87 }
88 }
89
90 public void SuspendThread()
91 {
92 m_SuspendScriptThreadFlag = true;
93 WakeUpScriptThread();
94 }
95
96 public void ResumeThread()
97 {
98 m_SuspendScriptThreadFlag = false;
99 WakeUpScriptThread();
100 }
101
102 public void Terminate()
103 {
104 m_Exiting = true;
105 WakeUpScriptThread();
106 thd.Join();
107 lock (m_AllThreads) {
108 m_AllThreads.Remove (thd);
109 }
110 thd = null;
111 }
112
113 public void TimeSlice()
114 {
115 XMRInstance instance = m_RunInstance;
116 if (instance != null) {
117 instance.suspendOnCheckRunTemp = true;
118 }
119 }
120
121 /**
122 * @brief Wake up this XMRScriptThread instance.
123 */
124 private void WakeUpScriptThread()
125 {
126 lock (m_WakeUpLock) {
127 m_WakeUpThis = true;
128 Monitor.PulseAll (m_WakeUpLock);
129 }
130 }
131
132 /**
133 * @brief Thread that runs the scripts.
134 */
135 private void RunScriptThread()
136 {
137 XMRInstance inst;
138 Mono.Tasklets.Continuation engstack = null;
139 if (m_Continuations) {
140 engstack = new Mono.Tasklets.Continuation ();
141 engstack.Mark ();
142 }
143 m_ScriptThreadTID = System.Threading.Thread.CurrentThread.ManagedThreadId;
144
145 while (!m_Exiting) {
146 XMREngine.UpdateMyThread ();
147
148 /*
149 * Handle 'xmr resume/suspend' commands.
150 */
151 if (m_SuspendScriptThreadFlag) {
152 lock (m_WakeUpLock) {
153 while (m_SuspendScriptThreadFlag &&
154 !m_Exiting &&
155 (engine.m_ThunkQueue.Count == 0)) {
156 Monitor.Wait (m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
157 XMREngine.UpdateMyThread ();
158 }
159 }
160 }
161
162 /*
163 * Maybe there are some scripts waiting to be migrated in or out.
164 */
165 ThreadStart thunk = null;
166 lock (m_WakeUpLock) {
167 if (engine.m_ThunkQueue.Count > 0) {
168 thunk = engine.m_ThunkQueue.Dequeue ();
169 }
170 }
171 if (thunk != null) {
172 inst = (XMRInstance)thunk.Target;
173 if (m_Continuations && (inst.scrstack == null)) {
174 inst.engstack = engstack;
175 inst.scrstack = new Mono.Tasklets.Continuation ();
176 inst.scrstack.Mark ();
177 }
178 thunk ();
179 continue;
180 }
181
182 if (engine.m_StartProcessing) {
183
184 /*
185 * If event just queued to any idle scripts
186 * start them right away. But only start so
187 * many so we can make some progress on yield
188 * queue.
189 */
190 int numStarts;
191 for (numStarts = 5; -- numStarts >= 0;) {
192 lock (engine.m_StartQueue) {
193 inst = engine.m_StartQueue.RemoveHead();
194 }
195 if (inst == null) break;
196 if (inst.m_IState != XMRInstState.ONSTARTQ) throw new Exception("bad state");
197 if (m_Continuations && (inst.scrstack == null)) {
198 inst.engstack = engstack;
199 inst.scrstack = new Mono.Tasklets.Continuation ();
200 inst.scrstack.Mark ();
201 }
202 RunInstance (inst);
203 }
204
205 /*
206 * If there is something to run, run it
207 * then rescan from the beginning in case
208 * a lot of things have changed meanwhile.
209 *
210 * These are considered lower priority than
211 * m_StartQueue as they have been taking at
212 * least one quantum of CPU time and event
213 * handlers are supposed to be quick.
214 */
215 lock (engine.m_YieldQueue) {
216 inst = engine.m_YieldQueue.RemoveHead();
217 }
218 if (inst != null) {
219 if (inst.m_IState != XMRInstState.ONYIELDQ) throw new Exception("bad state");
220 RunInstance (inst);
221 numStarts = -1;
222 }
223
224 /*
225 * If we left something dangling in the m_StartQueue or m_YieldQueue, go back to check it.
226 */
227 if (numStarts < 0) continue;
228 }
229
230 /*
231 * Nothing to do, sleep.
232 */
233 lock (m_WakeUpLock) {
234 if (!m_WakeUpThis && (m_WakeUpOne <= 0) && !m_Exiting) {
235 Monitor.Wait (m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
236 }
237 m_WakeUpThis = false;
238 if ((m_WakeUpOne > 0) && (-- m_WakeUpOne > 0)) {
239 Monitor.Pulse (m_WakeUpLock);
240 }
241 }
242 }
243 XMREngine.MyThreadExiting ();
244 }
245
246 /**
247 * @brief A script instance was just removed from the Start or Yield Queue.
248 * So run it for a little bit then stick in whatever queue it should go in.
249 */
250 private void RunInstance (XMRInstance inst)
251 {
252 m_LastRanAt = DateTime.UtcNow;
253 m_ScriptExecTime -= (long)(m_LastRanAt - DateTime.MinValue).TotalMilliseconds;
254 inst.m_IState = XMRInstState.RUNNING;
255 m_RunInstance = inst;
256 XMRInstState newIState = inst.RunOne();
257 m_RunInstance = null;
258 engine.HandleNewIState(inst, newIState);
259 m_ScriptExecTime += (long)(DateTime.UtcNow - DateTime.MinValue).TotalMilliseconds;
260 }
261 }
262}
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs
new file mode 100644
index 0000000..2e290dd
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs
@@ -0,0 +1,557 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using Mono.Tasklets;
29using System;
30using System.Collections.Generic;
31using System.Reflection;
32using System.Threading;
33
34
35
36/***************************\
37 * Use standard C# code *
38 * - uses system threads *
39\***************************/
40
41namespace OpenSim.Region.ScriptEngine.XMREngine {
42
43 public class ScriptUThread_Sys : IScriptUThread, IDisposable
44 {
45 private Exception except;
46 private int active; // -1: hibernating
47 // 0: exited
48 // 1: running
49 private object activeLock = new object ();
50 private XMRInstance instance;
51
52 public ScriptUThread_Sys (XMRInstance instance)
53 {
54 this.instance = instance;
55 }
56
57 /**
58 * @brief Start script event handler from the beginning.
59 * Return when either the script event handler completes
60 * or the script calls Hiber().
61 * @returns null: script did not throw any exception so far
62 * else: script threw an exception
63 */
64 public Exception StartEx ()
65 {
66 lock (activeLock) {
67
68 /*
69 * We should only be called when script is inactive.
70 */
71 if (active != 0) throw new Exception ("active=" + active);
72
73 /*
74 * Tell CallSEHThread() to run script event handler in a thread.
75 */
76 active = 1;
77 TredPoo.RunSomething (CallSEHThread);
78
79 /*
80 * Wait for script to call Hiber() or for script to
81 * return back out to CallSEHThread().
82 */
83 while (active > 0) {
84 Monitor.Wait (activeLock);
85 }
86 }
87
88 /*
89 * Return whether or not script threw an exception.
90 */
91 return except;
92 }
93
94 /**
95 * @brief We now want to run some more script code from where it last hibernated
96 * until it either finishes the script event handler or until the script
97 * calls Hiber() again.
98 */
99 public Exception ResumeEx ()
100 {
101 lock (activeLock) {
102
103 /*
104 * We should only be called when script is hibernating.
105 */
106 if (active >= 0) throw new Exception ("active=" + active);
107
108 /*
109 * Tell Hiber() to return back to script.
110 */
111 active = 1;
112 Monitor.PulseAll (activeLock);
113
114 /*
115 * Wait for script to call Hiber() again or for script to
116 * return back out to CallSEHThread().
117 */
118 while (active > 0) {
119 Monitor.Wait (activeLock);
120 }
121 }
122
123 /*
124 * Return whether or not script threw an exception.
125 */
126 return except;
127 }
128
129 /**
130 * @brief Script is being closed out.
131 * Terminate thread asap.
132 */
133 public void Dispose ()
134 {
135 lock (activeLock) {
136 instance = null;
137 Monitor.PulseAll (activeLock);
138 }
139 }
140
141 /**
142 * @brief Determine if script is active.
143 * Returns: 0: nothing started or has returned
144 * Resume() must not be called
145 * Start() may be called
146 * Hiber() must not be called
147 * -1: thread has called Hiber()
148 * Resume() may be called
149 * Start() may be called
150 * Hiber() must not be called
151 * 1: thread is running
152 * Resume() must not be called
153 * Start() must not be called
154 * Hiber() may be called
155 */
156 public int Active ()
157 {
158 return active;
159 }
160
161 /**
162 * @brief This thread executes the script event handler code.
163 */
164 private void CallSEHThread ()
165 {
166 lock (activeLock) {
167 if (active <= 0) throw new Exception ("active=" + active);
168
169 except = null; // assume completion without exception
170 try {
171 instance.CallSEH (); // run script event handler
172 } catch (Exception e) {
173 except = e; // threw exception, save for Start()/Resume()
174 }
175
176 active = 0; // tell Start() or Resume() we're done
177 Monitor.PulseAll (activeLock);
178 }
179 }
180
181 /**
182 * @brief Called by the script event handler whenever it wants to hibernate.
183 */
184 public void Hiber ()
185 {
186 if (active <= 0) throw new Exception ("active=" + active);
187
188 // tell Start() or Resume() we are hibernating
189 active = -1;
190 Monitor.PulseAll (activeLock);
191
192 // wait for Resume() or Dispose() to be called
193 while ((active < 0) && (instance != null)) {
194 Monitor.Wait (activeLock);
195 }
196
197 // don't execute any more script code, just exit
198 if (instance == null) {
199 throw new AbortedByDisposeException ();
200 }
201 }
202
203 /**
204 * @brief Number of remaining stack bytes.
205 */
206 public int StackLeft ()
207 {
208 return 0x7FFFFFFF;
209 }
210
211 public class AbortedByDisposeException : Exception, IXMRUncatchable { }
212
213 /**
214 * @brief Pool of threads that run script event handlers.
215 */
216 private class TredPoo {
217 private static readonly TimeSpan idleTimeSpan = new TimeSpan (0, 0, 1, 0, 0); // 1 minute
218
219 private static int tredPooAvail = 0;
220 private static object tredPooLock = new object ();
221 private static Queue<ThreadStart> tredPooQueue = new Queue<ThreadStart> ();
222
223 /**
224 * @brief Queue a function for execution in a system thread.
225 */
226 public static void RunSomething (ThreadStart entry)
227 {
228 lock (tredPooLock) {
229 tredPooQueue.Enqueue (entry);
230 Monitor.Pulse (tredPooLock);
231 if (tredPooAvail < tredPooQueue.Count) {
232 new TredPoo ();
233 }
234 }
235 }
236
237 /**
238 * @brief Start a new system thread.
239 * It will shortly attempt to dequeue work or if none,
240 * add itself to the available thread list.
241 */
242 private TredPoo ()
243 {
244 Thread thread = new Thread (Main);
245 thread.Name = "XMRUThread_sys";
246 thread.IsBackground = true;
247 thread.Start ();
248 tredPooAvail ++;
249 }
250
251 /**
252 * @brief Executes items from the queue or waits a little while
253 * if nothing. If idle for a while, it exits.
254 */
255 private void Main ()
256 {
257 int first = 1;
258 ThreadStart entry;
259 while (true) {
260 lock (tredPooLock) {
261 tredPooAvail -= first;
262 first = 0;
263 while (tredPooQueue.Count <= 0) {
264 tredPooAvail ++;
265 bool keepgoing = Monitor.Wait (tredPooLock, idleTimeSpan);
266 -- tredPooAvail;
267 if (!keepgoing) return;
268 }
269 entry = tredPooQueue.Dequeue ();
270 }
271 entry ();
272 }
273 }
274 }
275 }
276}
277
278
279
280/*************************************\
281 * Use Mono.Tasklets.Continuations *
282 * - memcpy's stack *
283\*************************************/
284
285namespace OpenSim.Region.ScriptEngine.XMREngine {
286
287 public partial class XMRInstance {
288 public Mono.Tasklets.Continuation engstack;
289 public Mono.Tasklets.Continuation scrstack;
290 }
291
292 public class ScriptUThread_Con : IScriptUThread, IDisposable
293 {
294 private XMRInstance instance;
295
296 public ScriptUThread_Con (XMRInstance instance)
297 {
298 this.instance = instance;
299 }
300
301 private const int SAVEENGINESTACK = 0;
302 private const int LOADENGINESTACK = 1;
303 private const int SAVESCRIPTSTACK = 2;
304 private const int LOADSCRIPTSTACK = 3;
305
306 private Exception except;
307 private int active;
308
309 /**
310 * @brief Start script event handler from the beginning.
311 * Return when either the script event handler completes
312 * or the script calls Hiber().
313 * @returns null: script did not throw any exception so far
314 * else: script threw an exception
315 */
316 public Exception StartEx ()
317 {
318 /*
319 * Save engine stack so we know how to jump back to engine in case
320 * the script calls Hiber().
321 */
322 switch (instance.engstack.Store (SAVEENGINESTACK)) {
323
324 /*
325 * Engine stack has been saved, start running the event handler.
326 */
327 case SAVEENGINESTACK: {
328
329 /*
330 * Run event handler according to stackFrames.
331 * In either case it is assumed that stateCode and eventCode
332 * indicate which event handler is to be called and that ehArgs
333 * points to the event handler argument list.
334 */
335 active = 1;
336 except = null;
337 try {
338 instance.CallSEH ();
339 } catch (Exception e) {
340 except = e;
341 }
342
343 /*
344 * We now want to return to the script engine.
345 * Setting active = 0 means the microthread has exited.
346 * We need to call engstack.Restore() in case the script called Hiber()
347 * anywhere, we want to return out the corresponding Restore() and not the
348 * Start().
349 */
350 active = 0;
351 instance.engstack.Restore (LOADENGINESTACK);
352 throw new Exception ("returned from Restore()");
353 }
354
355 /*
356 * Script called Hiber() somewhere so just return back out.
357 */
358 case LOADENGINESTACK: {
359 break;
360 }
361
362 default: throw new Exception ("bad engstack code");
363 }
364
365 return except;
366 }
367
368 public void Dispose ()
369 { }
370
371 /**
372 * @brief Determine if script is active.
373 * Returns: 0: nothing started or has returned
374 * Resume() must not be called
375 * Start() may be called
376 * Hiber() must not be called
377 * -1: thread has called Hiber()
378 * Resume() may be called
379 * Start() may be called
380 * Hiber() must not be called
381 * 1: thread is running
382 * Resume() must not be called
383 * Start() must not be called
384 * Hiber() may be called
385 */
386 public int Active ()
387 {
388 return active;
389 }
390
391 /**
392 * @brief Called by the script wherever it wants to hibernate.
393 * So this means to save the scripts stack in 'instance.scrstack' then
394 * restore the engstack to cause us to return back to the engine.
395 */
396 public void Hiber ()
397 {
398 /*
399 * Save where we are in the script's code in 'instance.scrstack'
400 * so we can wake the script when Resume() is called.
401 */
402 switch (instance.scrstack.Store (SAVESCRIPTSTACK)) {
403
404 /*
405 * Script's stack is now saved in 'instance.scrstack'.
406 * Reload the engine's stack from 'instance.engstack' and jump to it.
407 */
408 case SAVESCRIPTSTACK: {
409 active = -1;
410 instance.engstack.Restore (LOADENGINESTACK);
411 throw new Exception ("returned from Restore()");
412 }
413
414 /*
415 * Resume() was just called and we want to resume executing script code.
416 */
417 case LOADSCRIPTSTACK: {
418 break;
419 }
420
421 default: throw new Exception ("bad scrstack code");
422 }
423 }
424
425 /**
426 * @brief We now want to run some more script code from where it last hibernated
427 * until it either finishes the script event handler or until the script
428 * calls Hiber() again.
429 */
430 public Exception ResumeEx ()
431 {
432 /*
433 * Save where we are in the engine's code in 'instance.engstack'
434 * so if the script calls Hiber() again or exits, we know how to get
435 * back to the engine.
436 */
437 switch (instance.engstack.Store (SAVEENGINESTACK)) {
438
439 /*
440 * This is original call to Resume() from the engine,
441 * jump to where we left off within Hiber().
442 */
443 case SAVEENGINESTACK: {
444 active = 1;
445 instance.scrstack.Restore (LOADSCRIPTSTACK);
446 throw new Exception ("returned from Restore()");
447 }
448
449 /*
450 * Script has called Hiber() again, so return back to
451 * script engine code.
452 */
453 case LOADENGINESTACK: {
454 break;
455 }
456
457 default: throw new Exception ("bad engstack code");
458 }
459
460 return except;
461 }
462
463 /**
464 * @brief Number of remaining stack bytes.
465 */
466 public int StackLeft ()
467 {
468 return 0x7FFFFFFF;
469 }
470 }
471}
472
473
474
475/***********************************\
476 * Use Mono.Tasklets.MMRUThreads *
477 * - switches stack pointer *
478\***********************************/
479
480namespace OpenSim.Region.ScriptEngine.XMREngine {
481
482 public class ScriptUThread_MMR : IScriptUThread, IDisposable
483 {
484 private static Exception uthread_looked;
485 private static Type uttype;
486 private static Type uthread_entry;
487 private static MethodInfo uthread_dispose;
488 private static MethodInfo uthread_startex;
489 private static MethodInfo uthread_resumex;
490 private static MethodInfo uthread_suspend;
491 private static MethodInfo uthread_active;
492 private static MethodInfo uthread_stackleft;
493
494 public static Exception LoadMono ()
495 {
496 if ((uthread_looked == null) && (uthread_stackleft == null)) {
497 try {
498 Assembly mt = Assembly.Load ("Mono.Tasklets");
499 uttype = mt.GetType ("Mono.Tasklets.MMRUThread", true);
500 uthread_entry = mt.GetType ("Mono.Tasklets.MMRUThread+Entry", true);
501
502 uthread_dispose = uttype.GetMethod ("Dispose"); // no parameters, no return value
503 uthread_startex = uttype.GetMethod ("StartEx"); // takes uthread_entry delegate as parameter, returns exception
504 uthread_resumex = uttype.GetMethod ("ResumeEx"); // takes exception as parameter, returns exception
505 uthread_suspend = uttype.GetMethod ("Suspend", new Type[] { }); // no return value
506 uthread_active = uttype.GetMethod ("Active"); // no parameters, returns int
507 uthread_stackleft = uttype.GetMethod ("StackLeft"); // no parameters, returns IntPtr
508 } catch (Exception e) {
509 uthread_looked = new NotSupportedException ("'mmr' thread model requires patched mono", e);
510 }
511 }
512 return uthread_looked;
513 }
514
515 private static object[] resumex_args = new object[] { null };
516
517 private object uthread; // type MMRUThread
518 private object[] startex_args = new object[1];
519
520 public ScriptUThread_MMR (XMRInstance instance)
521 {
522 this.uthread = Activator.CreateInstance (uttype, new object[] { (IntPtr) instance.m_StackSize, instance.m_DescName });
523 startex_args[0] = Delegate.CreateDelegate (uthread_entry, instance, "CallSEH");
524 }
525
526 public void Dispose ()
527 {
528 uthread_dispose.Invoke (uthread, null);
529 uthread = null;
530 }
531
532 public Exception StartEx ()
533 {
534 return (Exception) uthread_startex.Invoke (uthread, startex_args);
535 }
536
537 public Exception ResumeEx ()
538 {
539 return (Exception) uthread_resumex.Invoke (uthread, resumex_args);
540 }
541
542 public void Hiber ()
543 {
544 uthread_suspend.Invoke (null, null);
545 }
546
547 public int Active ()
548 {
549 return (int) uthread_active.Invoke (uthread, null);
550 }
551
552 public int StackLeft ()
553 {
554 return (int) (IntPtr) uthread_stackleft.Invoke (null, null);
555 }
556 }
557}
diff --git a/prebuild.xml b/prebuild.xml
index 29d13f7..64236f9 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -2418,6 +2418,73 @@
2418 </Files> 2418 </Files>
2419 </Project> 2419 </Project>
2420 2420
2421 <!-- XMRengine -->
2422 <Project frameworkVersion="v4_6" name="Mono.Tasklets" path="OpenSim/Region/ScriptEngine/XMREngine" type="Library">
2423 <Configuration name="Debug">
2424 <Options>
2425 <OutputPath>../../../../bin/</OutputPath>
2426 </Options>
2427 </Configuration>
2428 <Configuration name="Release">
2429 <Options>
2430 <OutputPath>../../../../bin/</OutputPath>
2431 </Options>
2432 </Configuration>
2433
2434 <ReferencePath>../../../../bin/</ReferencePath>
2435
2436 <Files>
2437 <Match pattern="MonoTaskletsDummy.cs" recurse="true">
2438 <Exclude name="obj" pattern="obj"/>
2439 </Match>
2440 </Files>
2441 </Project>
2442
2443 <Project frameworkVersion="v4_6" name="OpenSim.Region.ScriptEngine.XMREngine" path="OpenSim/Region/ScriptEngine/XMREngine" type="Library">
2444 <Configuration name="Debug">
2445 <Options>
2446 <OutputPath>../../../../bin/</OutputPath>
2447 </Options>
2448 </Configuration>
2449 <Configuration name="Release">
2450 <Options>
2451 <OutputPath>../../../../bin/</OutputPath>
2452 </Options>
2453 </Configuration>
2454
2455 <ReferencePath>../../../../bin/</ReferencePath>
2456 <Reference name="log4net"/>
2457 <Reference name="Mono.Addins"/>
2458 <Reference name="Mono.Tasklets"/>
2459 <Reference name="Nini"/>
2460 <Reference name="OpenMetaverse"/>
2461 <Reference name="OpenMetaverse.StructuredData"/>
2462 <Reference name="OpenMetaverseTypes"/>
2463 <Reference name="OpenSim.Framework"/>
2464 <Reference name="OpenSim.Framework.Console"/>
2465 <Reference name="OpenSim.Framework.Monitoring"/>
2466 <Reference name="OpenSim.Region.ClientStack.LindenCaps"/>
2467 <Reference name="OpenSim.Region.CoreModules"/>
2468 <Reference name="OpenSim.Region.Framework"/>
2469 <Reference name="OpenSim.Region.ScriptEngine.Shared"/>
2470 <Reference name="OpenSim.Region.ScriptEngine.Shared.Api"/>
2471 <Reference name="OpenSim.Region.ScriptEngine.Shared.Api.Runtime"/>
2472 <Reference name="OpenSim.Services.Interfaces"/>
2473 <Reference name="System"/>
2474 <Reference name="System.Drawing"/>
2475 <Reference name="System.Xml"/>
2476
2477 <Files>
2478 <Match pattern="MMR*.cs" recurse="true">
2479 <Exclude name="obj" pattern="obj"/>
2480 </Match>
2481 <Match pattern="XMR*.cs" recurse="true">
2482 <Exclude name="obj" pattern="obj"/>
2483 </Match>
2484 </Files>
2485</Project>
2486
2487
2421 <!-- Addons --> 2488 <!-- Addons -->
2422 2489
2423 <Project frameworkVersion="v4_6" name="OpenSim.Addons.OfflineIM" path="OpenSim/Addons/OfflineIM" type="Library"> 2490 <Project frameworkVersion="v4_6" name="OpenSim.Addons.OfflineIM" path="OpenSim/Addons/OfflineIM" type="Library">