diff options
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using System.Reflection.Emit; | ||
32 | |||
33 | namespace 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 | |||
28 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
29 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
30 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
31 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
32 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
33 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
34 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
35 | |||
36 | namespace 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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Reflection; | ||
32 | |||
33 | namespace 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 | |||
28 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
29 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.IO; | ||
33 | using System.Reflection; | ||
34 | using System.Reflection.Emit; | ||
35 | using System.Text; | ||
36 | using System.Text.RegularExpressions; | ||
37 | |||
38 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
39 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
40 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
41 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
42 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
43 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
44 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
45 | |||
46 | namespace 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 | |||
28 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
29 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.IO; | ||
33 | using System.Reflection; | ||
34 | using System.Reflection.Emit; | ||
35 | using System.Runtime.Serialization; | ||
36 | using System.Text; | ||
37 | using System.Threading; | ||
38 | |||
39 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
40 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
41 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
42 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
43 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
44 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
45 | using 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 | |||
52 | namespace 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 | |||
28 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
29 | using System; | ||
30 | using System.Collections.Generic; | ||
31 | using System.IO; | ||
32 | using System.Reflection; | ||
33 | using System.Reflection.Emit; | ||
34 | using 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 | |||
44 | namespace 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 | |||
28 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
29 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.IO; | ||
33 | using System.Reflection; | ||
34 | using System.Reflection.Emit; | ||
35 | |||
36 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
37 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
38 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
39 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
40 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
41 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
42 | using 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 | |||
81 | namespace 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 | |||
32 | using System; | ||
33 | using System.IO; | ||
34 | using System.IO.Compression; | ||
35 | using System.Reflection; | ||
36 | using System.Security.Cryptography; | ||
37 | using System.Text; | ||
38 | |||
39 | namespace 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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using System.Text; | ||
32 | |||
33 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
34 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
35 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
36 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
37 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
38 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
39 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
40 | |||
41 | namespace 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 | |||
28 | namespace 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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using System.Reflection.Emit; | ||
32 | using System.Text; | ||
33 | |||
34 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
35 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
36 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
37 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
38 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
39 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
40 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
41 | |||
42 | /** | ||
43 | * @brief Generate code for the backend API calls. | ||
44 | */ | ||
45 | namespace 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 | |||
28 | using System; | ||
29 | using System.Reflection; | ||
30 | using System.Reflection.Emit; | ||
31 | |||
32 | namespace 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 | |||
28 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
29 | using System; | ||
30 | using System.Collections.Generic; | ||
31 | using System.IO; | ||
32 | using System.Reflection; | ||
33 | using System.Reflection.Emit; | ||
34 | |||
35 | namespace 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 | |||
28 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
29 | using System; | ||
30 | using System.Collections.Generic; | ||
31 | using System.IO; | ||
32 | using System.Reflection; | ||
33 | using System.Reflection.Emit; | ||
34 | using System.Text; | ||
35 | |||
36 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
37 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
38 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
39 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
40 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
41 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
42 | using 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 | */ | ||
49 | namespace 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 | |||
43 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
44 | using System; | ||
45 | using System.Collections.Generic; | ||
46 | using System.IO; | ||
47 | using System.Reflection; | ||
48 | using System.Reflection.Emit; | ||
49 | using System.Text; | ||
50 | |||
51 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
52 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
53 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
54 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
55 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
56 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
57 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
58 | |||
59 | namespace 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 | |||
44 | using System; | ||
45 | using System.Collections.Generic; | ||
46 | using System.IO; | ||
47 | using System.Net; | ||
48 | using System.Reflection; | ||
49 | using System.Reflection.Emit; | ||
50 | using System.Text; | ||
51 | |||
52 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
53 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
54 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
55 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
56 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
57 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
58 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
59 | |||
60 | namespace 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 | |||
28 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
29 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Reflection; | ||
33 | using System.Reflection.Emit; | ||
34 | |||
35 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
36 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
37 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
38 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
39 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
40 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
41 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
42 | |||
43 | /** | ||
44 | * @brief Generate script object code to perform type casting | ||
45 | */ | ||
46 | |||
47 | namespace 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 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | |||
32 | /** | ||
33 | * @brief Collection of variable/function/method definitions | ||
34 | */ | ||
35 | |||
36 | namespace 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 | |||
32 | using System; | ||
33 | using System.IO; | ||
34 | using System.Net; | ||
35 | using System.Text; | ||
36 | |||
37 | namespace 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 | |||
32 | using System; | ||
33 | |||
34 | namespace 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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Text; | ||
32 | |||
33 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
34 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
35 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
36 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
37 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
38 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
39 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
40 | |||
41 | // This class exists in the main app domain | ||
42 | // | ||
43 | namespace 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 | |||
28 | using log4net; | ||
29 | using System; | ||
30 | using System.Collections.Generic; | ||
31 | using System.IO; | ||
32 | using System.Reflection; | ||
33 | using System.Text; | ||
34 | using System.Threading; | ||
35 | |||
36 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
37 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
38 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
39 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
40 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
41 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
42 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
43 | |||
44 | namespace 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 | |||
28 | using log4net; | ||
29 | using Mono.Addins; | ||
30 | using Nini.Config; | ||
31 | using OpenSim.Framework; | ||
32 | using OpenSim.Framework.Console; | ||
33 | using OpenSim.Framework.Monitoring; | ||
34 | using OpenSim.Region.ClientStack.Linden; | ||
35 | using OpenSim.Region.Framework.Interfaces; | ||
36 | using OpenSim.Region.Framework.Scenes; | ||
37 | using OpenSim.Region.ScriptEngine.Interfaces; | ||
38 | using OpenSim.Region.ScriptEngine.Shared; | ||
39 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
40 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
41 | using OpenMetaverse; | ||
42 | using System; | ||
43 | using System.Collections; | ||
44 | using System.Collections.Generic; | ||
45 | using System.IO; | ||
46 | using System.Reflection; | ||
47 | using System.Reflection.Emit; | ||
48 | using System.Text; | ||
49 | using System.Threading; | ||
50 | using System.Timers; | ||
51 | using System.Xml; | ||
52 | |||
53 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
54 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
55 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
56 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
57 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
58 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
59 | using 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 | |||
64 | namespace 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 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Reflection; | ||
32 | using OpenMetaverse; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Region.Framework.Scenes; | ||
35 | using OpenSim.Region.Framework.Interfaces; | ||
36 | using OpenSim.Region.ScriptEngine.Shared; | ||
37 | using OpenSim.Region.ScriptEngine.Interfaces; | ||
38 | using log4net; | ||
39 | |||
40 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
41 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
42 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
43 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
44 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
45 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
46 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
47 | |||
48 | namespace 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 | |||
28 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
29 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.IO; | ||
33 | using System.Reflection; | ||
34 | using System.Reflection.Emit; | ||
35 | |||
36 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
37 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
38 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
39 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
40 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
41 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
42 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
43 | |||
44 | namespace 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 | |||
28 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
29 | using System; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Globalization; | ||
32 | using System.IO; | ||
33 | using System.Reflection.Emit; | ||
34 | using System.Runtime.Serialization; | ||
35 | using System.Text; | ||
36 | using System.Threading; | ||
37 | |||
38 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
39 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
40 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
41 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
42 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
43 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
44 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
45 | |||
46 | namespace 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 | |||
28 | using System; | ||
29 | using System.Threading; | ||
30 | using System.Reflection; | ||
31 | using System.Collections; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Runtime.Remoting.Lifetime; | ||
34 | using System.Security.Policy; | ||
35 | using System.IO; | ||
36 | using System.Xml; | ||
37 | using System.Text; | ||
38 | using OpenMetaverse; | ||
39 | using OpenSim.Framework; | ||
40 | using OpenSim.Region.ScriptEngine.Interfaces; | ||
41 | using OpenSim.Region.ScriptEngine.Shared; | ||
42 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
43 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
44 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
45 | using OpenSim.Region.Framework.Scenes; | ||
46 | using OpenSim.Region.Framework.Scenes.Scripting; | ||
47 | using OpenSim.Region.Framework.Interfaces; | ||
48 | using log4net; | ||
49 | |||
50 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
51 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
52 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
53 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
54 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
55 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
56 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
57 | |||
58 | namespace 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 | |||
28 | using System; | ||
29 | using System.Threading; | ||
30 | using System.Reflection; | ||
31 | using System.Collections; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Runtime.Remoting.Lifetime; | ||
34 | using System.Security.Policy; | ||
35 | using System.IO; | ||
36 | using System.Xml; | ||
37 | using System.Text; | ||
38 | using OpenMetaverse; | ||
39 | using OpenSim.Framework; | ||
40 | using OpenSim.Region.ScriptEngine.Interfaces; | ||
41 | using OpenSim.Region.ScriptEngine.Shared; | ||
42 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
43 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
44 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
45 | using OpenSim.Region.Framework.Scenes; | ||
46 | using log4net; | ||
47 | |||
48 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
49 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
50 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
51 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
52 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
53 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
54 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
55 | |||
56 | namespace 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 | |||
28 | using System; | ||
29 | using System.Threading; | ||
30 | using System.Reflection; | ||
31 | using System.Collections; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Runtime.Remoting.Lifetime; | ||
34 | using System.Security.Policy; | ||
35 | using System.IO; | ||
36 | using System.Xml; | ||
37 | using System.Text; | ||
38 | using OpenMetaverse; | ||
39 | using OpenSim.Framework; | ||
40 | using OpenSim.Region.ScriptEngine.Interfaces; | ||
41 | using OpenSim.Region.ScriptEngine.Shared; | ||
42 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
43 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
44 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
45 | using OpenSim.Region.Framework.Scenes; | ||
46 | using log4net; | ||
47 | |||
48 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
49 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
50 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
51 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
52 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
53 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
54 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
55 | |||
56 | namespace 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 | |||
28 | using System; | ||
29 | using System.Threading; | ||
30 | using System.Reflection; | ||
31 | using System.Collections; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Runtime.Remoting.Lifetime; | ||
34 | using System.Security.Policy; | ||
35 | using System.IO; | ||
36 | using System.Xml; | ||
37 | using System.Text; | ||
38 | using OpenMetaverse; | ||
39 | using OpenSim.Framework; | ||
40 | using OpenSim.Region.ScriptEngine.Interfaces; | ||
41 | using OpenSim.Region.ScriptEngine.Shared; | ||
42 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
43 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
44 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
45 | using OpenSim.Region.Framework.Scenes; | ||
46 | using log4net; | ||
47 | |||
48 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
49 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
50 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
51 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
52 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
53 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
54 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
55 | |||
56 | // This class exists in the main app domain | ||
57 | // | ||
58 | namespace 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 | |||
28 | using System; | ||
29 | using System.Threading; | ||
30 | using System.Reflection; | ||
31 | using System.Collections; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Reflection.Emit; | ||
34 | using System.Runtime.Remoting.Lifetime; | ||
35 | using System.Security.Policy; | ||
36 | using System.IO; | ||
37 | using System.Xml; | ||
38 | using System.Text; | ||
39 | using OpenMetaverse; | ||
40 | using OpenSim.Framework; | ||
41 | using OpenSim.Region.ScriptEngine.Interfaces; | ||
42 | using OpenSim.Region.ScriptEngine.Shared; | ||
43 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
44 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
45 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
46 | using OpenSim.Region.Framework.Scenes; | ||
47 | using log4net; | ||
48 | |||
49 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
50 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
51 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
52 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
53 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
54 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
55 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
56 | |||
57 | // This class exists in the main app domain | ||
58 | // | ||
59 | namespace 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 | |||
28 | using System; | ||
29 | |||
30 | namespace 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 | |||
28 | using System; | ||
29 | using System.Threading; | ||
30 | using System.Reflection; | ||
31 | using System.Collections; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Reflection.Emit; | ||
34 | using System.Runtime.Remoting.Lifetime; | ||
35 | using System.Security.Policy; | ||
36 | using System.IO; | ||
37 | using System.Xml; | ||
38 | using System.Text; | ||
39 | using OpenMetaverse; | ||
40 | using OpenSim.Framework; | ||
41 | using OpenSim.Region.Framework.Interfaces; | ||
42 | using OpenSim.Region.ScriptEngine.Interfaces; | ||
43 | using OpenSim.Region.ScriptEngine.Shared; | ||
44 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
45 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
46 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
47 | using OpenSim.Region.Framework.Scenes; | ||
48 | using log4net; | ||
49 | |||
50 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
51 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
52 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
53 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
54 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
55 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
56 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
57 | |||
58 | namespace 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 | |||
28 | using OpenMetaverse; | ||
29 | using OpenSim.Framework; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | using OpenSim.Region.ScriptEngine.Shared; | ||
32 | using System; | ||
33 | |||
34 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
35 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
36 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
37 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
38 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
39 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
40 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
41 | |||
42 | namespace 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 | |||
28 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
29 | using System; | ||
30 | using System.Collections.Generic; | ||
31 | using System.IO; | ||
32 | using System.Reflection; | ||
33 | using System.Reflection.Emit; | ||
34 | using System.Text; | ||
35 | |||
36 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
37 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
38 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
39 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
40 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
41 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
42 | using 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 | |||
49 | namespace 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 | |||
28 | using System; | ||
29 | using System.Reflection.Emit; | ||
30 | |||
31 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
32 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
33 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
34 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
35 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
36 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
37 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
38 | |||
39 | namespace 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 | |||
28 | using Mono.Tasklets; | ||
29 | using OpenSim.Framework.Monitoring; | ||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Threading; | ||
33 | |||
34 | namespace 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 | |||
28 | using Mono.Tasklets; | ||
29 | using System; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Reflection; | ||
32 | using System.Threading; | ||
33 | |||
34 | |||
35 | |||
36 | /***************************\ | ||
37 | * Use standard C# code * | ||
38 | * - uses system threads * | ||
39 | \***************************/ | ||
40 | |||
41 | namespace 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 | |||
285 | namespace 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 | |||
480 | namespace 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"> |