Selaa lähdekoodia

add experimental script engine XMRengine donated by mrieker (DreamNation) And our Melanie. ***DANGER*** ***TESTONLY*** ***disable HG*** dont leave running when not looking... tp/crossing to Xengine will reset scripts. i do see a few issues but should be testable, so we can decide if we should invest more on it.

UbitUmarov 6 vuotta sitten
vanhempi
commit
83e2fee71b
40 muutettua tiedostoa jossa 41980 lisäystä ja 13 poistoa
  1. 15 13
      OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs
  2. 106 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRDelegateCommon.cs
  3. 77 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRIEventHandlers.cs
  4. 94 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRInternalFuncDict.cs
  5. 1559 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptBinOpStr.cs
  6. 6262 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs
  7. 2637 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs
  8. 1677 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompValu.cs
  9. 216 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompile.cs
  10. 250 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptConsts.cs
  11. 95 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptEventCode.cs
  12. 664 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs
  13. 81 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptMyILGen.cs
  14. 256 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjCode.cs
  15. 947 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjWriter.cs
  16. 7719 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs
  17. 1729 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTokenize.cs
  18. 819 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTypeCast.cs
  19. 371 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRScriptVarDict.cs
  20. 269 0
      OpenSim/Region/ScriptEngine/XMREngine/MMRWebRequest.cs
  21. 55 0
      OpenSim/Region/ScriptEngine/XMREngine/MonoTaskletsDummy.cs
  22. 534 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs
  23. 491 0
      OpenSim/Region/ScriptEngine/XMREngine/XMREngXmrTestLs.cs
  24. 1979 0
      OpenSim/Region/ScriptEngine/XMREngine/XMREngine.cs
  25. 369 0
      OpenSim/Region/ScriptEngine/XMREngine/XMREvents.cs
  26. 172 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRHeapTracker.cs
  27. 2031 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRInstAbstract.cs
  28. 644 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs
  29. 436 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs
  30. 878 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs
  31. 230 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRInstMain.cs
  32. 384 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRInstMisc.cs
  33. 186 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRInstQueue.cs
  34. 1051 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs
  35. 76 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRInstSorpra.cs
  36. 5476 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRObjectTokens.cs
  37. 259 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRSDTypeClObj.cs
  38. 262 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRScriptThread.cs
  39. 557 0
      OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs
  40. 67 0
      prebuild.xml

+ 15 - 13
OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs

@@ -284,22 +284,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
                 // Remove from: Timers
                 m_Timer[engine].UnSetTimerEvents(localID, itemID);
 
-                // Remove from: HttpRequest
-                IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface<IHttpRequestModule>();
-                if (iHttpReq != null)
-                    iHttpReq.StopHttpRequest(localID, itemID);
+                if(engine.World != null)
+                {
+                    // Remove from: HttpRequest
+                    IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface<IHttpRequestModule>();
+                    if (iHttpReq != null)
+                        iHttpReq.StopHttpRequest(localID, itemID);
 
-                IWorldComm comms = engine.World.RequestModuleInterface<IWorldComm>();
-                if (comms != null)
-                    comms.DeleteListener(itemID);
+                    IWorldComm comms = engine.World.RequestModuleInterface<IWorldComm>();
+                    if (comms != null)
+                        comms.DeleteListener(itemID);
 
-                IXMLRPC xmlrpc = engine.World.RequestModuleInterface<IXMLRPC>();
-                if (xmlrpc != null)
-                {
-                    xmlrpc.DeleteChannels(itemID);
-                    xmlrpc.CancelSRDRequests(itemID);
+                    IXMLRPC xmlrpc = engine.World.RequestModuleInterface<IXMLRPC>();
+                    if (xmlrpc != null)
+                    {
+                        xmlrpc.DeleteChannels(itemID);
+                        xmlrpc.CancelSRDRequests(itemID);
+                    }
                 }
-
                 // Remove Sensors
                 m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID);
             }

+ 106 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRDelegateCommon.cs

@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    public class DelegateCommon {
+        private string sig;  // rettype(arg1type,arg2type,...), eg, "void(list,string,integer)"
+        private Type type;   // resultant delegate type
+
+        private static Dictionary<string, DelegateCommon> delegateCommons = new Dictionary<string, DelegateCommon> ();
+        private static Dictionary<Type, DelegateCommon> delegateCommonsBySysType = new Dictionary<Type, DelegateCommon> ();
+        private static ModuleBuilder delegateModuleBuilder = null;
+        public  static Type[] constructorArgTypes = new Type[] { typeof (object), typeof (IntPtr) };
+
+        private DelegateCommon () { }
+
+        public static Type GetType (System.Type ret, System.Type[] args, string sig)
+        {
+            DelegateCommon dc;
+            lock (delegateCommons) {
+                if (!delegateCommons.TryGetValue (sig, out dc)) {
+                    dc = new DelegateCommon ();
+                    dc.sig  = sig;
+                    dc.type = CreateDelegateType (sig, ret, args);
+                    delegateCommons.Add (sig, dc);
+                    delegateCommonsBySysType.Add (dc.type, dc);
+                }
+            }
+            return dc.type;
+        }
+
+        public static Type TryGetType (string sig)
+        {
+            DelegateCommon dc;
+            lock (delegateCommons) {
+                if (!delegateCommons.TryGetValue (sig, out dc)) dc = null;
+            }
+            return (dc == null) ? null : dc.type;
+        }
+
+        public static string TryGetName (Type t)
+        {
+            DelegateCommon dc;
+            lock (delegateCommons) {
+                if (!delegateCommonsBySysType.TryGetValue (t, out dc)) dc = null;
+            }
+            return (dc == null) ? null : dc.sig;
+        }
+
+        // http://blog.bittercoder.com/PermaLink,guid,a770377a-b1ad-4590-9145-36381757a52b.aspx
+        private static Type CreateDelegateType (string name, Type retType, Type[] argTypes)
+        {
+            if (delegateModuleBuilder == null) {
+                AssemblyName assembly = new AssemblyName();
+                assembly.Name = "CustomDelegateAssembly";
+                AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run);
+                delegateModuleBuilder = assemblyBuilder.DefineDynamicModule("CustomDelegateModule");
+            }
+
+            TypeBuilder typeBuilder = delegateModuleBuilder.DefineType(name, 
+                TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class |
+                TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof (MulticastDelegate));
+
+            ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(
+                MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
+                CallingConventions.Standard, constructorArgTypes);
+            constructorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
+
+            MethodBuilder methodBuilder = typeBuilder.DefineMethod("Invoke",
+                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot |
+                MethodAttributes.Virtual, retType, argTypes);
+            methodBuilder.SetImplementationFlags(MethodImplAttributes.Managed | MethodImplAttributes.Runtime);
+
+            return typeBuilder.CreateType();
+        }
+    }
+}

+ 77 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRIEventHandlers.cs

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public interface IEventHandlers {
+        void at_rot_target (int tnum, LSL_Rotation targetrot, LSL_Rotation ourrot);
+        void at_target (int tnum, LSL_Vector targetpos, LSL_Vector ourpos);
+        void attach (string id);
+        void changed (int change);
+        void collision (int num_detected);
+        void collision_end (int num_detected);
+        void collision_start (int num_detected);
+        void control (string id, int held, int change);
+        void dataserver (string queryid, string data);
+        void email (string time, string address, string subj, string message, int num_left);
+        void http_request (string request_id, string method, string body);
+        void http_response (string request_id, int status, LSL_List metadata, string body);
+        void land_collision (LSL_Vector pos);
+        void land_collision_end (LSL_Vector pos);
+        void land_collision_start (LSL_Vector pos);
+        void link_message (int sender_num, int num, string str, string id);
+        void listen (int channel, string name, string id, string message);
+        void money (string id, int amount);
+        void moving_end ();
+        void moving_start ();
+        void no_sensor ();
+        void not_at_rot_target ();
+        void not_at_target ();
+        void object_rez (string id);
+        void on_rez (int start_param);
+        void remote_data (int event_type, string channel, string message_id, string sender, int idata, string sdata);
+        void run_time_permissions (int perm);
+        void sensor (int num_detected);
+        void state_entry ();
+        void state_exit ();
+        void timer ();
+        void touch (int num_detected);
+        void touch_start (int num_detected);
+        void touch_end (int num_detected);
+        void transaction_result(string id, int success, string data);
+        void path_update(int type, LSL_List data);
+        void region_cross(LSL_Vector newpos, LSL_Vector oldpos);
+    }
+}

+ 94 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRInternalFuncDict.cs

@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    public class InternalFuncDict : VarDict {
+
+        /**
+         * @brief build dictionary of internal functions from an interface.
+         * @param iface = interface with function definitions
+         * @param inclSig = true: catalog by name with arg sig, eg, llSay(integer,string)
+         *                 false: catalog by simple name only, eg, state_entry
+         * @returns dictionary of function definition tokens
+         */
+        public InternalFuncDict (Type iface, bool inclSig)
+            : base (false)
+        {
+            /*
+             * Loop through list of all methods declared in the interface.
+             */
+            System.Reflection.MethodInfo[] ifaceMethods = iface.GetMethods ();
+            foreach (System.Reflection.MethodInfo ifaceMethod in ifaceMethods) {
+                string key = ifaceMethod.Name;
+
+                /*
+                 * Only do ones that begin with lower-case letters...
+                 * as any others can't be referenced by scripts
+                 */
+                if ((key[0] < 'a') || (key[0] > 'z')) continue;
+
+                try {
+
+                    /*
+                     * Create a corresponding TokenDeclVar struct.
+                     */
+                    System.Reflection.ParameterInfo[] parameters = ifaceMethod.GetParameters ();
+                    TokenArgDecl argDecl = new TokenArgDecl (null);
+                    for (int i = 0; i < parameters.Length; i++) {
+                        System.Reflection.ParameterInfo param = parameters[i];
+                        TokenType type = TokenType.FromSysType (null, param.ParameterType);
+                        TokenName name = new TokenName (null, param.Name);
+                        argDecl.AddArg (type, name);
+                    }
+                    TokenDeclVar declFunc = new TokenDeclVar (null, null, null);
+                    declFunc.name         = new TokenName (null, key);
+                    declFunc.retType      = TokenType.FromSysType (null, ifaceMethod.ReturnType);
+                    declFunc.argDecl      = argDecl;
+
+                    /*
+                     * Add the TokenDeclVar struct to the dictionary.
+                     */
+                    this.AddEntry (declFunc);
+                } catch (Exception except) {
+
+                    string msg = except.ToString ();
+                    int i = msg.IndexOf ("\n");
+                    if (i > 0) msg = msg.Substring (0, i);
+                    Console.WriteLine ("InternalFuncDict*: {0}:     {1}", key, msg);
+
+                    ///??? IGNORE ANY THAT FAIL - LIKE UNRECOGNIZED TYPE ???///
+                }
+            }
+        }
+    }
+}

+ 1559 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptBinOpStr.cs

@@ -0,0 +1,1559 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using System.Text.RegularExpressions;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    /**
+     * @brief This class is used to catalog the code emit routines based on a key string
+     *        The key string has the two types (eg, "integer", "rotation") and the operator (eg, "*", "!=")
+     */
+    public delegate void BinOpStrEmitBO (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result);
+    public class BinOpStr {
+        public static readonly Dictionary<string, BinOpStr> defined = DefineBinOps ();
+
+        public Type outtype;           // type of result of computation
+        public BinOpStrEmitBO emitBO;  // how to compute result
+        public bool rmwOK;             // is the <operator>= form valid?
+
+        public BinOpStr (Type outtype, BinOpStrEmitBO emitBO)
+        {
+            this.outtype = outtype;
+            this.emitBO  = emitBO;
+            this.rmwOK   = false;
+        }
+
+        public BinOpStr (Type outtype, BinOpStrEmitBO emitBO, bool rmwOK)
+        {
+            this.outtype = outtype;
+            this.emitBO  = emitBO;
+            this.rmwOK   = rmwOK;
+        }
+
+        private static TokenTypeBool   tokenTypeBool   = new TokenTypeBool   (null);
+        private static TokenTypeChar   tokenTypeChar   = new TokenTypeChar   (null);
+        private static TokenTypeFloat  tokenTypeFloat  = new TokenTypeFloat  (null);
+        private static TokenTypeInt    tokenTypeInt    = new TokenTypeInt    (null);
+        private static TokenTypeList   tokenTypeList   = new TokenTypeList   (null);
+        private static TokenTypeRot    tokenTypeRot    = new TokenTypeRot    (null);
+        private static TokenTypeStr    tokenTypeStr    = new TokenTypeStr    (null);
+        private static TokenTypeVec    tokenTypeVec    = new TokenTypeVec    (null);
+
+        private static MethodInfo stringAddStringMethInfo = ScriptCodeGen.GetStaticMethod (typeof (string), "Concat",  new Type[] { typeof (string), typeof (string) });
+        private static MethodInfo stringCmpStringMethInfo = ScriptCodeGen.GetStaticMethod (typeof (string), "Compare", new Type[] { typeof (string), typeof (string) });
+
+        private static MethodInfo infoMethListAddFloat = GetBinOpsMethod ("MethListAddFloat", new Type[] { typeof (LSL_List),     typeof (double)  });
+        private static MethodInfo infoMethListAddInt   = GetBinOpsMethod ("MethListAddInt",   new Type[] { typeof (LSL_List),     typeof (int)          });
+        private static MethodInfo infoMethListAddKey   = GetBinOpsMethod ("MethListAddKey",   new Type[] { typeof (LSL_List),     typeof (string)       });
+        private static MethodInfo infoMethListAddRot   = GetBinOpsMethod ("MethListAddRot",   new Type[] { typeof (LSL_List),     typeof (LSL_Rotation) });
+        private static MethodInfo infoMethListAddStr   = GetBinOpsMethod ("MethListAddStr",   new Type[] { typeof (LSL_List),     typeof (string)       });
+        private static MethodInfo infoMethListAddVec   = GetBinOpsMethod ("MethListAddVec",   new Type[] { typeof (LSL_List),     typeof (LSL_Vector)   });
+        private static MethodInfo infoMethListAddList  = GetBinOpsMethod ("MethListAddList",  new Type[] { typeof (LSL_List),     typeof (LSL_List)     });
+        private static MethodInfo infoMethFloatAddList = GetBinOpsMethod ("MethFloatAddList", new Type[] { typeof (double),  typeof (LSL_List)     });
+        private static MethodInfo infoMethIntAddList   = GetBinOpsMethod ("MethIntAddList",   new Type[] { typeof (int),          typeof (LSL_List)     });
+        private static MethodInfo infoMethKeyAddList   = GetBinOpsMethod ("MethKeyAddList",   new Type[] { typeof (string),       typeof (LSL_List)     });
+        private static MethodInfo infoMethRotAddList   = GetBinOpsMethod ("MethRotAddList",   new Type[] { typeof (LSL_Rotation), typeof (LSL_List)     });
+        private static MethodInfo infoMethStrAddList   = GetBinOpsMethod ("MethStrAddList",   new Type[] { typeof (string),       typeof (LSL_List)     });
+        private static MethodInfo infoMethVecAddList   = GetBinOpsMethod ("MethVecAddList",   new Type[] { typeof (LSL_Vector),   typeof (LSL_List)     });
+        private static MethodInfo infoMethListEqList   = GetBinOpsMethod ("MethListEqList",   new Type[] { typeof (LSL_List),     typeof (LSL_List)     });
+        private static MethodInfo infoMethListNeList   = GetBinOpsMethod ("MethListNeList",   new Type[] { typeof (LSL_List),     typeof (LSL_List)     });
+        private static MethodInfo infoMethRotEqRot     = GetBinOpsMethod ("MethRotEqRot",     new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
+        private static MethodInfo infoMethRotNeRot     = GetBinOpsMethod ("MethRotNeRot",     new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
+        private static MethodInfo infoMethRotAddRot    = GetBinOpsMethod ("MethRotAddRot",    new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
+        private static MethodInfo infoMethRotSubRot    = GetBinOpsMethod ("MethRotSubRot",    new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
+        private static MethodInfo infoMethRotMulRot    = GetBinOpsMethod ("MethRotMulRot",    new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
+        private static MethodInfo infoMethRotDivRot    = GetBinOpsMethod ("MethRotDivRot",    new Type[] { typeof (LSL_Rotation), typeof (LSL_Rotation) });
+        private static MethodInfo infoMethVecEqVec     = GetBinOpsMethod ("MethVecEqVec",     new Type[] { typeof (LSL_Vector),   typeof (LSL_Vector)   });
+        private static MethodInfo infoMethVecNeVec     = GetBinOpsMethod ("MethVecNeVec",     new Type[] { typeof (LSL_Vector),   typeof (LSL_Vector)   });
+        private static MethodInfo infoMethVecAddVec    = GetBinOpsMethod ("MethVecAddVec",    new Type[] { typeof (LSL_Vector),   typeof (LSL_Vector)   });
+        private static MethodInfo infoMethVecSubVec    = GetBinOpsMethod ("MethVecSubVec",    new Type[] { typeof (LSL_Vector),   typeof (LSL_Vector)   });
+        private static MethodInfo infoMethVecMulVec    = GetBinOpsMethod ("MethVecMulVec",    new Type[] { typeof (LSL_Vector),   typeof (LSL_Vector)   });
+        private static MethodInfo infoMethVecModVec    = GetBinOpsMethod ("MethVecModVec",    new Type[] { typeof (LSL_Vector),   typeof (LSL_Vector)   });
+        private static MethodInfo infoMethVecMulFloat  = GetBinOpsMethod ("MethVecMulFloat",  new Type[] { typeof (LSL_Vector),   typeof (double)  });
+        private static MethodInfo infoMethFloatMulVec  = GetBinOpsMethod ("MethFloatMulVec",  new Type[] { typeof (double),  typeof (LSL_Vector)   });
+        private static MethodInfo infoMethVecDivFloat  = GetBinOpsMethod ("MethVecDivFloat",  new Type[] { typeof (LSL_Vector),   typeof (double)  });
+        private static MethodInfo infoMethVecMulInt    = GetBinOpsMethod ("MethVecMulInt",    new Type[] { typeof (LSL_Vector),   typeof (int)          });
+        private static MethodInfo infoMethIntMulVec    = GetBinOpsMethod ("MethIntMulVec",    new Type[] { typeof (int),          typeof (LSL_Vector)   });
+        private static MethodInfo infoMethVecDivInt    = GetBinOpsMethod ("MethVecDivInt",    new Type[] { typeof (LSL_Vector),   typeof (int)          });
+        private static MethodInfo infoMethVecMulRot    = GetBinOpsMethod ("MethVecMulRot",    new Type[] { typeof (LSL_Vector),   typeof (LSL_Rotation) });
+        private static MethodInfo infoMethVecDivRot    = GetBinOpsMethod ("MethVecDivRot",    new Type[] { typeof (LSL_Vector),   typeof (LSL_Rotation) });
+
+        private static MethodInfo GetBinOpsMethod (string name, Type[] types)
+        {
+            return ScriptCodeGen.GetStaticMethod (typeof (BinOpStr), name, types);
+        }
+
+        /**
+         * @brief Create a dictionary for processing binary operators.
+         *        This tells us, for a given type, an operator and another type,
+         *        is the operation permitted, and if so, what is the type of the result?
+         * The key is <lefttype><opcode><righttype>,
+         *   where <lefttype> and <righttype> are strings returned by (TokenType...).ToString()
+         *   and <opcode> is string returned by (TokenKw...).ToString()
+         * The value is a BinOpStr struct giving the resultant type and a method to generate the code.
+         */
+        private static Dictionary<string, BinOpStr> DefineBinOps ()
+        {
+            Dictionary<string, BinOpStr> bos = new Dictionary<string, BinOpStr> ();
+
+            string[] booltypes = new string[] { "bool", "char", "float", "integer", "key", "list", "string" };
+
+            /*
+             * Get the && and || all out of the way...
+             * Simply cast their left and right operands to boolean then process.
+             */
+            for (int i = 0; i < booltypes.Length; i ++) {
+                for (int j = 0; j < booltypes.Length; j ++) {
+                    bos.Add (booltypes[i] + "&&" + booltypes[j], 
+                             new BinOpStr (typeof (bool), BinOpStrAndAnd));
+                    bos.Add (booltypes[i] + "||" + booltypes[j], 
+                             new BinOpStr (typeof (bool), BinOpStrOrOr));
+                }
+            }
+
+            /*
+             * Pound through all the other combinations we support.
+             */
+
+            // boolean : somethingelse
+            DefineBinOpsBoolX (bos, "bool");
+            DefineBinOpsBoolX (bos, "char");
+            DefineBinOpsBoolX (bos, "float");
+            DefineBinOpsBoolX (bos, "integer");
+            DefineBinOpsBoolX (bos, "key");
+            DefineBinOpsBoolX (bos, "list");
+            DefineBinOpsBoolX (bos, "string");
+
+            // stuff with chars
+            DefineBinOpsChar (bos);
+
+            // somethingelse : boolean
+            DefineBinOpsXBool (bos, "char");
+            DefineBinOpsXBool (bos, "float");
+            DefineBinOpsXBool (bos, "integer");
+            DefineBinOpsXBool (bos, "key");
+            DefineBinOpsXBool (bos, "list");
+            DefineBinOpsXBool (bos, "string");
+
+            // float : somethingelse
+            DefineBinOpsFloatX (bos, "float");
+            DefineBinOpsFloatX (bos, "integer");
+
+            // integer : float
+            DefineBinOpsXFloat (bos, "integer");
+
+            // anything else with integers
+            DefineBinOpsInteger (bos);
+
+            // key : somethingelse
+            DefineBinOpsKeyX (bos, "key");
+            DefineBinOpsKeyX (bos, "string");
+
+            // string : key
+            DefineBinOpsXKey (bos, "string");
+
+            // things with lists
+            DefineBinOpsList (bos);
+
+            // things with rotations
+            DefineBinOpsRotation (bos);
+
+            // things with strings
+            DefineBinOpsString (bos);
+
+            // things with vectors
+            DefineBinOpsVector (bos);
+
+            // Contrary to some beliefs, scripts do things like string+integer and integer+string
+            bos.Add ("bool+string",    new BinOpStr (typeof (string), BinOpStrStrAddStr));
+            bos.Add ("char+string",    new BinOpStr (typeof (string), BinOpStrStrAddStr));
+            bos.Add ("float+string",   new BinOpStr (typeof (string), BinOpStrStrAddStr));
+            bos.Add ("integer+string", new BinOpStr (typeof (string), BinOpStrStrAddStr));
+            bos.Add ("string+bool",    new BinOpStr (typeof (string), BinOpStrStrAddStr, true));
+            bos.Add ("string+char",    new BinOpStr (typeof (string), BinOpStrStrAddStr, true));
+            bos.Add ("string+float",   new BinOpStr (typeof (string), BinOpStrStrAddStr, true));
+            bos.Add ("string+integer", new BinOpStr (typeof (string), BinOpStrStrAddStr, true));
+
+            // Now for our final slight-of-hand, we're going to scan through all those.
+            // And wherever we see an 'integer' in the key, we are going to make another
+            // entry with 'bool', as we want to accept a bool as having a value of 0 or 1.
+            // This lets us do things like 3.5 * (x > 0).
+
+            Dictionary<string, BinOpStr> bos2 = new Dictionary<string, BinOpStr> ();
+            foreach (KeyValuePair<string, BinOpStr> kvp in bos) {
+                string   key = kvp.Key;
+                BinOpStr val = kvp.Value;
+                bos2.Add (key, val);
+            }
+            Regex wordReg = new Regex("\\w+");
+            Regex opReg = new Regex("\\W+");
+            foreach (KeyValuePair<string, BinOpStr> kvp in bos) {
+                string   key  = kvp.Key;
+                BinOpStr val  = kvp.Value;
+                MatchCollection matches = wordReg.Matches(key);
+                if (matches.Count != 2)
+                    continue;
+                Match opM = opReg.Match(key);
+                if (!opM.Success)
+                    continue;
+                string left = matches[0].Value;
+                string right = matches[1].Value;
+                string op = opM.Value;
+                string key2;
+                if (left == "integer" && right == "integer")
+                {
+                    key2 = "bool"+op+"bool";
+                    if (!bos2.ContainsKey (key2)) bos2.Add (key2, val);
+                    key2 = "bool"+op+"integer";
+                    if (!bos2.ContainsKey (key2)) bos2.Add (key2, val);
+                    key2 = "integer"+op+"bool";
+                    if (!bos2.ContainsKey (key2)) bos2.Add (key2, val);
+                }
+                else
+                {
+                    key2 = key.Replace("integer", "bool");
+                    if (!bos2.ContainsKey (key2)) bos2.Add (key2, val);
+                }
+            }
+            return bos2;
+        }
+
+        private static void DefineBinOpsBoolX (Dictionary<string, BinOpStr> bos, string x)
+        {
+            bos.Add ("bool|"  + x, new BinOpStr (typeof (int),  BinOpStrBoolOrX));
+            bos.Add ("bool^"  + x, new BinOpStr (typeof (int),  BinOpStrBoolXorX));
+            bos.Add ("bool&"  + x, new BinOpStr (typeof (int),  BinOpStrBoolAndX));
+            bos.Add ("bool==" + x, new BinOpStr (typeof (bool), BinOpStrBoolEqX));
+            bos.Add ("bool!=" + x, new BinOpStr (typeof (bool), BinOpStrBoolNeX));
+        }
+
+        private static void DefineBinOpsXBool (Dictionary<string, BinOpStr> bos, string x)
+        {
+            bos.Add (x + "|bool",  new BinOpStr (typeof (int),  BinOpStrBoolOrX));
+            bos.Add (x + "^bool",  new BinOpStr (typeof (int),  BinOpStrBoolXorX));
+            bos.Add (x + "&bool",  new BinOpStr (typeof (int),  BinOpStrBoolAndX));
+            bos.Add (x + "==bool", new BinOpStr (typeof (bool), BinOpStrBoolEqX));
+            bos.Add (x + "!=bool", new BinOpStr (typeof (bool), BinOpStrBoolNeX));
+        }
+
+        private static void DefineBinOpsFloatX (Dictionary<string, BinOpStr> bos, string x)
+        {
+            bos.Add ("float==" + x, new BinOpStr (typeof (bool),        BinOpStrFloatEqX));
+            bos.Add ("float!=" + x, new BinOpStr (typeof (bool),        BinOpStrFloatNeX));
+            bos.Add ("float<"  + x, new BinOpStr (typeof (bool),        BinOpStrFloatLtX));
+            bos.Add ("float<=" + x, new BinOpStr (typeof (bool),        BinOpStrFloatLeX));
+            bos.Add ("float>"  + x, new BinOpStr (typeof (bool),        BinOpStrFloatGtX));
+            bos.Add ("float>=" + x, new BinOpStr (typeof (bool),        BinOpStrFloatGeX));
+            bos.Add ("float+"  + x, new BinOpStr (typeof (double), BinOpStrFloatAddX, true));
+            bos.Add ("float-"  + x, new BinOpStr (typeof (double), BinOpStrFloatSubX, true));
+            bos.Add ("float*"  + x, new BinOpStr (typeof (double), BinOpStrFloatMulX, true));
+            bos.Add ("float/"  + x, new BinOpStr (typeof (double), BinOpStrFloatDivX, true));
+            bos.Add ("float%"  + x, new BinOpStr (typeof (double), BinOpStrFloatModX, true));
+        }
+
+        private static void DefineBinOpsXFloat (Dictionary<string, BinOpStr> bos, string x)
+        {
+            bos.Add (x + "==float", new BinOpStr (typeof (bool),        BinOpStrXEqFloat));
+            bos.Add (x + "!=float", new BinOpStr (typeof (bool),        BinOpStrXNeFloat));
+            bos.Add (x + "<float",  new BinOpStr (typeof (bool),        BinOpStrXLtFloat));
+            bos.Add (x + "<=float", new BinOpStr (typeof (bool),        BinOpStrXLeFloat));
+            bos.Add (x + ">float",  new BinOpStr (typeof (bool),        BinOpStrXGtFloat));
+            bos.Add (x + ">=float", new BinOpStr (typeof (bool),        BinOpStrXGeFloat));
+            bos.Add (x + "+float",  new BinOpStr (typeof (double), BinOpStrXAddFloat, true));
+            bos.Add (x + "-float",  new BinOpStr (typeof (double), BinOpStrXSubFloat, true));
+            bos.Add (x + "*float",  new BinOpStr (typeof (double), BinOpStrXMulFloat, true));
+            bos.Add (x + "/float",  new BinOpStr (typeof (double), BinOpStrXDivFloat, true));
+            bos.Add (x + "%float",  new BinOpStr (typeof (double), BinOpStrXModFloat, true));
+        }
+
+        private static void DefineBinOpsChar (Dictionary<string, BinOpStr> bos)
+        {
+            bos.Add ("char==char",   new BinOpStr (typeof (bool), BinOpStrCharEqChar));
+            bos.Add ("char!=char",   new BinOpStr (typeof (bool), BinOpStrCharNeChar));
+            bos.Add ("char<char",    new BinOpStr (typeof (bool), BinOpStrCharLtChar));
+            bos.Add ("char<=char",   new BinOpStr (typeof (bool), BinOpStrCharLeChar));
+            bos.Add ("char>char",    new BinOpStr (typeof (bool), BinOpStrCharGtChar));
+            bos.Add ("char>=char",   new BinOpStr (typeof (bool), BinOpStrCharGeChar));
+            bos.Add ("char+integer", new BinOpStr (typeof (char), BinOpStrCharAddInt, true));
+            bos.Add ("char-integer", new BinOpStr (typeof (char), BinOpStrCharSubInt, true));
+            bos.Add ("char-char",    new BinOpStr (typeof (int),  BinOpStrCharSubChar));
+        }
+
+        private static void DefineBinOpsInteger (Dictionary<string, BinOpStr> bos)
+        {
+            bos.Add ("integer==integer", new BinOpStr (typeof (bool), BinOpStrIntEqInt));
+            bos.Add ("integer!=integer", new BinOpStr (typeof (bool), BinOpStrIntNeInt));
+            bos.Add ("integer<integer",  new BinOpStr (typeof (bool), BinOpStrIntLtInt));
+            bos.Add ("integer<=integer", new BinOpStr (typeof (bool), BinOpStrIntLeInt));
+            bos.Add ("integer>integer",  new BinOpStr (typeof (bool), BinOpStrIntGtInt));
+            bos.Add ("integer>=integer", new BinOpStr (typeof (bool), BinOpStrIntGeInt));
+            bos.Add ("integer|integer",  new BinOpStr (typeof (int),  BinOpStrIntOrInt,  true));
+            bos.Add ("integer^integer",  new BinOpStr (typeof (int),  BinOpStrIntXorInt, true));
+            bos.Add ("integer&integer",  new BinOpStr (typeof (int),  BinOpStrIntAndInt, true));
+            bos.Add ("integer+integer",  new BinOpStr (typeof (int),  BinOpStrIntAddInt, true));
+            bos.Add ("integer-integer",  new BinOpStr (typeof (int),  BinOpStrIntSubInt, true));
+            bos.Add ("integer*integer",  new BinOpStr (typeof (int),  BinOpStrIntMulInt, true));
+            bos.Add ("integer/integer",  new BinOpStr (typeof (int),  BinOpStrIntDivInt, true));
+            bos.Add ("integer%integer",  new BinOpStr (typeof (int),  BinOpStrIntModInt, true));
+            bos.Add ("integer<<integer", new BinOpStr (typeof (int),  BinOpStrIntShlInt, true));
+            bos.Add ("integer>>integer", new BinOpStr (typeof (int),  BinOpStrIntShrInt, true));
+        }
+
+        private static void DefineBinOpsKeyX (Dictionary<string, BinOpStr> bos, string x)
+        {
+            bos.Add ("key==" + x, new BinOpStr (typeof (bool), BinOpStrKeyEqX));
+            bos.Add ("key!=" + x, new BinOpStr (typeof (bool), BinOpStrKeyNeX));
+        }
+
+        private static void DefineBinOpsXKey (Dictionary<string, BinOpStr> bos, string x)
+        {
+            bos.Add (x + "==key", new BinOpStr (typeof (bool), BinOpStrKeyEqX));
+            bos.Add (x + "!=key", new BinOpStr (typeof (bool), BinOpStrKeyNeX));
+        }
+
+        private static void DefineBinOpsList (Dictionary<string, BinOpStr> bos)
+        {
+            bos.Add ("list+float",     new BinOpStr (typeof (LSL_List), BinOpStrListAddFloat, true));
+            bos.Add ("list+integer",   new BinOpStr (typeof (LSL_List), BinOpStrListAddInt,   true));
+            bos.Add ("list+key",       new BinOpStr (typeof (LSL_List), BinOpStrListAddKey,   true));
+            bos.Add ("list+list",      new BinOpStr (typeof (LSL_List), BinOpStrListAddList,  true));
+            bos.Add ("list+rotation",  new BinOpStr (typeof (LSL_List), BinOpStrListAddRot,   true));
+            bos.Add ("list+string",    new BinOpStr (typeof (LSL_List), BinOpStrListAddStr,   true));
+            bos.Add ("list+vector",    new BinOpStr (typeof (LSL_List), BinOpStrListAddVec,   true));
+
+            bos.Add ("float+list",     new BinOpStr (typeof (LSL_List), BinOpStrFloatAddList));
+            bos.Add ("integer+list",   new BinOpStr (typeof (LSL_List), BinOpStrIntAddList));
+            bos.Add ("key+list",       new BinOpStr (typeof (LSL_List), BinOpStrKeyAddList));
+            bos.Add ("rotation+list",  new BinOpStr (typeof (LSL_List), BinOpStrRotAddList));
+            bos.Add ("string+list",    new BinOpStr (typeof (LSL_List), BinOpStrStrAddList));
+            bos.Add ("vector+list",    new BinOpStr (typeof (LSL_List), BinOpStrVecAddList));
+
+            bos.Add ("list==list",     new BinOpStr (typeof (bool), BinOpStrListEqList));
+            bos.Add ("list!=list",     new BinOpStr (typeof (int),  BinOpStrListNeList));
+        }
+
+        // all operations allowed by LSL_Rotation definition
+        private static void DefineBinOpsRotation (Dictionary<string, BinOpStr> bos)
+        {
+            bos.Add ("rotation==rotation", new BinOpStr (typeof (bool),         BinOpStrRotEqRot));
+            bos.Add ("rotation!=rotation", new BinOpStr (typeof (bool),         BinOpStrRotNeRot));
+            bos.Add ("rotation+rotation",  new BinOpStr (typeof (LSL_Rotation), BinOpStrRotAddRot, true));
+            bos.Add ("rotation-rotation",  new BinOpStr (typeof (LSL_Rotation), BinOpStrRotSubRot, true));
+            bos.Add ("rotation*rotation",  new BinOpStr (typeof (LSL_Rotation), BinOpStrRotMulRot, true));
+            bos.Add ("rotation/rotation",  new BinOpStr (typeof (LSL_Rotation), BinOpStrRotDivRot, true));
+        }
+
+        private static void DefineBinOpsString (Dictionary<string, BinOpStr> bos)
+        {
+            bos.Add ("string==string", new BinOpStr (typeof (bool),   BinOpStrStrEqStr));
+            bos.Add ("string!=string", new BinOpStr (typeof (bool),   BinOpStrStrNeStr));
+            bos.Add ("string<string",  new BinOpStr (typeof (bool),   BinOpStrStrLtStr));
+            bos.Add ("string<=string", new BinOpStr (typeof (bool),   BinOpStrStrLeStr));
+            bos.Add ("string>string",  new BinOpStr (typeof (bool),   BinOpStrStrGtStr));
+            bos.Add ("string>=string", new BinOpStr (typeof (bool),   BinOpStrStrGeStr));
+            bos.Add ("string+string",  new BinOpStr (typeof (string), BinOpStrStrAddStr, true));
+        }
+
+        // all operations allowed by LSL_Vector definition
+        private static void DefineBinOpsVector (Dictionary<string, BinOpStr> bos)
+        {
+            bos.Add ("vector==vector",  new BinOpStr (typeof (bool),        BinOpStrVecEqVec));
+            bos.Add ("vector!=vector",  new BinOpStr (typeof (bool),        BinOpStrVecNeVec));
+            bos.Add ("vector+vector",   new BinOpStr (typeof (LSL_Vector),  BinOpStrVecAddVec, true));
+            bos.Add ("vector-vector",   new BinOpStr (typeof (LSL_Vector),  BinOpStrVecSubVec, true));
+            bos.Add ("vector*vector",   new BinOpStr (typeof (double), BinOpStrVecMulVec));
+            bos.Add ("vector%vector",   new BinOpStr (typeof (LSL_Vector),  BinOpStrVecModVec, true));
+
+            bos.Add ("vector*float",    new BinOpStr (typeof (LSL_Vector),  BinOpStrVecMulFloat, true));
+            bos.Add ("float*vector",    new BinOpStr (typeof (LSL_Vector),  BinOpStrFloatMulVec));
+            bos.Add ("vector/float",    new BinOpStr (typeof (LSL_Vector),  BinOpStrVecDivFloat, true));
+
+            bos.Add ("vector*integer",  new BinOpStr (typeof (LSL_Vector),  BinOpStrVecMulInt, true));
+            bos.Add ("integer*vector",  new BinOpStr (typeof (LSL_Vector),  BinOpStrIntMulVec));
+            bos.Add ("vector/integer",  new BinOpStr (typeof (LSL_Vector),  BinOpStrVecDivInt, true));
+
+            bos.Add ("vector*rotation", new BinOpStr (typeof (LSL_Vector),  BinOpStrVecMulRot, true));
+            bos.Add ("vector/rotation", new BinOpStr (typeof (LSL_Vector),  BinOpStrVecDivRot, true));
+        }
+
+        /**
+         * @brief These methods actually emit the code to perform the arithmetic.
+         * @param scg    = what script we are compiling
+         * @param left   = left-hand operand location in memory (type as given by BinOpStr entry)
+         * @param right  = right-hand operand location in memory (type as given by BinOpStr entry)
+         * @param result = result location in memory (type as given by BinOpStr entry)
+         */
+        private static void BinOpStrAndAnd (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeBool);
+            right.PushVal (scg, errorAt, tokenTypeBool);
+            scg.ilGen.Emit (errorAt, OpCodes.And);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrOrOr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeBool);
+            right.PushVal (scg, errorAt, tokenTypeBool);
+            scg.ilGen.Emit (errorAt, OpCodes.Or);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrBoolOrX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Or);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrBoolXorX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrBoolAndX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.And);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrBoolEqX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeBool);
+            right.PushVal (scg, errorAt, tokenTypeBool);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrBoolNeX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeBool);
+            right.PushVal (scg, errorAt, tokenTypeBool);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrFloatEqX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrFloatNeX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrFloatLtX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Clt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrFloatLeX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Cgt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrFloatGtX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Cgt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrFloatGeX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Clt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrFloatAddX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Add);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrFloatSubX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Sub);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrFloatMulX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Mul);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrFloatDivX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Div);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrFloatModX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Rem);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrXEqFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrXNeFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrXLtFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Clt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrXLeFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Cgt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrXGtFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Cgt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrXGeFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Clt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrXAddFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Add);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrXSubFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Sub);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrXMulFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Mul);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrXDivFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Div);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrXModFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Rem);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrCharEqChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeChar);
+            right.PushVal (scg, errorAt, tokenTypeChar);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrCharNeChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeChar);
+            right.PushVal (scg, errorAt, tokenTypeChar);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrCharLtChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeChar);
+            right.PushVal (scg, errorAt, tokenTypeChar);
+            scg.ilGen.Emit (errorAt, OpCodes.Clt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrCharLeChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeChar);
+            right.PushVal (scg, errorAt, tokenTypeChar);
+            scg.ilGen.Emit (errorAt, OpCodes.Cgt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrCharGtChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeChar);
+            right.PushVal (scg, errorAt, tokenTypeChar);
+            scg.ilGen.Emit (errorAt, OpCodes.Cgt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrCharGeChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeChar);
+            right.PushVal (scg, errorAt, tokenTypeChar);
+            scg.ilGen.Emit (errorAt, OpCodes.Clt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrCharAddInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeChar);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Add);
+            result.PopPost (scg, errorAt, tokenTypeChar);
+        }
+
+        private static void BinOpStrCharSubInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeChar);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Sub);
+            result.PopPost (scg, errorAt, tokenTypeChar);
+        }
+
+        private static void BinOpStrCharSubChar (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeChar);
+            right.PushVal (scg, errorAt, tokenTypeChar);
+            scg.ilGen.Emit (errorAt, OpCodes.Sub);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrIntEqInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrIntNeInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrIntLtInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Clt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrIntLeInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Cgt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrIntGtInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Cgt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrIntGeInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Clt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrIntOrInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Or);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrIntXorInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrIntAndInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.And);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrIntAddInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Add);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrIntSubInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Sub);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrIntMulInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Mul);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrIntDivInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            // note that we must allow 0x800000/-1 -> 0x80000000 for lslangtest1.lsl
+            // so sign-extend the operands to 64-bit then divide and truncate result
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Conv_I8);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Conv_I8);
+            scg.ilGen.Emit (errorAt, OpCodes.Div);
+            scg.ilGen.Emit (errorAt, OpCodes.Conv_I4);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrIntModInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            // note that we must allow 0x800000%-1 -> 0 for lslangtest1.lsl
+            // so sign-extend the operands to 64-bit then mod and truncate result
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Conv_I8);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Conv_I8);
+            scg.ilGen.Emit (errorAt, OpCodes.Rem);
+            scg.ilGen.Emit (errorAt, OpCodes.Conv_I4);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrIntShlInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Shl);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrIntShrInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Shr);
+            result.PopPost (scg, errorAt, tokenTypeInt);
+        }
+
+        private static void BinOpStrKeyEqX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrKeyNeX (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrListAddFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeList);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddFloat);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrListAddInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeList);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddInt);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrListAddKey (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeList);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddKey);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrListAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeList);
+            right.PushVal (scg, errorAt, tokenTypeList);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddList);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrListAddRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeList);
+            right.PushVal (scg, errorAt, tokenTypeRot);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddRot);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrListAddStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeList);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddStr);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrListAddVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeList);
+            right.PushVal (scg, errorAt, tokenTypeVec);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListAddVec);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrFloatAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeList);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethFloatAddList);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrIntAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeList);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethIntAddList);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrKeyAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeList);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethKeyAddList);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrRotAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeRot);
+            right.PushVal (scg, errorAt, tokenTypeList);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotAddList);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrStrAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeList);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethStrAddList);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrVecAddList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeList);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecAddList);
+            result.PopPost (scg, errorAt, tokenTypeList);
+        }
+
+        private static void BinOpStrListEqList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeList);
+            right.PushVal (scg, errorAt, tokenTypeList);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListEqList);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrListNeList (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeList);
+            right.PushVal (scg, errorAt, tokenTypeList);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethListNeList);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrRotEqRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeRot);
+            right.PushVal (scg, errorAt, tokenTypeRot);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotEqRot);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrRotNeRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeRot);
+            right.PushVal (scg, errorAt, tokenTypeRot);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotNeRot);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrRotAddRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeRot);
+            right.PushVal (scg, errorAt, tokenTypeRot);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotAddRot);
+            result.PopPost (scg, errorAt, tokenTypeRot);
+        }
+
+        private static void BinOpStrRotSubRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeRot);
+            right.PushVal (scg, errorAt, tokenTypeRot);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotSubRot);
+            result.PopPost (scg, errorAt, tokenTypeRot);
+        }
+
+        private static void BinOpStrRotMulRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeRot);
+            right.PushVal (scg, errorAt, tokenTypeRot);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotMulRot);
+            result.PopPost (scg, errorAt, tokenTypeRot);
+        }
+
+        private static void BinOpStrRotDivRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeRot);
+            right.PushVal (scg, errorAt, tokenTypeRot);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethRotDivRot);
+            result.PopPost (scg, errorAt, tokenTypeRot);
+        }
+
+        private static void BinOpStrStrEqStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrStrNeStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrStrLtStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
+            scg.ilGen.Emit (errorAt, OpCodes.Clt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrStrLeStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Clt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrStrGtStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
+            scg.ilGen.Emit (errorAt, OpCodes.Cgt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrStrGeStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringCmpStringMethInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_M1);
+            scg.ilGen.Emit (errorAt, OpCodes.Cgt);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        // Called by many type combinations so both operands need to be cast to strings
+        private static void BinOpStrStrAddStr (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeStr);
+            right.PushVal (scg, errorAt, tokenTypeStr);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringAddStringMethInfo);
+            result.PopPost (scg, errorAt, tokenTypeStr);
+        }
+
+        private static void BinOpStrVecEqVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeVec);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecEqVec);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrVecNeVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeVec);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecNeVec);
+            result.PopPost (scg, errorAt, tokenTypeBool);
+        }
+
+        private static void BinOpStrVecAddVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeVec);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecAddVec);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        private static void BinOpStrVecSubVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeVec);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecSubVec);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        private static void BinOpStrVecMulVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeVec);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecMulVec);
+            result.PopPost (scg, errorAt, tokenTypeFloat);
+        }
+
+        private static void BinOpStrVecModVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeVec);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecModVec);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        private static void BinOpStrVecMulFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecMulFloat);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        private static void BinOpStrFloatMulVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeFloat);
+            right.PushVal (scg, errorAt, tokenTypeVec);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethFloatMulVec);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        private static void BinOpStrVecDivFloat (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeFloat);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecDivFloat);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        private static void BinOpStrVecMulInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecMulInt);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        private static void BinOpStrIntMulVec (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeInt);
+            right.PushVal (scg, errorAt, tokenTypeVec);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethIntMulVec);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        private static void BinOpStrVecDivInt (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeInt);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecDivInt);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        private static void BinOpStrVecMulRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeRot);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecMulRot);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        private static void BinOpStrVecDivRot (ScriptCodeGen scg, Token errorAt, CompValu left, CompValu right, CompValu result)
+        {
+            result.PopPre (scg, errorAt);
+            left.PushVal (scg, errorAt, tokenTypeVec);
+            right.PushVal (scg, errorAt, tokenTypeRot);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, infoMethVecDivRot);
+            result.PopPost (scg, errorAt, tokenTypeVec);
+        }
+
+        /**
+         * @brief These methods are called at runtime as helpers.
+         *        Needed to pick up functionality defined by overloaded operators of LSL_ types.
+         *        They need to be marked public or runtime says they are inaccessible.
+         */
+        public static LSL_List MethListAddFloat (LSL_List left, double right)
+        {
+            return MethListAddObj (left, new LSL_Float (right));
+        }
+        public static LSL_List MethListAddInt (LSL_List left, int right)
+        {
+            return MethListAddObj (left, new LSL_Integer (right));
+        }
+        public static LSL_List MethListAddKey (LSL_List left, string right)
+        {
+            return MethListAddObj (left, new LSL_Key (right));
+        }
+        public static LSL_List MethListAddRot (LSL_List left, LSL_Rotation right)
+        {
+            return MethListAddObj (left, right);
+        }
+        public static LSL_List MethListAddStr (LSL_List left, string right)
+        {
+            return MethListAddObj (left, new LSL_String (right));
+        }
+        public static LSL_List MethListAddVec (LSL_List left, LSL_Vector right)
+        {
+            return MethListAddObj (left, right);
+        }
+        public static LSL_List MethListAddObj (LSL_List left, object right)
+        {
+            int oldlen = left.Length;
+            object[] newarr = new object[oldlen+1];
+            Array.Copy (left.Data, newarr, oldlen);
+            newarr[oldlen] = right;
+            return new LSL_List (newarr);
+        }
+
+        public static LSL_List MethListAddList (LSL_List left, LSL_List right)
+        {
+            int leftlen = left.Length;
+            int ritelen = right.Length;
+            object[] newarr = new object[leftlen+ritelen];
+            Array.Copy (left.Data, newarr, leftlen);
+            Array.Copy (right.Data, 0, newarr, leftlen, ritelen);
+            return new LSL_List (newarr);
+        }
+
+        public static LSL_List MethFloatAddList (double left, LSL_List right)
+        {
+            return MethObjAddList (new LSL_Float (left), right);
+        }
+        public static LSL_List MethIntAddList (int left, LSL_List right)
+        {
+            return MethObjAddList (new LSL_Integer (left), right);
+        }
+        public static LSL_List MethKeyAddList (string left, LSL_List right)
+        {
+            return MethObjAddList (new LSL_Key (left), right);
+        }
+        public static LSL_List MethRotAddList (LSL_Rotation left, LSL_List right)
+        {
+            return MethObjAddList (left, right);
+        }
+        public static LSL_List MethStrAddList (string left, LSL_List right)
+        {
+            return MethObjAddList (new LSL_String (left), right);
+        }
+        public static LSL_List MethVecAddList (LSL_Vector left, LSL_List right)
+        {
+            return MethObjAddList (left, right);
+        }
+        public static LSL_List MethObjAddList (object left, LSL_List right)
+        {
+            int oldlen = right.Length;
+            object[] newarr = new object[oldlen+1];
+            newarr[0] = left;
+            Array.Copy (right.Data, 0, newarr, 1, oldlen);
+            return new LSL_List (newarr);
+        }
+
+        public static bool MethListEqList (LSL_List left, LSL_List right)
+        {
+            return left == right;
+        }
+
+        // According to http://wiki.secondlife.com/wiki/LlGetListLength
+        // jackassed LSL allows 'somelist != []' to get the length of a list
+        public static int MethListNeList (LSL_List left, LSL_List right)
+        {
+            int leftlen = left.Length;
+            int ritelen = right.Length;
+            return leftlen - ritelen;
+        }
+
+        public static bool MethRotEqRot (LSL_Rotation left, LSL_Rotation right)
+        {
+            return left == right;
+        }
+
+        public static bool MethRotNeRot (LSL_Rotation left, LSL_Rotation right)
+        {
+            return left != right;
+        }
+
+        public static LSL_Rotation MethRotAddRot (LSL_Rotation left, LSL_Rotation right)
+        {
+            return left + right;
+        }
+
+        public static LSL_Rotation MethRotSubRot (LSL_Rotation left, LSL_Rotation right)
+        {
+            return left - right;
+        }
+
+        public static LSL_Rotation MethRotMulRot (LSL_Rotation left, LSL_Rotation right)
+        {
+            return left * right;
+        }
+
+        public static LSL_Rotation MethRotDivRot (LSL_Rotation left, LSL_Rotation right)
+        {
+            return left / right;
+        }
+
+        public static bool MethVecEqVec (LSL_Vector left, LSL_Vector right)
+        {
+            return left == right;
+        }
+
+        public static bool MethVecNeVec (LSL_Vector left, LSL_Vector right)
+        {
+            return left != right;
+        }
+
+        public static LSL_Vector MethVecAddVec (LSL_Vector left, LSL_Vector right)
+        {
+            return left + right;
+        }
+
+        public static LSL_Vector MethVecSubVec (LSL_Vector left, LSL_Vector right)
+        {
+            return left - right;
+        }
+
+        public static double MethVecMulVec (LSL_Vector left, LSL_Vector right)
+        {
+            return (double)(left * right).value;
+        }
+
+        public static LSL_Vector MethVecModVec (LSL_Vector left, LSL_Vector right)
+        {
+            return left % right;
+        }
+
+        public static LSL_Vector MethVecMulFloat (LSL_Vector left, double right)
+        {
+            return left * right;
+        }
+
+        public static LSL_Vector MethFloatMulVec (double left, LSL_Vector right)
+        {
+            return left * right;
+        }
+
+        public static LSL_Vector MethVecDivFloat (LSL_Vector left, double right)
+        {
+            return left / right;
+        }
+
+        public static LSL_Vector MethVecMulInt (LSL_Vector left, int right)
+        {
+            return left * right;
+        }
+
+        public static LSL_Vector MethIntMulVec (int left, LSL_Vector right)
+        {
+            return left * right;
+        }
+
+        public static LSL_Vector MethVecDivInt (LSL_Vector left, int right)
+        {
+            return left / right;
+        }
+
+        public static LSL_Vector MethVecMulRot (LSL_Vector left, LSL_Rotation right)
+        {
+            return left * right;
+        }
+
+        public static LSL_Vector MethVecDivRot (LSL_Vector left, LSL_Rotation right)
+        {
+            return left / right;
+        }
+    }
+}

+ 6262 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs

@@ -0,0 +1,6262 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+/**
+ * @brief translate a reduced script token into corresponding CIL code.
+ * The single script token contains a tokenized and textured version of the whole script file.
+ */
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public interface IScriptCodeGen
+    {
+        ScriptMyILGen ilGen { get; } // the output instruction stream
+        void ErrorMsg (Token token, string message);
+        void PushDefaultValue (TokenType type);
+        void PushXMRInst (); 
+    }
+
+    public class ScriptCodeGen : IScriptCodeGen
+    {
+        private static readonly bool DEBUG_STACKCAPRES = false;
+        private static readonly bool DEBUG_TRYSTMT = false;
+
+        public static readonly string OBJECT_CODE_MAGIC = "XMRObjectCode";
+        public static int COMPILED_VERSION_VALUE = 20;  // incremented when compiler changes for compatibility testing
+
+        public static readonly int CALL_FRAME_MEMUSE = 64;
+        public static readonly int STRING_LEN_TO_MEMUSE = 2;
+
+        public static Type xmrInstSuperType = null;  // typeof whatever is actually malloc'd for script instances
+                                                     // - must inherit from XMRInstAbstract
+
+        /*
+         * Static tables that there only needs to be one copy of for all.
+         */
+        private static VarDict legalEventHandlers = CreateLegalEventHandlers ();
+        private static CompValu[]      zeroCompValus = new CompValu[0];
+        private static TokenType[]     zeroArgs      = new TokenType[0];
+        private static TokenTypeBool   tokenTypeBool = new TokenTypeBool   (null);
+        private static TokenTypeExc    tokenTypeExc  = new TokenTypeExc    (null);
+        private static TokenTypeFloat  tokenTypeFlt  = new TokenTypeFloat  (null);
+        private static TokenTypeInt    tokenTypeInt  = new TokenTypeInt    (null);
+        private static TokenTypeObject tokenTypeObj  = new TokenTypeObject (null);
+        private static TokenTypeRot    tokenTypeRot  = new TokenTypeRot    (null);
+        private static TokenTypeStr    tokenTypeStr  = new TokenTypeStr    (null);
+        private static TokenTypeVec    tokenTypeVec  = new TokenTypeVec    (null);
+        private static Type[] instanceTypeArg = new Type[] { typeof (XMRInstAbstract) };
+        private static string[] instanceNameArg = new string[] { "$xmrthis" };
+
+        private static ConstructorInfo lslFloatConstructorInfo = typeof (LSL_Float).GetConstructor (new Type[] { typeof (double) });
+        private static ConstructorInfo lslIntegerConstructorInfo = typeof (LSL_Integer).GetConstructor (new Type[] { typeof (int) });
+        private static ConstructorInfo lslListConstructorInfo = typeof (LSL_List).GetConstructor (new Type[] { typeof (object[]) });
+        public  static ConstructorInfo lslRotationConstructorInfo = typeof (LSL_Rotation).GetConstructor (new Type[] { typeof (double), typeof (double), typeof (double), typeof (double) });
+        private static ConstructorInfo lslStringConstructorInfo = typeof (LSL_String).GetConstructor (new Type[] { typeof (string) });
+        public  static ConstructorInfo lslVectorConstructorInfo = typeof (LSL_Vector).GetConstructor (new Type[] { typeof (double), typeof (double), typeof (double) });
+        private static ConstructorInfo scriptBadCallNoExceptionConstructorInfo = typeof (ScriptBadCallNoException).GetConstructor (new Type[] { typeof (int) });
+        private static ConstructorInfo scriptChangeStateExceptionConstructorInfo = typeof (ScriptChangeStateException).GetConstructor (new Type[] { typeof (int) });
+        private static ConstructorInfo scriptRestoreCatchExceptionConstructorInfo = typeof (ScriptRestoreCatchException).GetConstructor (new Type[] { typeof (Exception) });
+        private static ConstructorInfo scriptUndefinedStateExceptionConstructorInfo = typeof (ScriptUndefinedStateException).GetConstructor (new Type[] { typeof (string) });
+        private static ConstructorInfo sdtClassConstructorInfo = typeof (XMRSDTypeClObj).GetConstructor (new Type[] { typeof (XMRInstAbstract), typeof (int) });
+        private static ConstructorInfo xmrArrayConstructorInfo = typeof (XMR_Array).GetConstructor (new Type[] { typeof (XMRInstAbstract) });
+        private static FieldInfo callModeFieldInfo       = typeof (XMRInstAbstract).GetField ("callMode");
+        private static FieldInfo doGblInitFieldInfo      = typeof (XMRInstAbstract).GetField ("doGblInit");
+        private static FieldInfo ehArgsFieldInfo         = typeof (XMRInstAbstract).GetField ("ehArgs");
+        private static FieldInfo rotationXFieldInfo      = typeof (LSL_Rotation).GetField ("x");
+        private static FieldInfo rotationYFieldInfo      = typeof (LSL_Rotation).GetField ("y");
+        private static FieldInfo rotationZFieldInfo      = typeof (LSL_Rotation).GetField ("z");
+        private static FieldInfo rotationSFieldInfo      = typeof (LSL_Rotation).GetField ("s");
+        private static FieldInfo sdtXMRInstFieldInfo     = typeof (XMRSDTypeClObj).GetField ("xmrInst");
+        private static FieldInfo vectorXFieldInfo        = typeof (LSL_Vector).GetField ("x");
+        private static FieldInfo vectorYFieldInfo        = typeof (LSL_Vector).GetField ("y");
+        private static FieldInfo vectorZFieldInfo        = typeof (LSL_Vector).GetField ("z");
+
+        private static MethodInfo arrayClearMethodInfo   = typeof (XMR_Array).GetMethod ("__pub_clear", new Type[] { });
+        private static MethodInfo arrayCountMethodInfo   = typeof (XMR_Array).GetMethod ("__pub_count", new Type[] { });
+        private static MethodInfo arrayIndexMethodInfo   = typeof (XMR_Array).GetMethod ("__pub_index", new Type[] { typeof (int) });
+        private static MethodInfo arrayValueMethodInfo   = typeof (XMR_Array).GetMethod ("__pub_value", new Type[] { typeof (int) });
+        private static MethodInfo checkRunStackMethInfo  = typeof (XMRInstAbstract).GetMethod ("CheckRunStack", new Type[] { });
+        private static MethodInfo checkRunQuickMethInfo  = typeof (XMRInstAbstract).GetMethod ("CheckRunQuick", new Type[] { });
+        private static MethodInfo ehArgUnwrapFloat       = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapFloat",    new Type[] { typeof (object) });
+        private static MethodInfo ehArgUnwrapInteger     = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapInteger",  new Type[] { typeof (object) });
+        private static MethodInfo ehArgUnwrapRotation    = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapRotation", new Type[] { typeof (object) });
+        private static MethodInfo ehArgUnwrapString      = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapString",   new Type[] { typeof (object) });
+        private static MethodInfo ehArgUnwrapVector      = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapVector",   new Type[] { typeof (object) });
+        private static MethodInfo xmrArrPubIndexMethod   = typeof (XMR_Array).GetMethod ("__pub_index", new Type[] { typeof (int) });
+        private static MethodInfo xmrArrPubValueMethod   = typeof (XMR_Array).GetMethod ("__pub_value", new Type[] { typeof (int) });
+        private static MethodInfo captureStackFrameMethodInfo = typeof (XMRInstAbstract).GetMethod ("CaptureStackFrame", new Type[] { typeof (string), typeof (int), typeof (int) });
+        private static MethodInfo restoreStackFrameMethodInfo = typeof (XMRInstAbstract).GetMethod ("RestoreStackFrame", new Type[] { typeof (string), typeof (int).MakeByRefType () });
+        private static MethodInfo stringCompareMethodInfo     = GetStaticMethod (typeof (String), "Compare", new Type[] { typeof (string), typeof (string), typeof (StringComparison) });
+        private static MethodInfo stringConcat2MethodInfo     = GetStaticMethod (typeof (String), "Concat", new Type[] { typeof (string), typeof (string) });
+        private static MethodInfo stringConcat3MethodInfo     = GetStaticMethod (typeof (String), "Concat", new Type[] { typeof (string), typeof (string), typeof (string) });
+        private static MethodInfo stringConcat4MethodInfo     = GetStaticMethod (typeof (String), "Concat", new Type[] { typeof (string), typeof (string), typeof (string), typeof (string) });
+        private static MethodInfo lslRotationNegateMethodInfo = GetStaticMethod (typeof (ScriptCodeGen), 
+                                                                                 "LSLRotationNegate", 
+                                                                                 new Type[] { typeof (LSL_Rotation) });
+        private static MethodInfo lslVectorNegateMethodInfo = GetStaticMethod (typeof (ScriptCodeGen), 
+                                                                               "LSLVectorNegate", 
+                                                                               new Type[] { typeof (LSL_Vector) });
+        private static MethodInfo scriptRestoreCatchExceptionUnwrap = GetStaticMethod (typeof (ScriptRestoreCatchException), "Unwrap", new Type[] { typeof (Exception) });
+        private static MethodInfo thrownExceptionWrapMethodInfo  = GetStaticMethod (typeof (ScriptThrownException), "Wrap", new Type[] { typeof (object) });
+        private static MethodInfo heapTrackerListPush   = typeof (HeapTrackerList).  GetMethod ("Push", new Type[0]);
+        private static MethodInfo heapTrackerObjectPush = typeof (HeapTrackerObject).GetMethod ("Push", new Type[0]);
+        private static MethodInfo heapTrackerStringPush = typeof (HeapTrackerString).GetMethod ("Push", new Type[0]);
+
+        private static MethodInfo catchExcToStrMethodInfo = GetStaticMethod (typeof (ScriptCodeGen),
+                                                                             "CatchExcToStr",
+                                                                             new Type[] { typeof (Exception) });
+
+        private static MethodInfo consoleWriteMethodInfo      = GetStaticMethod (typeof (ScriptCodeGen), "ConsoleWrite",   new Type[] { typeof (object) });
+        public static void ConsoleWrite (object o)
+        {
+            if (o == null) o = "<<null>>";
+            Console.Write (o.ToString ());
+        }
+
+        public static bool CodeGen (TokenScript tokenScript, BinaryWriter objFileWriter, string sourceHash)
+        {
+            /*
+             * Run compiler such that it has a 'this' context for convenience.
+             */
+            ScriptCodeGen scg = new ScriptCodeGen (tokenScript, objFileWriter, sourceHash);
+
+            /*
+             * Return pointer to resultant script object code.
+             */
+            return !scg.youveAnError;
+        }
+
+        /*
+         * There is one set of these variables for each script being compiled.
+         */
+        private bool mightGetHere = false;
+        private bool youveAnError = false;
+        private BreakContTarg curBreakTarg = null;
+        private BreakContTarg curContTarg  = null;
+        private int lastErrorLine = 0;
+        private int nStates = 0;
+        private string sourceHash;
+        private string lastErrorFile = "";
+        private string[] stateNames;
+        private XMRInstArSizes glblSizes = new XMRInstArSizes ();
+        private Token errorMessageToken = null;
+        private TokenDeclVar curDeclFunc = null;
+        private TokenStmtBlock curStmtBlock = null;
+        private BinaryWriter objFileWriter = null;
+        private TokenScript tokenScript = null;
+        public  int tempCompValuNum = 0;
+        private TokenDeclSDTypeClass currentSDTClass = null;
+
+        private Dictionary<string, int> stateIndices = null;
+
+        // These get cleared at beginning of every function definition
+        private ScriptMyLocal instancePointer;   // holds XMRInstanceSuperType pointer
+        private ScriptMyLabel retLabel  = null;  // where to jump to exit function
+        private ScriptMyLocal retValue  = null;
+        private ScriptMyLocal actCallNo = null;  // for the active try/catch/finally stack or the big one outside them all
+        private LinkedList<CallLabel> actCallLabels = new LinkedList<CallLabel> ();  // for the active try/catch/finally stack or the big one outside them all
+        private LinkedList<CallLabel> allCallLabels = new LinkedList<CallLabel> ();  // this holds each and every one for all stacks in total
+        public  CallLabel openCallLabel = null;  // only one call label can be open at a time
+                                                 // - the call label is open from the time of CallPre() until corresponding CallPost()
+                                                 // - so no non-trivial pushes/pops etc allowed between a CallPre() and a CallPost()
+
+        private ScriptMyILGen _ilGen;
+        public ScriptMyILGen ilGen { get { return _ilGen; } }
+
+        private ScriptCodeGen (TokenScript tokenScript, BinaryWriter objFileWriter, string sourceHash)
+        {
+            this.tokenScript   = tokenScript;
+            this.objFileWriter = objFileWriter;
+            this.sourceHash    = sourceHash;
+
+            try {
+                PerformCompilation ();
+            } catch {
+                // if we've an error, just punt on any exception
+                // it's probably just a null reference from something
+                // not being filled in etc.
+                if (!youveAnError) throw;
+            } finally {
+                objFileWriter = null;
+            }
+        }
+
+        /**
+         * @brief Convert 'tokenScript' to 'objFileWriter' format.
+         *   'tokenScript' is a parsed/reduced abstract syntax tree of the script source file
+         *   'objFileWriter' is a serialized form of the CIL code that we generate
+         */
+        private void PerformCompilation ()
+        {
+            /*
+             * errorMessageToken is used only when the given token doesn't have a
+             * output delegate associated with it such as for backend API functions
+             * that only have one copy for the whole system.  It is kept up-to-date
+             * approximately but is rarely needed so going to assume it doesn't have 
+             * to be exact.
+             */
+            errorMessageToken = tokenScript;
+
+            /*
+             * Set up dictionary to translate state names to their index number.
+             */
+            stateIndices = new Dictionary<string, int> ();
+
+            /*
+             * Assign each state its own unique index.
+             * The default state gets 0.
+             */
+            nStates = 0;
+            tokenScript.defaultState.body.index = nStates ++;
+            stateIndices.Add ("default", 0);
+            foreach (KeyValuePair<string, TokenDeclState> kvp in tokenScript.states) {
+                TokenDeclState declState = kvp.Value;
+                declState.body.index = nStates ++;
+                stateIndices.Add (declState.name.val, declState.body.index);
+            }
+
+            /*
+             * Make up an array that translates state indices to state name strings.
+             */
+            stateNames = new string[nStates];
+            stateNames[0] = "default";
+            foreach (KeyValuePair<string, TokenDeclState> kvp in tokenScript.states) {
+                TokenDeclState declState = kvp.Value;
+                stateNames[declState.body.index] = declState.name.val;
+            }
+
+            /*
+             * Make sure we have delegates for all script-defined functions and methods,
+             * creating anonymous ones if needed.  Note that this includes all property 
+             * getter and setter methods.
+             */
+            foreach (TokenDeclVar declFunc in tokenScript.variablesStack) {
+                if (declFunc.retType != null) {
+                    declFunc.GetDelType ();
+                }
+            }
+            while (true) {
+                bool itIsAGoodDayToDie = true;
+                try {
+                    foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                        itIsAGoodDayToDie = false;
+                        if (sdType is TokenDeclSDTypeClass) {
+                            TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
+                            foreach (TokenDeclVar declFunc in sdtClass.members) {
+                                if (declFunc.retType != null) {
+                                    declFunc.GetDelType ();
+                                    if (declFunc.funcNameSig.val.StartsWith ("$ctor(")) {
+                                        // this is for the "$new()" static method that we create below.
+                                        // See GenerateStmtNewobj() etc.
+                                        new TokenTypeSDTypeDelegate (declFunc, sdtClass.MakeRefToken (declFunc), 
+                                                declFunc.argDecl.types, tokenScript);
+                                    }
+                                }
+                            }
+                        }
+                        if (sdType is TokenDeclSDTypeInterface) {
+                            TokenDeclSDTypeInterface sdtIFace = (TokenDeclSDTypeInterface)sdType;
+                            foreach (TokenDeclVar declFunc in sdtIFace.methsNProps) {
+                                if (declFunc.retType != null) {
+                                    declFunc.GetDelType ();
+                                }
+                            }
+                        }
+                        itIsAGoodDayToDie = true;
+                    }
+                    break;
+                } catch (InvalidOperationException) {
+                    if (!itIsAGoodDayToDie) throw;
+                    // fetching the delegate created an anonymous entry in tokenScript.sdSrcTypesValues
+                    // which made the foreach statement puque, so start over...
+                }
+            }
+
+            /*
+             * No more types can be defined or we won't be able to write them to the object file.
+             */
+            tokenScript.sdSrcTypesSeal ();
+
+            /*
+             * Assign all global variables a slot in its corresponding XMRInstance.gbl<Type>s[] array.
+             * Global variables are simply elements of those arrays at runtime, thus we don't need to create
+             * an unique class for each script, we can just use XMRInstance as is for all.
+             */
+            foreach (TokenDeclVar declVar in tokenScript.variablesStack) {
+
+                /*
+                 * Omit 'constant' variables as they are coded inline so don't need a slot.
+                 */
+                if (declVar.constant) continue;
+
+                /*
+                 * Do functions later.
+                 */
+                if (declVar.retType != null) continue;
+
+                /*
+                 * Create entry in the value array for the variable or property.
+                 */
+                declVar.location = new CompValuGlobalVar (declVar, glblSizes);
+            }
+
+            /*
+             * Likewise for any static fields in script-defined classes.
+             * They can be referenced anywhere by <typename>.<fieldname>, see 
+             * GenerateFromLValSField().
+             */
+            foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                if (!(sdType is TokenDeclSDTypeClass)) continue;
+                TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
+
+                foreach (TokenDeclVar declVar in sdtClass.members) {
+
+                    /*
+                     * Omit 'constant' variables as they are coded inline so don't need a slot.
+                     */
+                    if (declVar.constant) continue;
+
+                    /*
+                     * Do methods later.
+                     */
+                    if (declVar.retType != null) continue;
+
+                    /*
+                     * Ignore non-static fields for now.
+                     * They get assigned below.
+                     */
+                    if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) == 0) continue;
+
+                    /*
+                     * Create entry in the value array for the static field or static property.
+                     */
+                    declVar.location = new CompValuGlobalVar (declVar, glblSizes);
+                }
+            }
+
+            /*
+             * Assign slots for all interface method prototypes.
+             * These indices are used to index the array of delegates that holds a class' implementation of an 
+             * interface.
+             * Properties do not get a slot because they aren't called as such.  But their corresponding
+             * <name>$get() and <name>$set(<type>) methods are in the table and they each get a slot.
+             */
+            foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                if (!(sdType is TokenDeclSDTypeInterface)) continue;
+                TokenDeclSDTypeInterface sdtIFace = (TokenDeclSDTypeInterface)sdType;
+                int vti = 0;
+                foreach (TokenDeclVar im in sdtIFace.methsNProps) {
+                    if ((im.getProp == null) && (im.setProp == null)) {
+                        im.vTableIndex = vti ++;
+                    }
+                }
+            }
+
+            /*
+             * Assign slots for all instance fields and virtual methods of script-defined classes.
+             */
+            int maxExtends = tokenScript.sdSrcTypesCount;
+            bool didOne;
+            do {
+                didOne = false;
+                foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                    if (!(sdType is TokenDeclSDTypeClass)) continue;
+                    TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
+                    if (sdtClass.slotsAssigned) continue;
+
+                    /*
+                     * If this class extends another, the extended class has to already 
+                     * be set up, because our slots add on to the end of the extended class.
+                     */
+                    TokenDeclSDTypeClass extends = sdtClass.extends;
+                    if (extends != null) {
+                        if (!extends.slotsAssigned) continue;
+                        sdtClass.instSizes     = extends.instSizes;
+                        sdtClass.numVirtFuncs  = extends.numVirtFuncs;
+                        sdtClass.numInterfaces = extends.numInterfaces;
+
+                        int n = maxExtends;
+                        for (TokenDeclSDTypeClass ex = extends; ex != null; ex = ex.extends) {
+                            if (-- n < 0) break;
+                        }
+                        if (n < 0) {
+                            ErrorMsg (sdtClass, "loop in extended classes");
+                            sdtClass.slotsAssigned = true;
+                            continue;
+                        }
+                    }
+
+                    /*
+                     * Extended class's slots all assigned, assign our instance fields 
+                     * slots in the XMRSDTypeClObj arrays.
+                     */
+                    foreach (TokenDeclVar declVar in sdtClass.members) {
+                        if (declVar.retType != null) continue;
+                        if (declVar.constant) continue;
+                        if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) continue;
+                        if ((declVar.getProp == null) && (declVar.setProp == null)) {
+                            declVar.type.AssignVarSlot (declVar, sdtClass.instSizes);
+                        }
+                    }
+
+                    /*
+                     * ... and assign virtual method vtable slots.
+                     *
+                     *                   - : error if any overridden method, doesn't need a slot
+                     *            abstract : error if any overridden method, alloc new slot but leave it empty
+                     *                 new : ignore any overridden method, doesn't need a slot
+                     *        new abstract : ignore any overridden method, alloc new slot but leave it empty
+                     *            override : must have overridden abstract/virtual, use old slot
+                     *   override abstract : must have overridden abstract, use old slot but it is still empty
+                     *              static : error if any overridden method, doesn't need a slot
+                     *          static new : ignore any overridden method, doesn't need a slot
+                     *             virtual : error if any overridden method, alloc new slot and fill it in
+                     *         virtual new : ignore any overridden method, alloc new slot and fill it in
+                     */
+                    foreach (TokenDeclVar declFunc in sdtClass.members) {
+                        if (declFunc.retType == null) continue;
+                        curDeclFunc = declFunc;
+
+                        /*
+                         * See if there is a method in an extended class that this method overshadows.
+                         * If so, check for various conflicts.
+                         * In any case, SDT_NEW on our method means to ignore any overshadowed method.
+                         */
+                        string declLongName = sdtClass.longName.val + "." + declFunc.funcNameSig.val;
+                        uint declFlags = declFunc.sdtFlags;
+                        TokenDeclVar overridden = null;
+                        if ((declFlags & ScriptReduce.SDT_NEW) == 0) {
+                            for (TokenDeclSDTypeClass sdtd = extends; sdtd != null; sdtd = sdtd.extends) {
+                                overridden = FindExactWithRet (sdtd.members, declFunc.name, declFunc.retType, declFunc.argDecl.types);
+                                if (overridden != null) break;
+                            }
+                        }
+                        if (overridden != null) do {
+                            string overLongName = overridden.sdtClass.longName.val;
+                            uint overFlags = overridden.sdtFlags;
+
+                            /*
+                             * See if overridden method allows itself to be overridden.
+                             */
+                            if ((overFlags & ScriptReduce.SDT_ABSTRACT) != 0) {
+                                if ((declFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE)) == 0) {
+                                    ErrorMsg (declFunc, declLongName + " overshadows abstract " + overLongName + " but is not marked abstract, new or override");
+                                    break;
+                                }
+                            } else if ((overFlags & ScriptReduce.SDT_FINAL) != 0) {
+                                ErrorMsg (declFunc, declLongName + " overshadows final " + overLongName + " but is not marked new");
+                            } else if ((overFlags & (ScriptReduce.SDT_OVERRIDE | ScriptReduce.SDT_VIRTUAL)) != 0) {
+                                if ((declFlags & (ScriptReduce.SDT_NEW | ScriptReduce.SDT_OVERRIDE)) == 0) {
+                                    ErrorMsg (declFunc, declLongName + " overshadows virtual " + overLongName + " but is not marked new or override");
+                                    break;
+                                }
+                            } else {
+                                ErrorMsg (declFunc, declLongName + " overshadows non-virtual " + overLongName + " but is not marked new");
+                                break;
+                            }
+
+                            /*
+                             * See if our method is capable of overriding the other method.
+                             */
+                            if ((declFlags & ScriptReduce.SDT_ABSTRACT) != 0) {
+                                if ((overFlags & ScriptReduce.SDT_ABSTRACT) == 0) {
+                                    ErrorMsg (declFunc, declLongName + " abstract overshadows non-abstract " + overLongName + " but is not marked new");
+                                    break;
+                                }
+                            } else if ((declFlags & ScriptReduce.SDT_OVERRIDE) != 0) {
+                                if ((overFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE | ScriptReduce.SDT_VIRTUAL)) == 0) {
+                                    ErrorMsg (declFunc, declLongName + " override overshadows non-abstract/non-virtual " + overLongName);
+                                    break;
+                                }
+                            } else {
+                                ErrorMsg (declFunc, declLongName + " overshadows " + overLongName + " but is not marked new");
+                                break;
+                            }
+                        } while (false);
+
+                        /*
+                         * Now we can assign it a vtable slot if it needs one (ie, it is virtual).
+                         */
+                        declFunc.vTableIndex = -1;
+                        if (overridden != null) {
+                            declFunc.vTableIndex = overridden.vTableIndex;
+                        } else if ((declFlags & ScriptReduce.SDT_OVERRIDE) != 0) {
+                            ErrorMsg (declFunc, declLongName + " marked override but nothing matching found that it overrides");
+                        }
+                        if ((declFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_VIRTUAL)) != 0) {
+                            declFunc.vTableIndex = sdtClass.numVirtFuncs ++;
+                        }
+                    }
+                    curDeclFunc = null;
+
+                    /*
+                     * ... and assign implemented interface slots.
+                     * Note that our implementations of a given interface is completely independent of any 
+                     * rootward class's implementation of that same interface.
+                     */
+                    int nIFaces        = sdtClass.numInterfaces + sdtClass.implements.Count;
+                    sdtClass.iFaces    = new TokenDeclSDTypeInterface[nIFaces];
+                    sdtClass.iImplFunc = new TokenDeclVar[nIFaces][];
+                    for (int i = 0; i < sdtClass.numInterfaces; i ++) {
+                        sdtClass.iFaces[i]    = extends.iFaces[i];
+                        sdtClass.iImplFunc[i] = extends.iImplFunc[i];
+                    }
+
+                    foreach (TokenDeclSDTypeInterface intf in sdtClass.implements) {
+                        int i = sdtClass.numInterfaces ++;
+                        sdtClass.iFaces[i] = intf;
+                        sdtClass.intfIndices.Add (intf.longName.val, i);
+                        int nMeths = 0;
+                        foreach (TokenDeclVar m in intf.methsNProps) {
+                            if ((m.getProp == null) && (m.setProp == null)) nMeths ++;
+                        }
+                        sdtClass.iImplFunc[i] = new TokenDeclVar[nMeths];
+                    }
+
+                    foreach (TokenDeclVar classMeth in sdtClass.members) {
+                        if (classMeth.retType == null) continue;
+                        curDeclFunc = classMeth;
+                        for (TokenIntfImpl intfImpl = classMeth.implements; intfImpl != null; intfImpl = (TokenIntfImpl)intfImpl.nextToken) {
+
+                            /*
+                             * One of the class methods implements an interface method.
+                             * Try to find the interface method that is implemented and verify its signature.
+                             */
+                            TokenDeclSDTypeInterface intfType = intfImpl.intfType.decl;
+                            TokenDeclVar intfMeth = FindExactWithRet (intfType.methsNProps, intfImpl.methName, classMeth.retType, classMeth.argDecl.types);
+                            if (intfMeth == null) {
+                                ErrorMsg (intfImpl, "interface does not define method " + intfImpl.methName.val + classMeth.argDecl.GetArgSig ());
+                                continue;
+                            }
+
+                            /*
+                             * See if this class was declared to implement that interface.
+                             */
+                            bool found = false;
+                            foreach (TokenDeclSDTypeInterface intf in sdtClass.implements) {
+                                if (intf == intfType) {
+                                    found = true;
+                                    break;
+                                }
+                            }
+                            if (!found) {
+                                ErrorMsg (intfImpl, "class not declared to implement " + intfType.longName.val);
+                                continue;
+                            }
+
+                            /*
+                             * Get index in iFaces[] and iImplFunc[] arrays.
+                             * Start scanning from the end in case one of our rootward classes also implements the interface.
+                             * We should always be successful because we know by now that this class implements the interface.
+                             */
+                            int i;
+                            for (i = sdtClass.numInterfaces; -- i >= 0;) {
+                                if (sdtClass.iFaces[i] == intfType) break;
+                            }
+
+                            /*
+                             * Now remember which of the class methods implements that interface method.
+                             */
+                            int j = intfMeth.vTableIndex;
+                            if (sdtClass.iImplFunc[i][j] != null) {
+                                ErrorMsg (intfImpl, "also implemented by " + sdtClass.iImplFunc[i][j].funcNameSig.val);
+                                continue;
+                            }
+                            sdtClass.iImplFunc[i][j] = classMeth;
+                        }
+                    }
+                    curDeclFunc = null;
+
+                    /*
+                     * Now make sure this class implements all methods for all declared interfaces.
+                     */
+                    for (int i = sdtClass.numInterfaces - sdtClass.implements.Count; i < sdtClass.numInterfaces; i ++) {
+                        TokenDeclVar[] implementations = sdtClass.iImplFunc[i];
+                        for (int j = implementations.Length; -- j >= 0;) {
+                            if (implementations[j] == null) {
+                                TokenDeclSDTypeInterface intf = sdtClass.iFaces[i];
+                                TokenDeclVar meth = null;
+                                foreach (TokenDeclVar im in intf.methsNProps) {
+                                    if (im.vTableIndex == j) {
+                                        meth = im;
+                                        break;
+                                    }
+                                }
+                                ErrorMsg (sdtClass, "does not implement " + intf.longName.val + "." + meth.funcNameSig.val);
+                            }
+                        }
+                    }
+
+                    /*
+                     * All slots for this class have been assigned.
+                     */
+                    sdtClass.slotsAssigned = true;
+                    didOne = true;
+                }
+            } while (didOne);
+
+            /*
+             * Compute final values for all variables/fields declared as 'constant'.
+             * Note that there may be forward references.
+             */
+            do {
+                didOne = false;
+                foreach (TokenDeclVar tdv in tokenScript.variablesStack) {
+                    if (tdv.constant && !(tdv.init is TokenRValConst)) {
+                        tdv.init = tdv.init.TryComputeConstant (LookupInitConstants, ref didOne);
+                    }
+                }
+                foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                    if (!(sdType is TokenDeclSDTypeClass)) continue;
+                    currentSDTClass = (TokenDeclSDTypeClass)sdType;
+                    foreach (TokenDeclVar tdv in currentSDTClass.members) {
+                        if (tdv.constant && !(tdv.init is TokenRValConst)) {
+                            tdv.init = tdv.init.TryComputeConstant (LookupInitConstants, ref didOne);
+                        }
+                    }
+                }
+                currentSDTClass = null;
+            } while (didOne);
+
+            /*
+             * Now we should be able to assign all those constants their type and location.
+             */
+            foreach (TokenDeclVar tdv in tokenScript.variablesStack) {
+                if (tdv.constant) {
+                    if (tdv.init is TokenRValConst) {
+                        TokenRValConst rvc = (TokenRValConst)tdv.init;
+                        tdv.type = rvc.tokType;
+                        tdv.location = rvc.GetCompValu ();
+                    } else {
+                        ErrorMsg (tdv, "value is not constant");
+                    }
+                }
+            }
+            foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                if (!(sdType is TokenDeclSDTypeClass)) continue;
+                currentSDTClass = (TokenDeclSDTypeClass)sdType;
+                foreach (TokenDeclVar tdv in currentSDTClass.members) {
+                    if (tdv.constant) {
+                        if (tdv.init is TokenRValConst) {
+                            TokenRValConst rvc = (TokenRValConst)tdv.init;
+                            tdv.type = rvc.tokType;
+                            tdv.location = rvc.GetCompValu ();
+                        } else {
+                            ErrorMsg (tdv, "value is not constant");
+                        }
+                    }
+                }
+            }
+            currentSDTClass = null;
+
+            /*
+             * For all classes that define all the methods needed for the class, ie, they aren't abstract,
+             * define a static class.$new() method with same args as the $ctor(s).  This will allow the
+             * class to be instantiated via the new operator.
+             */
+            foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                if (!(sdType is TokenDeclSDTypeClass)) continue;
+                TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
+
+                /*
+                 * See if the class as it stands would be able to fill every slot of its vtable.
+                 */
+                bool[] filled = new bool[sdtClass.numVirtFuncs];
+                int numFilled = 0;
+                for (TokenDeclSDTypeClass sdtc = sdtClass; sdtc != null; sdtc = sdtc.extends) {
+                    foreach (TokenDeclVar tdf in sdtc.members) {
+                        if ((tdf.retType != null) && (tdf.vTableIndex >= 0) && ((tdf.sdtFlags & ScriptReduce.SDT_ABSTRACT) == 0)) {
+                            if (!filled[tdf.vTableIndex]) {
+                                filled[tdf.vTableIndex] = true;
+                                numFilled ++;
+                            }
+                        }
+                    }
+                }
+
+                /*
+                 * If so, define a static class.$new() method for every constructor defined for the class.
+                 * Give it the same access (private/protected/public) as the script declared for the constructor.
+                 * Note that the reducer made sure there is at least a default constructor for every class.
+                 */
+                if (numFilled >= sdtClass.numVirtFuncs) {
+                    List<TokenDeclVar> newobjDeclFuncs = new List<TokenDeclVar> ();
+                    foreach (TokenDeclVar ctorDeclFunc in sdtClass.members) {
+                        if ((ctorDeclFunc.funcNameSig != null) && ctorDeclFunc.funcNameSig.val.StartsWith ("$ctor(")) {
+                            TokenDeclVar newobjDeclFunc = DefineNewobjFunc (ctorDeclFunc);
+                            newobjDeclFuncs.Add (newobjDeclFunc);
+                        }
+                    }
+                    foreach (TokenDeclVar newobjDeclFunc in newobjDeclFuncs) {
+                        sdtClass.members.AddEntry (newobjDeclFunc);
+                    }
+                }
+            }
+
+            /*
+             * Write fixed portion of object file.
+             */
+            objFileWriter.Write (OBJECT_CODE_MAGIC.ToCharArray ());
+            objFileWriter.Write (COMPILED_VERSION_VALUE);
+            objFileWriter.Write (sourceHash);
+            objFileWriter.Write (tokenScript.expiryDays);
+            glblSizes.WriteToFile (objFileWriter);
+
+            objFileWriter.Write (nStates);
+            for (int i = 0; i < nStates; i ++) {
+                objFileWriter.Write (stateNames[i]);
+            }
+
+            /*
+             * For debugging, we also write out global variable array slot assignments.
+             */
+            foreach (TokenDeclVar declVar in tokenScript.variablesStack) {
+                if (declVar.retType == null) {
+                    WriteOutGblAssignment ("", declVar);
+                }
+            }
+            foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                if (!(sdType is TokenDeclSDTypeClass)) continue;
+                TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
+                foreach (TokenDeclVar declVar in sdtClass.members) {
+                    if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) {
+                        WriteOutGblAssignment (sdtClass.longName.val + ".", declVar);
+                    }
+                }
+            }
+            objFileWriter.Write ("");
+
+            /*
+             * Write out script-defined types.
+             */
+            foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                objFileWriter.Write (sdType.longName.val);
+                sdType.WriteToFile (objFileWriter);
+            }
+            objFileWriter.Write ("");
+
+            /*
+             * Output function headers then bodies.
+             * Do all headers first in case bodies do forward references.
+             * Do both global functions, script-defined class static methods and 
+             * script-defined instance methods, as we handle the differences
+             * during compilation of the functions/methods themselves.
+             */
+            for (int pass = 0; pass < 2; pass ++) {
+                foreach (TokenDeclVar declFunc in tokenScript.variablesStack) {
+                    if (declFunc.retType != null) {
+                        if (pass == 0) GenerateMethodHeader (declFunc);
+                                  else GenerateMethodBody   (declFunc);
+                    }
+                }
+                foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                    if (sdType is TokenDeclSDTypeClass) {
+                        TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
+                        foreach (TokenDeclVar declFunc in sdtClass.members) {
+                            if ((declFunc.retType != null) && ((declFunc.sdtFlags & ScriptReduce.SDT_ABSTRACT) == 0)) {
+                                if (pass == 0) GenerateMethodHeader (declFunc);
+                                          else GenerateMethodBody   (declFunc);
+                            }
+                        }
+                    }
+                }
+            }
+
+            /*
+             * Output default state event handler functions.
+             * Each event handler is a private static method named 'default <eventname>'.
+             * Splice in a default state_entry() handler if none defined so we can init global vars.
+             */
+            TokenDeclVar defaultStateEntry = null;
+            for (defaultStateEntry = tokenScript.defaultState.body.eventFuncs;
+                 defaultStateEntry != null;
+                 defaultStateEntry = (TokenDeclVar)defaultStateEntry.nextToken) {
+                if (defaultStateEntry.funcNameSig.val == "state_entry()") break;
+            }
+            if (defaultStateEntry == null) {
+                defaultStateEntry               = new TokenDeclVar   (tokenScript.defaultState.body, null, tokenScript);
+                defaultStateEntry.name          = new TokenName      (tokenScript.defaultState.body, "state_entry");
+                defaultStateEntry.retType       = new TokenTypeVoid  (tokenScript.defaultState.body);
+                defaultStateEntry.argDecl       = new TokenArgDecl   (tokenScript.defaultState.body);
+                defaultStateEntry.body          = new TokenStmtBlock (tokenScript.defaultState.body);
+                defaultStateEntry.body.function = defaultStateEntry;
+
+                defaultStateEntry.nextToken = tokenScript.defaultState.body.eventFuncs;
+                tokenScript.defaultState.body.eventFuncs = defaultStateEntry;
+            }
+            GenerateStateEventHandlers ("default", tokenScript.defaultState.body);
+
+            /*
+             * Output script-defined state event handler methods.
+             * Each event handler is a private static method named <statename> <eventname>
+             */
+            foreach (KeyValuePair<string, TokenDeclState> kvp in tokenScript.states) {
+                TokenDeclState declState = kvp.Value;
+                GenerateStateEventHandlers (declState.name.val, declState.body);
+            }
+
+            ScriptObjWriter.TheEnd (objFileWriter);
+        }
+
+        /**
+         * @brief Write out what slot was assigned for a global or sdtclass static variable.
+         *        Constants, functions, instance fields, methods, properties do not have slots in the global variables arrays.
+         */
+        private void WriteOutGblAssignment (string pfx, TokenDeclVar declVar)
+        {
+            if (!declVar.constant && (declVar.retType == null) && (declVar.getProp == null) && (declVar.setProp == null)) {
+                objFileWriter.Write (pfx + declVar.name.val);    // string
+                objFileWriter.Write (declVar.vTableArray.Name);  // string
+                objFileWriter.Write (declVar.vTableIndex);       // int
+            }
+        }
+
+        /**
+         * @brief generate event handler code
+         * Writes out a function definition for each state handler
+         * named <statename> <eventname>
+         *
+         * However, each has just 'XMRInstance __sw' as its single argument
+         * and each of its user-visible argments is extracted from __sw.ehArgs[].
+         *
+         * So we end up generating something like this:
+         *
+         *   private static void <statename> <eventname>(XMRInstance __sw)
+         *   {
+         *      <typeArg0> <nameArg0> = (<typeArg0>)__sw.ehArgs[0];
+         *      <typeArg1> <nameArg1> = (<typeArg1>)__sw.ehArgs[1];
+         *
+         *      ... script code ...
+         *   }
+         *
+         * The continuations code assumes there will be no references to ehArgs[]
+         * after the first call to CheckRun() as CheckRun() makes no attempt to
+         * serialize the ehArgs[] array, as doing so would be redundant.  Any values
+         * from ehArgs[] that are being used will be in local stack variables and
+         * thus preserved that way.
+         */
+        private void GenerateStateEventHandlers (string statename, TokenStateBody body)
+        {
+            Dictionary<string,TokenDeclVar> statehandlers = new Dictionary<string,TokenDeclVar> ();
+            for (Token t = body.eventFuncs; t != null; t = t.nextToken) {
+                TokenDeclVar tdv = (TokenDeclVar)t;
+                string eventname = tdv.GetSimpleName ();
+                if (statehandlers.ContainsKey (eventname)) {
+                    ErrorMsg (tdv, "event handler " + eventname + " already defined for state " + statename);
+                } else {
+                    statehandlers.Add (eventname, tdv);
+                    GenerateEventHandler (statename, tdv);
+                }
+            }
+        }
+
+        private void GenerateEventHandler (string statename, TokenDeclVar declFunc)
+        {
+            string eventname = declFunc.GetSimpleName ();
+            TokenArgDecl argDecl = declFunc.argDecl;
+
+            /*
+             * Make sure event handler name is valid and that number and type of arguments is correct.
+             * Apparently some scripts exist with fewer than correct number of args in their declaration 
+             * so allow for that.  It is ok because the handlers are called with the arguments in an
+             * object[] array, and we just won't access the missing argments in the vector.  But the 
+             * specified types must match one of the prototypes in legalEventHandlers.
+             */
+            TokenDeclVar protoDeclFunc = legalEventHandlers.FindExact (eventname, argDecl.types);
+            if (protoDeclFunc == null) {
+                ErrorMsg (declFunc, "unknown event handler " + eventname + argDecl.GetArgSig ());
+                return;
+            }
+
+            /*
+             * Output function header.
+             * They just have the XMRInstAbstract pointer as the one argument.
+             */
+            string functionName = statename + " " + eventname;
+            _ilGen = new ScriptObjWriter (tokenScript, 
+                                          functionName,
+                                          typeof (void),
+                                          instanceTypeArg,
+                                          instanceNameArg,
+                                          objFileWriter);
+            StartFunctionBody (declFunc);
+
+            /*
+             * Create a temp to hold XMRInstanceSuperType version of arg 0.
+             */
+            instancePointer = ilGen.DeclareLocal (xmrInstSuperType, "__xmrinst");
+            ilGen.Emit (declFunc, OpCodes.Ldarg_0);
+            ilGen.Emit (declFunc, OpCodes.Castclass, xmrInstSuperType);
+            ilGen.Emit (declFunc, OpCodes.Stloc, instancePointer);
+
+            /*
+             * Output args as variable definitions and initialize each from __sw.ehArgs[].
+             * If the script writer goofed, the typecast will complain.
+             */
+            int nArgs = argDecl.vars.Length;
+            for (int i = 0; i < nArgs; i ++) {
+
+                /*
+                 * Say that the argument variable is going to be located in a local var.
+                 */
+                TokenDeclVar argVar = argDecl.vars[i];
+                TokenType argTokType = argVar.type;
+                CompValuLocalVar local = new CompValuLocalVar (argTokType, argVar.name.val, this);
+                argVar.location = local;
+
+                /*
+                 * Copy from the ehArgs[i] element to the temp var.
+                 * Cast as needed, there is a lot of craziness like OpenMetaverse.Quaternion.
+                 */
+                local.PopPre (this, argVar.name);
+                PushXMRInst ();                                          // instance
+                ilGen.Emit (declFunc, OpCodes.Ldfld, ehArgsFieldInfo);   // instance.ehArgs (array of objects)
+                ilGen.Emit (declFunc, OpCodes.Ldc_I4, i);                // array index = i
+                ilGen.Emit (declFunc, OpCodes.Ldelem, typeof (object));  // select the argument we want
+                TokenType stkTokType = tokenTypeObj;                     // stack has a type 'object' on it now
+                Type argSysType = argTokType.ToSysType ();               // this is the type the script expects
+                if (argSysType == typeof (double)) {                // LSL_Float/double -> double
+                    ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapFloat);
+                    stkTokType = tokenTypeFlt;                       // stack has a type 'double' on it now
+                }
+                if (argSysType == typeof (int)) {                        // LSL_Integer/int -> int
+                    ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapInteger);
+                    stkTokType = tokenTypeInt;                       // stack has a type 'int' on it now
+                }
+                if (argSysType == typeof (LSL_List)) {                   // LSL_List -> LSL_List
+                    TypeCast.CastTopOfStack (this, argVar.name, stkTokType, argTokType, true);
+                    stkTokType = argTokType;                         // stack has a type 'LSL_List' on it now
+                }
+                if (argSysType == typeof (LSL_Rotation)) {               // OpenMetaverse.Quaternion/LSL_Rotation -> LSL_Rotation
+                    ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapRotation);
+                    stkTokType = tokenTypeRot;                       // stack has a type 'LSL_Rotation' on it now
+                }
+                if (argSysType == typeof (string)) {                     // LSL_Key/LSL_String/string -> string
+                    ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapString);
+                    stkTokType = tokenTypeStr;                       // stack has a type 'string' on it now
+                }
+                if (argSysType == typeof (LSL_Vector)) {                 // OpenMetaverse.Vector3/LSL_Vector -> LSL_Vector
+                    ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapVector);
+                    stkTokType = tokenTypeVec;                       // stack has a type 'LSL_Vector' on it now
+                }
+                local.PopPost (this, argVar.name, stkTokType);           // pop stack type into argtype
+            }
+
+            /*
+             * Output code for the statements and clean up.
+             */
+            GenerateFuncBody ();
+        }
+
+        /**
+         * @brief generate header for an arbitrary script-defined global function.
+         * @param declFunc = function being defined
+         */
+        private void GenerateMethodHeader (TokenDeclVar declFunc)
+        {
+            curDeclFunc = declFunc;
+
+            /*
+             * Make up array of all argument types as seen by the code generator.
+             * We splice in XMRInstanceSuperType or XMRSDTypeClObj for the first 
+             * arg as the function itself is static, followed by script-visible
+             * arg types.
+             */
+            TokenArgDecl argDecl = declFunc.argDecl;
+            int nArgs = argDecl.vars.Length;
+            Type[] argTypes = new Type[nArgs+1];
+            string[] argNames = new string[nArgs+1];
+            if (IsSDTInstMethod ()) {
+                argTypes[0] = typeof (XMRSDTypeClObj);
+                argNames[0] = "$sdtthis";
+            } else {
+                argTypes[0] = xmrInstSuperType;
+                argNames[0] = "$xmrthis";
+            }
+            for (int i = 0; i < nArgs; i ++) {
+                argTypes[i+1] = argDecl.vars[i].type.ToSysType ();
+                argNames[i+1] = argDecl.vars[i].name.val;
+            }
+
+            /*
+             * Set up entrypoint.
+             */
+            string objCodeName = declFunc.GetObjCodeName ();
+            declFunc.ilGen = new ScriptObjWriter (tokenScript, 
+                                                  objCodeName,
+                                                  declFunc.retType.ToSysType (),
+                                                  argTypes,
+                                                  argNames,
+                                                  objFileWriter);
+
+            /*
+             * This says how to generate a call to the function and to get a delegate.
+             */
+            declFunc.location = new CompValuGlobalMeth (declFunc);
+
+            curDeclFunc = null;
+        }
+
+        /**
+         * @brief generate code for an arbitrary script-defined function.
+         * @param name = name of the function
+         * @param argDecl = argument declarations
+         * @param body = function's code body
+         */
+        private void GenerateMethodBody (TokenDeclVar declFunc)
+        {
+            /*
+             * Set up code generator for the function's contents.
+             */
+            _ilGen = declFunc.ilGen;
+            StartFunctionBody (declFunc);
+
+            /*
+             * Create a temp to hold XMRInstanceSuperType version of arg 0.
+             * For most functions, arg 0 is already XMRInstanceSuperType.
+             * But for script-defined class instance methods, arg 0 holds
+             * the XMRSDTypeClObj pointer and so we read the XMRInstAbstract
+             * pointer from its XMRSDTypeClObj.xmrInst field then cast it to
+             * XMRInstanceSuperType.
+             */
+            if (IsSDTInstMethod ()) {
+                instancePointer = ilGen.DeclareLocal (xmrInstSuperType, "__xmrinst");
+                ilGen.Emit (declFunc, OpCodes.Ldarg_0);
+                ilGen.Emit (declFunc, OpCodes.Ldfld, sdtXMRInstFieldInfo);
+                ilGen.Emit (declFunc, OpCodes.Castclass, xmrInstSuperType);
+                ilGen.Emit (declFunc, OpCodes.Stloc, instancePointer);
+            }
+
+            /*
+             * Define location of all script-level arguments so script body can access them.
+             * The argument indices need to have +1 added to them because XMRInstance or 
+             * XMRSDTypeClObj is spliced in at arg 0.
+             */
+            TokenArgDecl argDecl = declFunc.argDecl;
+            int nArgs = argDecl.vars.Length;
+            for (int i = 0; i < nArgs; i ++) {
+                TokenDeclVar argVar = argDecl.vars[i];
+                argVar.location = new CompValuArg (argVar.type, i + 1);
+            }
+
+            /*
+             * Output code for the statements and clean up.
+             */
+            GenerateFuncBody ();
+        }
+
+        private void StartFunctionBody (TokenDeclVar declFunc)
+        {
+            /*
+             * Start current function being processed.
+             * Set 'mightGetHere' as the code at the top is always executed.
+             */
+            instancePointer = null;
+            mightGetHere    = true;
+            curBreakTarg    = null;
+            curContTarg     = null;
+            curDeclFunc     = declFunc;
+
+            /*
+             * Start generating code.
+             */
+            ((ScriptObjWriter)ilGen).BegMethod ();
+        }
+
+        /**
+         * @brief Define function for a script-defined type's <typename>.$new(<argsig>) method.
+         *        See GenerateStmtNewobj() for more info.
+         */
+        private TokenDeclVar DefineNewobjFunc (TokenDeclVar ctorDeclFunc)
+        {
+            /*
+             * Set up 'static classname $new(params-same-as-ctor) { }'.
+             */
+            TokenDeclVar newobjDeclFunc = new TokenDeclVar (ctorDeclFunc, null, tokenScript);
+            newobjDeclFunc.name         = new TokenName (newobjDeclFunc, "$new");
+            newobjDeclFunc.retType      = ctorDeclFunc.sdtClass.MakeRefToken (newobjDeclFunc);
+            newobjDeclFunc.argDecl      = ctorDeclFunc.argDecl;
+            newobjDeclFunc.sdtClass     = ctorDeclFunc.sdtClass;
+            newobjDeclFunc.sdtFlags     = ScriptReduce.SDT_STATIC | ctorDeclFunc.sdtFlags;
+
+            /*
+             * Declare local variable named '$objptr' in a frame just under 
+             * what the '$new(...)' function's arguments are declared in.
+             */
+            TokenDeclVar objptrVar = new TokenDeclVar (newobjDeclFunc, newobjDeclFunc, tokenScript);
+            objptrVar.type         = newobjDeclFunc.retType;
+            objptrVar.name         = new TokenName (newobjDeclFunc, "$objptr");
+            VarDict newFrame       = new VarDict (false);
+            newFrame.outerVarDict  = ctorDeclFunc.argDecl.varDict;
+            newFrame.AddEntry (objptrVar);
+
+            /*
+             * Set up '$objptr.$ctor'
+             */
+            TokenLValName objptrLValName  = new TokenLValName (objptrVar.name, newFrame);
+                                                                                   // ref a var by giving its name
+            TokenLValIField objptrDotCtor = new TokenLValIField (newobjDeclFunc);  // an instance member reference
+            objptrDotCtor.baseRVal        = objptrLValName;                        // '$objptr'
+            objptrDotCtor.fieldName       = ctorDeclFunc.name;                     // '.' '$ctor'
+
+            /*
+             * Set up '$objptr.$ctor(arglist)' call for use in the '$new(...)' body.
+             * Copy the arglist from the constructor declaration so triviality 
+             * processing will pick the correct overloaded constructor.
+             */
+            TokenRValCall callCtorRVal = new TokenRValCall (newobjDeclFunc);   // doing a call of some sort
+            callCtorRVal.meth          = objptrDotCtor;                        // calling $objptr.$ctor()
+            TokenDeclVar[] argList     = newobjDeclFunc.argDecl.vars;          // get args $new() was declared with
+            callCtorRVal.nArgs         = argList.Length;                       // ...that is nArgs we are passing to $objptr.$ctor()
+            for (int i = argList.Length; -- i >= 0;) {
+                TokenDeclVar arg          = argList[i];                    // find out about one of the args
+                TokenLValName argLValName = new TokenLValName (arg.name, ctorDeclFunc.argDecl.varDict);
+                                                                           // pass arg of that name to $objptr.$ctor()
+                argLValName.nextToken     = callCtorRVal.args;             // link to list of args passed to $objptr.$ctor()
+                callCtorRVal.args         = argLValName;
+            }
+
+            /*
+             * Set up a funky call to the constructor for the code body.
+             * This will let code generator know there is some craziness.
+             * See GenerateStmtNewobj().
+             *
+             * This is in essence:
+             *    {
+             *        classname $objptr = newobj (classname);
+             *        $objptr.$ctor (...);
+             *        return $objptr;
+             *    }
+             */
+            TokenStmtNewobj newobjStmtBody = new TokenStmtNewobj (ctorDeclFunc);
+            newobjStmtBody.objptrVar       = objptrVar;
+            newobjStmtBody.rValCall        = callCtorRVal;
+            TokenStmtBlock newobjBody      = new TokenStmtBlock (ctorDeclFunc);
+            newobjBody.statements          = newobjStmtBody;
+
+            /*
+             * Link that code as the body of the function.
+             */
+            newobjDeclFunc.body = newobjBody;
+
+            /*
+             * Say the function calls '$objptr.$ctor(arglist)' so we will inherit ctor's triviality.
+             */
+            newobjDeclFunc.unknownTrivialityCalls.AddLast (callCtorRVal);
+            return newobjDeclFunc;
+        }
+
+        private class TokenStmtNewobj : TokenStmt {
+            public TokenDeclVar objptrVar;
+            public TokenRValCall rValCall;
+            public TokenStmtNewobj (Token original) : base (original) { }
+        }
+
+        /**
+         * @brief Output function body (either event handler or script-defined method).
+         */
+        private void GenerateFuncBody ()
+        {
+            /*
+             * We want to know if the function's code is trivial, ie,
+             * if it doesn't have anything that might be an infinite 
+             * loop and that is doesn't call anything that might have 
+             * an infinite loop.  If it is, we don't need any CheckRun()
+             * stuff or any of the frame save/restore stuff.
+             */
+            bool isTrivial = curDeclFunc.IsFuncTrivial (this);
+
+            /*
+             * Clear list of all call labels.
+             * A call label is inserted just before every call that can possibly
+             * call CheckRun(), including any direct calls to CheckRun().
+             * Then, when restoring stack, we can just switch to this label to
+             * resume at the correct spot.
+             */
+            actCallLabels.Clear ();
+            allCallLabels.Clear ();
+            openCallLabel = null;
+
+            /*
+             * Alloc stack space for local vars.
+             */
+            AllocLocalVarStackSpace ();
+
+            /*
+             * Any return statements inside function body jump to this label
+             * after putting return value in __retval.
+             */
+            retLabel = ilGen.DefineLabel ("__retlbl");
+            retValue = null;
+            if (!(curDeclFunc.retType is TokenTypeVoid)) {
+                retValue = ilGen.DeclareLocal (curDeclFunc.retType.ToSysType (), "__retval");
+            }
+
+            /*
+             * Output:
+             *    int __mainCallNo = -1;
+             *    try {
+             *        if (instance.callMode != CallMode_NORMAL) goto __cmRestore;
+             */
+            actCallNo = null;
+            ScriptMyLabel cmRestore = null;
+            if (!isTrivial) {
+                actCallNo = ilGen.DeclareLocal (typeof (int), "__mainCallNo");
+                SetCallNo (curDeclFunc, actCallNo, -1);
+                cmRestore = ilGen.DefineLabel ("__cmRestore");
+                ilGen.BeginExceptionBlock ();
+                PushXMRInst ();
+                ilGen.Emit (curDeclFunc, OpCodes.Ldfld, ScriptCodeGen.callModeFieldInfo);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_NORMAL);
+                ilGen.Emit (curDeclFunc, OpCodes.Bne_Un, cmRestore);
+            }
+
+            /*
+             * Splice in the code optimizer for the body of the function.
+             */
+            ScriptCollector collector = new ScriptCollector ((ScriptObjWriter)ilGen);
+            _ilGen = collector;
+
+            /*
+             * If this is the default state_entry() handler, output code to set all global
+             * variables to their initial values.  Note that every script must have a
+             * default state_entry() handler, we provide one if the script doesn't explicitly
+             * define one.
+             */
+            string methname = ilGen.methName;
+            if (methname == "default state_entry") {
+
+                // if (!doGblInit) goto skipGblInit;
+                ScriptMyLabel skipGblInitLabel = ilGen.DefineLabel ("__skipGblInit");
+                PushXMRInst ();                                  // instance
+                ilGen.Emit (curDeclFunc, OpCodes.Ldfld, doGblInitFieldInfo);  // instance.doGblInit
+                ilGen.Emit (curDeclFunc, OpCodes.Brfalse, skipGblInitLabel);
+
+                // $globalvarinit();
+                TokenDeclVar gviFunc = tokenScript.globalVarInit;
+                if (gviFunc.body.statements != null) {
+                    gviFunc.location.CallPre  (this, gviFunc);
+                    gviFunc.location.CallPost (this, gviFunc);
+                }
+
+                // various $staticfieldinit();
+                foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
+                    if (sdType is TokenDeclSDTypeClass) {
+                        TokenDeclVar sfiFunc = ((TokenDeclSDTypeClass)sdType).staticFieldInit;
+                        if ((sfiFunc != null) && (sfiFunc.body.statements != null)) {
+                            sfiFunc.location.CallPre  (this, sfiFunc);
+                            sfiFunc.location.CallPost (this, sfiFunc);
+                        }
+                    }
+                }
+
+                // doGblInit = 0;
+                PushXMRInst ();                                  // instance
+                ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4_0);
+                ilGen.Emit (curDeclFunc, OpCodes.Stfld, doGblInitFieldInfo);  // instance.doGblInit
+
+                //skipGblInit:
+                ilGen.MarkLabel (skipGblInitLabel);
+            }
+
+            /*
+             * If this is a script-defined type constructor, call the base constructor and call
+             * this class's $instfieldinit() method to initialize instance fields.
+             */
+            if ((curDeclFunc.sdtClass != null) && curDeclFunc.funcNameSig.val.StartsWith ("$ctor(")) {
+                if (curDeclFunc.baseCtorCall != null) {
+                    GenerateFromRValCall (curDeclFunc.baseCtorCall);
+                }
+                TokenDeclVar ifiFunc = ((TokenDeclSDTypeClass)curDeclFunc.sdtClass).instFieldInit;
+                if (ifiFunc.body.statements != null) {
+                    CompValu thisCompValu = new CompValuArg (ifiFunc.sdtClass.MakeRefToken (ifiFunc), 0);
+                    CompValu ifiFuncLocn  = new CompValuInstMember (ifiFunc, thisCompValu, true);
+                    ifiFuncLocn.CallPre  (this, ifiFunc);
+                    ifiFuncLocn.CallPost (this, ifiFunc);
+                }
+            }
+
+            /*
+             * See if time to suspend in case they are doing a loop with recursion.
+             */
+            if (!isTrivial) EmitCallCheckRun (curDeclFunc, true);
+
+            /*
+             * Output code body.
+             */
+            GenerateStmtBlock (curDeclFunc.body);
+
+            /*
+             * If code falls through to this point, means they are missing 
+             * a return statement.  And that is legal only if the function 
+             * returns 'void'.
+             */
+            if (mightGetHere) {
+                if (!(curDeclFunc.retType is TokenTypeVoid)) {
+                    ErrorMsg (curDeclFunc.body, "missing final return statement");
+                }
+                ilGen.Emit (curDeclFunc, OpCodes.Leave, retLabel);
+            }
+
+            /*
+             * End of the code to be optimized.
+             * Do optimizations then write it all out to object file.
+             * After this, all code gets written directly to object file.
+             * Optimization must be completed before we scan the allCallLabels
+             * list below to look for active locals and temps.
+             */
+            collector.Optimize ();
+            _ilGen = collector.WriteOutAll ();
+            collector = null;
+
+            /*
+             * Output code to restore stack frame from stream.
+             * It jumps back to the call labels within the function body.
+             */
+            List<ScriptMyLocal> activeTemps = null;
+            if (!isTrivial) {
+
+                /*
+                 * Build list of locals and temps active at all the call labels.
+                 */
+                activeTemps = new List<ScriptMyLocal> ();
+                foreach (CallLabel cl in allCallLabels) {
+                    foreach (ScriptMyLocal lcl in cl.callLabel.whereAmI.localsReadBeforeWritten) {
+                        if (!activeTemps.Contains (lcl)) {
+                            activeTemps.Add (lcl);
+                        }
+                    }
+                }
+
+                /*
+                 * Output code to restore the args, locals and temps then jump to
+                 * the call label that we were interrupted at.
+                 */
+                ilGen.MarkLabel (cmRestore);
+                GenerateFrameRestoreCode (activeTemps);
+            }
+
+            /*
+             * Output epilog that saves stack frame state if CallMode_SAVE.
+             *
+             *   finally {
+             *      if (instance.callMode != CallMode_SAVE) goto __endFin;
+             *      GenerateFrameCaptureCode();
+             *   __endFin:
+             *   }
+             */
+            ScriptMyLabel endFin = null;
+            if (!isTrivial) {
+                ilGen.BeginFinallyBlock ();
+                endFin = ilGen.DefineLabel ("__endFin");
+                PushXMRInst ();
+                ilGen.Emit (curDeclFunc, OpCodes.Ldfld, callModeFieldInfo);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_SAVE);
+                ilGen.Emit (curDeclFunc, OpCodes.Bne_Un, endFin);
+                GenerateFrameCaptureCode (activeTemps);
+                ilGen.MarkLabel (endFin);
+                ilGen.Emit (curDeclFunc, OpCodes.Endfinally);
+                ilGen.EndExceptionBlock ();
+            }
+
+            /*
+             * Output the 'real' return opcode.
+             */
+            ilGen.MarkLabel (retLabel);
+            if (!(curDeclFunc.retType is TokenTypeVoid)) {
+                ilGen.Emit (curDeclFunc, OpCodes.Ldloc, retValue);
+            }
+            ilGen.Emit (curDeclFunc, OpCodes.Ret);
+            retLabel = null;
+            retValue = null;
+
+            /*
+             * No more instructions for this method.
+             */
+            ((ScriptObjWriter)ilGen).EndMethod ();
+            _ilGen = null;
+
+            /*
+             * Not generating function code any more.
+             */
+            curBreakTarg = null;
+            curContTarg  = null;
+            curDeclFunc  = null;
+        }
+
+        /**
+         * @brief Allocate stack space for all local variables, regardless of
+         *        which { } statement block they are actually defined in.
+         */
+        private void AllocLocalVarStackSpace ()
+        {
+            foreach (TokenDeclVar localVar in curDeclFunc.localVars) {
+
+                /*
+                 * Skip all 'constant' vars as they were handled by the reducer.
+                 */
+                if (localVar.constant) continue;
+
+                /*
+                 * Get a stack location for the local variable.
+                 */
+                localVar.location = new CompValuLocalVar (localVar.type, localVar.name.val, this);
+            }
+        }
+
+        /**
+         * @brief Generate code to write all arguments and locals to the capture stack frame.
+         *        This includes temp variables.
+         *        We only need to save what is active at the point of callLabels through because 
+         *        those are the only points we will jump to on restore.  This saves us from saving 
+         *        all the little temp vars we create.
+         * @param activeTemps = list of locals and temps that we care about, ie, which
+         *                      ones get restored by GenerateFrameRestoreCode().
+         */
+        private void GenerateFrameCaptureCode (List<ScriptMyLocal> activeTemps)
+        {
+            /*
+             * Compute total number of slots we need to save stuff.
+             * Assume we need to save all call arguments.
+             */
+            int nSaves = curDeclFunc.argDecl.vars.Length + activeTemps.Count;
+
+            /*
+             * Output code to allocate a stack frame object with an object array.
+             * This also pushes the stack frame object on the instance.stackFrames list.
+             * It returns a pointer to the object array it allocated.
+             */
+            PushXMRInst ();
+            ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName);
+            GetCallNo (curDeclFunc, actCallNo);
+            ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, nSaves);
+            ilGen.Emit (curDeclFunc, OpCodes.Call, captureStackFrameMethodInfo);
+
+            if (DEBUG_STACKCAPRES) {
+                ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName + "*: capture mainCallNo=");
+                ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldloc, actCallNo);
+                ilGen.Emit (curDeclFunc, OpCodes.Box, typeof (int));
+                ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+            }
+
+            /*
+             * Copy arg values to object array, boxing as needed.
+             */
+            int i = 0;
+            foreach (TokenDeclVar argVar in curDeclFunc.argDecl.varDict) {
+                ilGen.Emit (curDeclFunc, OpCodes.Dup);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i);
+                argVar.location.PushVal (this, argVar.name, tokenTypeObj);
+                if (DEBUG_STACKCAPRES) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n    arg:" + argVar.name.val + "=");
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+                    ilGen.Emit (curDeclFunc, OpCodes.Dup);
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+                }
+                ilGen.Emit (curDeclFunc, OpCodes.Stelem_Ref);
+                i ++;
+            }
+
+            /*
+             * Copy local and temp values to object array, boxing as needed.
+             */
+            foreach (ScriptMyLocal lcl in activeTemps) {
+                ilGen.Emit (curDeclFunc, OpCodes.Dup);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i ++);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldloc, lcl);
+                Type t = lcl.type;
+                if (t == typeof (HeapTrackerList)) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, heapTrackerListPush);
+                    t = typeof (LSL_List);
+                }
+                if (t == typeof (HeapTrackerObject)) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, heapTrackerObjectPush);
+                    t = typeof (object);
+                }
+                if (t == typeof (HeapTrackerString)) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, heapTrackerStringPush);
+                    t = typeof (string);
+                }
+                if (t.IsValueType) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Box, t);
+                }
+                if (DEBUG_STACKCAPRES) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n    lcl:" + lcl.name + "=");
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+                    ilGen.Emit (curDeclFunc, OpCodes.Dup);
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+                }
+                ilGen.Emit (curDeclFunc, OpCodes.Stelem_Ref);
+            }
+            if (DEBUG_STACKCAPRES) {
+                ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n");
+                ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+            }
+
+            ilGen.Emit (curDeclFunc, OpCodes.Pop);
+        }
+
+        /**
+         * @brief Generate code to restore all arguments and locals from the restore stack frame.
+         *        This includes temp variables.
+         */
+        private void GenerateFrameRestoreCode (List<ScriptMyLocal> activeTemps)
+        {
+            ScriptMyLocal objArray = ilGen.DeclareLocal (typeof (object[]), "__restObjArray");
+
+            /*
+             * Output code to pop stack frame from instance.stackFrames.
+             * It returns a pointer to the object array that contains values to be restored.
+             */
+            PushXMRInst ();
+            ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName);
+            ilGen.Emit (curDeclFunc, OpCodes.Ldloca, actCallNo);  // __mainCallNo
+            ilGen.Emit (curDeclFunc, OpCodes.Call, restoreStackFrameMethodInfo);
+            ilGen.Emit (curDeclFunc, OpCodes.Stloc, objArray);
+            if (DEBUG_STACKCAPRES) {
+                ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName + "*: restore mainCallNo=");
+                ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldloc, actCallNo);
+                ilGen.Emit (curDeclFunc, OpCodes.Box, typeof (int));
+                ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+            }
+
+            /*
+             * Restore argument values from object array, unboxing as needed.
+             * Although the caller has restored them to what it called us with, it's possible that this 
+             * function has modified them since, so we need to do our own restore.
+             */
+            int i = 0;
+            foreach (TokenDeclVar argVar in curDeclFunc.argDecl.varDict) {
+                CompValu argLoc = argVar.location;
+                argLoc.PopPre (this, argVar.name);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldloc, objArray);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldelem_Ref);
+                if (DEBUG_STACKCAPRES) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n    arg:" + argVar.name.val + "=");
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+                    ilGen.Emit (curDeclFunc, OpCodes.Dup);
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+                }
+                TypeCast.CastTopOfStack (this, argVar.name, tokenTypeObj, argLoc.type, true);
+                argLoc.PopPost (this, argVar.name);
+                i ++;
+            }
+
+            /*
+             * Restore local and temp values from object array, unboxing as needed.
+             */
+            foreach (ScriptMyLocal lcl in activeTemps) {
+                Type t = lcl.type;
+                Type u = t;
+                if (t == typeof (HeapTrackerList))   u = typeof (LSL_List);
+                if (t == typeof (HeapTrackerObject)) u = typeof (object);
+                if (t == typeof (HeapTrackerString)) u = typeof (string);
+                if (u != t) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Ldloc, lcl);
+                }
+                ilGen.Emit (curDeclFunc, OpCodes.Ldloc, objArray);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i ++);
+                ilGen.Emit (curDeclFunc, OpCodes.Ldelem_Ref);
+                if (DEBUG_STACKCAPRES) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n    lcl:" + lcl.name + "=");
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+                    ilGen.Emit (curDeclFunc, OpCodes.Dup);
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+                }
+                if (u.IsValueType) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Unbox_Any, u);
+                } else if (u != typeof (object)) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Castclass, u);
+                }
+                if (u != t) {
+                    ilGen.Emit (curDeclFunc, OpCodes.Call, t.GetMethod ("Pop", new Type[] { u }));
+                } else {
+                    ilGen.Emit (curDeclFunc, OpCodes.Stloc, lcl);
+                }
+            }
+            if (DEBUG_STACKCAPRES) {
+                ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n");
+                ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
+            }
+
+            OutputCallNoSwitchStmt ();
+        }
+
+        /**
+         * @brief Output a switch statement with a case for each possible 
+         *        value of whatever callNo is currently active, either 
+         *        __mainCallNo or one of the try/catch/finally's callNos.
+         *
+         *   switch (callNo) {
+         *      case 0: goto __call_0;
+         *      case 1: goto __call_1;
+         *      ...
+         *   }
+         *   throw new ScriptBadCallNoException (callNo);
+         */
+        private void OutputCallNoSwitchStmt ()
+        {
+            ScriptMyLabel[] callLabels = new ScriptMyLabel[actCallLabels.Count];
+            foreach (CallLabel cl in actCallLabels) {
+                callLabels[cl.index] = cl.callLabel;
+            }
+            GetCallNo (curDeclFunc, actCallNo);
+            ilGen.Emit (curDeclFunc, OpCodes.Switch, callLabels);
+
+            GetCallNo (curDeclFunc, actCallNo);
+            ilGen.Emit (curDeclFunc, OpCodes.Newobj, scriptBadCallNoExceptionConstructorInfo);
+            ilGen.Emit (curDeclFunc, OpCodes.Throw);
+        }
+
+        /**
+         * @brief There is one of these per call that can possibly call CheckRun(),
+         *        including direct calls to CheckRun().
+         *        They mark points that the stack capture/restore code will save & restore to.
+         *        All object-code level local vars active at the call label's point will 
+         *        be saved & restored.
+         *
+         *            callNo = 5;
+         *        __call_5:
+         *            push call arguments from temps
+         *            call SomethingThatCallsCheckRun()
+         *
+         *        If SomethingThatCallsCheckRun() actually calls CheckRun(), our restore code
+         *        will restore our args, locals & temps, then jump to __call_5, which will then 
+         *        call SomethingThatCallsCheckRun() again, which will restore its stuff likewise.
+         *        When eventually the actual CheckRun() call is restored, it will turn off restore 
+         *        mode (by changing callMode from CallMode_RESTORE to CallMode_NORMAL) and return, 
+         *        allowing the code to run normally from that point.
+         */
+        public class CallLabel {
+            public int           index;       // sequential integer, starting at 0, within actCallLabels
+                                              // - used for the switch statement
+            public ScriptMyLabel callLabel;   // the actual label token
+
+            public CallLabel (ScriptCodeGen scg, Token errorAt)
+            {
+                if (scg.openCallLabel != null) throw new Exception ("call label already open");
+
+                if (!scg.curDeclFunc.IsFuncTrivial (scg)) {
+                    this.index = scg.actCallLabels.Count;
+                    string name = "__call_" + index + "_" + scg.allCallLabels.Count;
+
+                    /*
+                     * Make sure eval stack is empty because the frame capture/restore 
+                     * code expects such (restore switch stmt has an empty stack).
+                     */
+                    int depth = ((ScriptCollector)scg.ilGen).stackDepth.Count;
+                    if (depth > 0) {
+                        // maybe need to call Trivialize()
+                        throw new Exception ("call label stack depth " + depth + " at " + errorAt.SrcLoc);
+                    }
+
+                    /*
+                     * Eval stack is empty so the restore code can handle it.
+                     */
+                    this.index = scg.actCallLabels.Count;
+                    scg.actCallLabels.AddLast (this);
+                    scg.allCallLabels.AddLast (this);
+                    this.callLabel = scg.ilGen.DefineLabel (name);
+                    scg.SetCallNo (errorAt, scg.actCallNo, this.index);
+                    scg.ilGen.MarkLabel (this.callLabel);
+                }
+
+                scg.openCallLabel = this;
+            }
+        };
+
+        /**
+         * @brief generate code for an arbitrary statement.
+         */
+        private void GenerateStmt (TokenStmt stmt)
+        {
+            errorMessageToken = stmt;
+            if (stmt is TokenDeclVar)       { GenerateDeclVar       ((TokenDeclVar)stmt);       return; }
+            if (stmt is TokenStmtBlock)     { GenerateStmtBlock     ((TokenStmtBlock)stmt);     return; }
+            if (stmt is TokenStmtBreak)     { GenerateStmtBreak     ((TokenStmtBreak)stmt);     return; }
+            if (stmt is TokenStmtCont)      { GenerateStmtCont      ((TokenStmtCont)stmt);      return; }
+            if (stmt is TokenStmtDo)        { GenerateStmtDo        ((TokenStmtDo)stmt);        return; }
+            if (stmt is TokenStmtFor)       { GenerateStmtFor       ((TokenStmtFor)stmt);       return; }
+            if (stmt is TokenStmtForEach)   { GenerateStmtForEach   ((TokenStmtForEach)stmt);   return; }
+            if (stmt is TokenStmtIf)        { GenerateStmtIf        ((TokenStmtIf)stmt);        return; }
+            if (stmt is TokenStmtJump)      { GenerateStmtJump      ((TokenStmtJump)stmt);      return; }
+            if (stmt is TokenStmtLabel)     { GenerateStmtLabel     ((TokenStmtLabel)stmt);     return; }
+            if (stmt is TokenStmtNewobj)    { GenerateStmtNewobj    ((TokenStmtNewobj)stmt);    return; }
+            if (stmt is TokenStmtNull)      {                                                   return; }
+            if (stmt is TokenStmtRet)       { GenerateStmtRet       ((TokenStmtRet)stmt);       return; }
+            if (stmt is TokenStmtRVal)      { GenerateStmtRVal      ((TokenStmtRVal)stmt);      return; }
+            if (stmt is TokenStmtState)     { GenerateStmtState     ((TokenStmtState)stmt);     return; }
+            if (stmt is TokenStmtSwitch)    { GenerateStmtSwitch    ((TokenStmtSwitch)stmt);    return; }
+            if (stmt is TokenStmtThrow)     { GenerateStmtThrow     ((TokenStmtThrow)stmt);     return; }
+            if (stmt is TokenStmtTry)       { GenerateStmtTry       ((TokenStmtTry)stmt);       return; }
+            if (stmt is TokenStmtVarIniDef) { GenerateStmtVarIniDef ((TokenStmtVarIniDef)stmt); return; }
+            if (stmt is TokenStmtWhile)     { GenerateStmtWhile     ((TokenStmtWhile)stmt);     return; }
+            throw new Exception ("unknown TokenStmt type " + stmt.GetType ().ToString ());
+        }
+
+        /**
+         * @brief generate statement block (ie, with braces)
+         */
+        private void GenerateStmtBlock (TokenStmtBlock stmtBlock)
+        {
+            if (!mightGetHere) return;
+
+            /*
+             * Push new current statement block pointer for anyone who cares.
+             */
+            TokenStmtBlock oldStmtBlock = curStmtBlock;
+            curStmtBlock = stmtBlock;
+
+            /*
+             * Output the statements that make up the block.
+             */
+            for (Token t = stmtBlock.statements; t != null; t = t.nextToken) {
+                GenerateStmt ((TokenStmt)t);
+            }
+
+            /*
+             * Pop the current statement block.
+             */
+            curStmtBlock = oldStmtBlock;
+        }
+
+        /**
+         * @brief output code for a 'break' statement
+         */
+        private void GenerateStmtBreak (TokenStmtBreak breakStmt)
+        {
+            if (!mightGetHere) return;
+
+            /*
+             * Make sure we are in a breakable situation.
+             */
+            if (curBreakTarg == null) {
+                ErrorMsg (breakStmt, "not in a breakable situation");
+                return;
+            }
+
+            /*
+             * Tell anyone who cares that the break target was actually used.
+             */
+            curBreakTarg.used = true;
+
+            /*
+             * Output the instructions.
+             */
+            EmitJumpCode (curBreakTarg.label, curBreakTarg.block, breakStmt);
+        }
+
+        /**
+         * @brief output code for a 'continue' statement
+         */
+        private void GenerateStmtCont (TokenStmtCont contStmt)
+        {
+            if (!mightGetHere) return;
+
+            /*
+             * Make sure we are in a contable situation.
+             */
+            if (curContTarg == null) {
+                ErrorMsg (contStmt, "not in a continueable situation");
+                return;
+            }
+
+            /*
+             * Tell anyone who cares that the continue target was actually used.
+             */
+            curContTarg.used = true;
+
+            /*
+             * Output the instructions.
+             */
+            EmitJumpCode (curContTarg.label, curContTarg.block, contStmt);
+        }
+
+        /**
+         * @brief output code for a 'do' statement
+         */
+        private void GenerateStmtDo (TokenStmtDo doStmt)
+        {
+            if (!mightGetHere) return;
+
+            BreakContTarg oldBreakTarg = curBreakTarg;
+            BreakContTarg oldContTarg  = curContTarg;
+            ScriptMyLabel loopLabel    = ilGen.DefineLabel ("doloop_" + doStmt.Unique);
+
+            curBreakTarg = new BreakContTarg (this, "dobreak_" + doStmt.Unique);
+            curContTarg  = new BreakContTarg (this, "docont_"  + doStmt.Unique);
+
+            ilGen.MarkLabel (loopLabel);
+            GenerateStmt (doStmt.bodyStmt);
+            if (curContTarg.used) {
+                ilGen.MarkLabel (curContTarg.label);
+                mightGetHere = true;
+            }
+
+            if (mightGetHere) {
+                EmitCallCheckRun (doStmt, false);
+                CompValu testRVal = GenerateFromRVal (doStmt.testRVal);
+                if (IsConstBoolExprTrue (testRVal)) {
+
+                    /*
+                     * Unconditional looping, unconditional branch and
+                     * say we never fall through to next statement.
+                     */
+                    ilGen.Emit (doStmt, OpCodes.Br, loopLabel);
+                    mightGetHere = false;
+                } else {
+
+                    /*
+                     * Conditional looping, test and brach back to top of loop.
+                     */
+                    testRVal.PushVal (this, doStmt.testRVal, tokenTypeBool);
+                    ilGen.Emit (doStmt, OpCodes.Brtrue, loopLabel);
+                }
+            }
+
+            /*
+             * If 'break' statement was used, output target label.
+             * And assume that since a 'break' statement was used, it's possible for the code to get here.
+             */
+            if (curBreakTarg.used) {
+                ilGen.MarkLabel (curBreakTarg.label);
+                mightGetHere = true;
+            }
+
+            curBreakTarg = oldBreakTarg;
+            curContTarg  = oldContTarg;
+        }
+
+        /**
+         * @brief output code for a 'for' statement
+         */
+        private void GenerateStmtFor (TokenStmtFor forStmt)
+        {
+            if (!mightGetHere) return;
+
+            BreakContTarg oldBreakTarg = curBreakTarg;
+            BreakContTarg oldContTarg  = curContTarg;
+            ScriptMyLabel loopLabel    = ilGen.DefineLabel ("forloop_" + forStmt.Unique);
+
+            curBreakTarg = new BreakContTarg (this, "forbreak_" + forStmt.Unique);
+            curContTarg  = new BreakContTarg (this, "forcont_"  + forStmt.Unique);
+
+            if (forStmt.initStmt != null) {
+                GenerateStmt (forStmt.initStmt);
+            }
+            ilGen.MarkLabel (loopLabel);
+
+            /*
+             * See if we have a test expression that is other than a constant TRUE.
+             * If so, test it and conditionally branch to end if false.
+             */
+            if (forStmt.testRVal != null) {
+                CompValu testRVal = GenerateFromRVal (forStmt.testRVal);
+                if (!IsConstBoolExprTrue (testRVal)) {
+                    testRVal.PushVal (this, forStmt.testRVal, tokenTypeBool);
+                    ilGen.Emit (forStmt, OpCodes.Brfalse, curBreakTarg.label);
+                    curBreakTarg.used = true;
+                }
+            }
+
+            /*
+             * Output loop body.
+             */
+            GenerateStmt (forStmt.bodyStmt);
+
+            /*
+             * Here's where a 'continue' statement jumps to.
+             */
+            if (curContTarg.used) {
+                ilGen.MarkLabel (curContTarg.label);
+                mightGetHere = true;
+            }
+
+            if (mightGetHere) {
+
+                /*
+                 * After checking for excessive CPU time, output increment statement, if any.
+                 */
+                EmitCallCheckRun (forStmt, false);
+                if (forStmt.incrRVal != null) {
+                    GenerateFromRVal (forStmt.incrRVal);
+                }
+
+                /*
+                 * Unconditional branch back to beginning of loop.
+                 */
+                ilGen.Emit (forStmt, OpCodes.Br, loopLabel);
+            }
+
+            /*
+             * If test needs label, output label for it to jump to.
+             * Otherwise, clear mightGetHere as we know loop never
+             * falls out the bottom.
+             */
+            mightGetHere = curBreakTarg.used;
+            if (mightGetHere) {
+                ilGen.MarkLabel (curBreakTarg.label);
+            }
+
+            curBreakTarg = oldBreakTarg;
+            curContTarg  = oldContTarg;
+        }
+
+        private void GenerateStmtForEach (TokenStmtForEach forEachStmt)
+        {
+            if (!mightGetHere) return;
+
+            BreakContTarg oldBreakTarg = curBreakTarg;
+            BreakContTarg oldContTarg  = curContTarg;
+            CompValu      keyLVal      = null;
+            CompValu      valLVal      = null;
+            CompValu      arrayRVal    = GenerateFromRVal (forEachStmt.arrayRVal);
+
+            if (forEachStmt.keyLVal != null) {
+                keyLVal = GenerateFromLVal (forEachStmt.keyLVal);
+                if (!(keyLVal.type is TokenTypeObject)) {
+                    ErrorMsg (forEachStmt.arrayRVal, "must be object");
+                }
+            }
+            if (forEachStmt.valLVal != null) {
+                valLVal = GenerateFromLVal (forEachStmt.valLVal);
+                if (!(valLVal.type is TokenTypeObject)) {
+                    ErrorMsg (forEachStmt.arrayRVal, "must be object");
+                }
+            }
+            if (!(arrayRVal.type is TokenTypeArray)) {
+                ErrorMsg (forEachStmt.arrayRVal, "must be an array");
+            }
+
+            curBreakTarg = new BreakContTarg (this, "foreachbreak_" + forEachStmt.Unique);
+            curContTarg  = new BreakContTarg (this, "foreachcont_"  + forEachStmt.Unique);
+
+            CompValuTemp indexVar = new CompValuTemp (new TokenTypeInt (forEachStmt), this);
+            ScriptMyLabel loopLabel = ilGen.DefineLabel ("foreachloop_" + forEachStmt.Unique);
+
+            // indexVar = 0
+            ilGen.Emit (forEachStmt, OpCodes.Ldc_I4_0);
+            indexVar.Pop (this, forEachStmt);
+
+            ilGen.MarkLabel (loopLabel);
+
+            // key = array.__pub_index (indexVar);
+            // if (key == null) goto curBreakTarg;
+            if (keyLVal != null) {
+                keyLVal.PopPre (this, forEachStmt.keyLVal);
+                arrayRVal.PushVal (this, forEachStmt.arrayRVal);
+                indexVar.PushVal (this, forEachStmt);
+                ilGen.Emit (forEachStmt, OpCodes.Call, xmrArrPubIndexMethod);
+                keyLVal.PopPost (this, forEachStmt.keyLVal);
+                keyLVal.PushVal (this, forEachStmt.keyLVal);
+                ilGen.Emit (forEachStmt, OpCodes.Brfalse, curBreakTarg.label);
+                curBreakTarg.used = true;
+            }
+
+            // val = array._pub_value (indexVar);
+            // if (val == null) goto curBreakTarg;
+            if (valLVal != null) {
+                valLVal.PopPre (this, forEachStmt.valLVal);
+                arrayRVal.PushVal (this, forEachStmt.arrayRVal);
+                indexVar.PushVal (this, forEachStmt);
+                ilGen.Emit (forEachStmt, OpCodes.Call, xmrArrPubValueMethod);
+                valLVal.PopPost (this, forEachStmt.valLVal);
+                if (keyLVal == null) {
+                    valLVal.PushVal (this, forEachStmt.valLVal);
+                    ilGen.Emit (forEachStmt, OpCodes.Brfalse, curBreakTarg.label);
+                    curBreakTarg.used = true;
+                }
+            }
+
+            // indexVar ++;
+            indexVar.PushVal (this, forEachStmt);
+            ilGen.Emit (forEachStmt, OpCodes.Ldc_I4_1);
+            ilGen.Emit (forEachStmt, OpCodes.Add);
+            indexVar.Pop (this, forEachStmt);
+
+            // body statement
+            GenerateStmt (forEachStmt.bodyStmt);
+
+            // continue label
+            if (curContTarg.used) {
+                ilGen.MarkLabel (curContTarg.label);
+                mightGetHere = true;
+            }
+
+            // call CheckRun()
+            if (mightGetHere) {
+                EmitCallCheckRun (forEachStmt, false);
+                ilGen.Emit (forEachStmt, OpCodes.Br, loopLabel);
+            }
+
+            // break label
+            ilGen.MarkLabel (curBreakTarg.label);
+            mightGetHere = true;
+
+            curBreakTarg = oldBreakTarg;
+            curContTarg  = oldContTarg;
+        }
+
+        /**
+         * @brief output code for an 'if' statement
+         * Braces are necessary because what may be one statement for trueStmt or elseStmt in
+         * the script may translate to more than one statement in the resultant C# code.
+         */
+        private void GenerateStmtIf (TokenStmtIf ifStmt)
+        {
+            if (!mightGetHere) return;
+
+            bool constVal;
+
+            /*
+             * Test condition and see if constant test expression.
+             */
+            CompValu testRVal = GenerateFromRVal (ifStmt.testRVal);
+            if (IsConstBoolExpr (testRVal, out constVal)) {
+
+                /*
+                 * Constant, output just either the true or else part.
+                 */
+                if (constVal) {
+                    GenerateStmt (ifStmt.trueStmt);
+                } else if (ifStmt.elseStmt != null) {
+                    GenerateStmt (ifStmt.elseStmt);
+                }
+            } else if (ifStmt.elseStmt == null) {
+
+                /*
+                 * This is an 'if' statement without an 'else' clause.
+                 */
+                testRVal.PushVal (this, ifStmt.testRVal, tokenTypeBool);
+                ScriptMyLabel doneLabel = ilGen.DefineLabel ("ifdone_" + ifStmt.Unique);
+                ilGen.Emit (ifStmt, OpCodes.Brfalse, doneLabel);  // brfalse doneLabel
+                GenerateStmt (ifStmt.trueStmt);                   // generate true body code
+                ilGen.MarkLabel (doneLabel);
+                mightGetHere = true;                              // there's always a possibility of getting here
+            } else {
+
+                /*
+                 * This is an 'if' statement with an 'else' clause.
+                 */
+                testRVal.PushVal (this, ifStmt.testRVal, tokenTypeBool);
+                ScriptMyLabel elseLabel = ilGen.DefineLabel ("ifelse_" + ifStmt.Unique);
+                ilGen.Emit (ifStmt, OpCodes.Brfalse, elseLabel);  // brfalse elseLabel
+                GenerateStmt (ifStmt.trueStmt);                   // generate true body code
+                bool trueMightGetHere = mightGetHere;             // save whether or not true falls through
+                ScriptMyLabel doneLabel = ilGen.DefineLabel ("ifdone_" + ifStmt.Unique);
+                ilGen.Emit (ifStmt, OpCodes.Br, doneLabel);       // branch to done
+                ilGen.MarkLabel (elseLabel);                      // beginning of else code
+                mightGetHere = true;                              // the top of the else might be executed
+                GenerateStmt (ifStmt.elseStmt);                   // output else code
+                ilGen.MarkLabel (doneLabel);                      // where end of true clause code branches to
+                mightGetHere |= trueMightGetHere;                 // gets this far if either true or else falls through
+            }
+        }
+
+        /**
+         * @brief output code for a 'jump' statement
+         */
+        private void GenerateStmtJump (TokenStmtJump jumpStmt)
+        {
+            if (!mightGetHere) return;
+
+            /*
+             * Make sure the target label is defined somewhere in the function.
+             */
+            TokenStmtLabel stmtLabel;
+            if (!curDeclFunc.labels.TryGetValue (jumpStmt.label.val, out stmtLabel)) {
+                ErrorMsg (jumpStmt, "undefined label " + jumpStmt.label.val);
+                return;
+            }
+            if (!stmtLabel.labelTagged) {
+                stmtLabel.labelStruct = ilGen.DefineLabel ("jump_" + stmtLabel.name.val);
+                stmtLabel.labelTagged = true;
+            }
+
+            /*
+             * Emit instructions to do the jump.
+             */
+            EmitJumpCode (stmtLabel.labelStruct, stmtLabel.block, jumpStmt);
+        }
+
+        /**
+         * @brief Emit code to jump to a label
+         * @param target = label being jumped to
+         * @param targetsBlock = { ... } the label is defined in
+         */
+        private void EmitJumpCode (ScriptMyLabel target, TokenStmtBlock targetsBlock, Token errorAt)
+        {
+            /*
+             * Jumps never fall through.
+             */
+            mightGetHere = false;
+
+            /*
+             * Find which block the target label is in.  Must be in this or an outer block,
+             * no laterals allowed.  And if we exit a try/catch block, use Leave instead of Br.
+             *
+             *    jump lateral;
+             *    {
+             *        @lateral;
+             *    }
+             */
+            bool useLeave = false;
+            TokenStmtBlock stmtBlock;
+            Stack<TokenStmtTry> finallyBlocksCalled = new Stack<TokenStmtTry> ();
+            for (stmtBlock = curStmtBlock; stmtBlock != targetsBlock; stmtBlock = stmtBlock.outerStmtBlock) {
+                if (stmtBlock == null) {
+                    ErrorMsg (errorAt, "no lateral jumps allowed");
+                    return;
+                }
+                if (stmtBlock.isFinally) {
+                    ErrorMsg (errorAt, "cannot jump out of finally");
+                    return;
+                }
+                if (stmtBlock.isTry || stmtBlock.isCatch) useLeave = true;
+                if ((stmtBlock.tryStmt != null) && (stmtBlock.tryStmt.finallyStmt != null)) {
+                    finallyBlocksCalled.Push (stmtBlock.tryStmt);
+                }
+            }
+
+            /*
+             * If popping through more than one finally block, we have to break it down for the stack 
+             * capture and restore code, one finally block at a time.
+             *
+             *     try {
+             *         try {
+             *             try {
+             *                 jump exit;
+             *             } finally {
+             *                 llOwnerSay ("exiting inner");
+             *             }
+             *         } finally {
+             *             llOwnerSay ("exiting middle");
+             *         }
+             *     } finally {
+             *         llOwnerSay ("exiting outer");
+             *     }
+             *   @exit;
+             *
+             *     try {
+             *         try {
+             *             try {
+             *                 jump intr2_exit;         <<< gets its own tryNo call label so inner try knows where to restore to
+             *             } finally {
+             *                 llOwnerSay ("exiting inner");
+             *             }
+             *             jump outtry2;
+             *           @intr2_exit; jump intr1_exit;  <<< gets its own tryNo call label so middle try knows where to restore to
+             *           @outtry2;
+             *         } finally {
+             *             llOwnerSay ("exiting middle");
+             *         }
+             *         jump outtry1;
+             *       @intr1_exit: jump exit;            <<< gets its own tryNo call label so outer try knows where to restore to
+             *       @outtry1;
+             *     } finally {
+             *         llOwnerSay ("exiting outer");
+             *     }
+             *   @exit;
+             */
+            int level = 0;
+            while (finallyBlocksCalled.Count > 1) {
+                TokenStmtTry finallyBlock = finallyBlocksCalled.Pop ();
+                string intername = "intr" + (++ level) + "_" + target.name;
+                IntermediateLeave iLeave;
+                if (!finallyBlock.iLeaves.TryGetValue (intername, out iLeave)) {
+                    iLeave = new IntermediateLeave ();
+                    iLeave.jumpIntoLabel = ilGen.DefineLabel (intername);
+                    iLeave.jumpAwayLabel = target;
+                    finallyBlock.iLeaves.Add (intername, iLeave);
+                }
+                target = iLeave.jumpIntoLabel;
+            }
+
+            /*
+             * Finally output the branch/leave opcode.
+             * If using Leave, prefix with a call label in case the corresponding finally block
+             * calls CheckRun() and that CheckRun() captures the stack, it will have a point to 
+             * restore to that will properly jump back into the finally block.
+             */
+            if (useLeave) {
+                new CallLabel (this, errorAt);
+                ilGen.Emit (errorAt, OpCodes.Leave, target);
+                openCallLabel = null;
+            } else {
+                ilGen.Emit (errorAt, OpCodes.Br, target);
+            }
+        }
+
+        /**
+         * @brief output code for a jump target label statement.
+         * If there are any backward jumps to the label, do a CheckRun() also.
+         */
+        private void GenerateStmtLabel (TokenStmtLabel labelStmt)
+        {
+            if (!labelStmt.labelTagged) {
+                labelStmt.labelStruct = ilGen.DefineLabel ("jump_" + labelStmt.name.val);
+                labelStmt.labelTagged = true;
+            }
+            ilGen.MarkLabel (labelStmt.labelStruct);
+            if (labelStmt.hasBkwdRefs) {
+                EmitCallCheckRun (labelStmt, false);
+            }
+
+            /*
+             * We are going to say that the label falls through.
+             * It would be nice if we could analyze all referencing
+             * goto's to see if all of them are not used but we are
+             * going to assume that if the script writer put a label
+             * somewhere, it is probably going to be used.
+             */
+            mightGetHere = true;
+        }
+
+        /**
+         * @brief Generate code for a script-defined type's <typename>.$new(<argsig>) method.
+         *        It is used to malloc the object and initialize it.
+         *        It is defined as a script-defined type static method, so the object level
+         *        method gets the XMRInstance pointer passed as arg 0, and the method is 
+         *        supposed to return the allocated and constructed XMRSDTypeClObj
+         *        object pointer.
+         */
+        private void GenerateStmtNewobj (TokenStmtNewobj newobjStmt)
+        {
+            /*
+             * First off, malloc a new empty XMRSDTypeClObj object
+             * then call the XMRSDTypeClObj()-level constructor.
+             * Store the result in local var $objptr.
+             */
+            newobjStmt.objptrVar.location.PopPre (this, newobjStmt);
+            ilGen.Emit (newobjStmt, OpCodes.Ldarg_0);
+            ilGen.Emit (newobjStmt, OpCodes.Ldc_I4, curDeclFunc.sdtClass.sdTypeIndex);
+            ilGen.Emit (newobjStmt, OpCodes.Newobj, sdtClassConstructorInfo);
+            newobjStmt.objptrVar.location.PopPost (this, newobjStmt);
+
+            /*
+             * Now call the script-level constructor.
+             * Pass the object pointer in $objptr as it's 'this' argument.
+             * The rest of the args are the script-visible args and are just copied from $new() call.
+             */
+            GenerateFromRValCall (newobjStmt.rValCall);
+
+            /*
+             * Put object pointer in retval so it gets returned to caller.
+             */
+            newobjStmt.objptrVar.location.PushVal (this, newobjStmt);
+            ilGen.Emit (newobjStmt, OpCodes.Stloc, retValue);
+
+            /*
+             * Exit the function like a return statement.
+             * And thus we don't fall through.
+             */
+            ilGen.Emit (newobjStmt, OpCodes.Leave, retLabel);
+            mightGetHere = false;
+        }
+
+        /**
+         * @brief output code for a return statement.
+         * @param retStmt = return statement token, including return value if any
+         */
+        private void GenerateStmtRet (TokenStmtRet retStmt)
+        {
+            if (!mightGetHere) return;
+
+            for (TokenStmtBlock stmtBlock = curStmtBlock; stmtBlock != null; stmtBlock = stmtBlock.outerStmtBlock) {
+                if (stmtBlock.isFinally) {
+                    ErrorMsg (retStmt, "cannot return out of finally");
+                    return;
+                }
+            }
+
+            if (curDeclFunc.retType is TokenTypeVoid) {
+                if (retStmt.rVal != null) {
+                    ErrorMsg (retStmt, "function returns void, no value allowed");
+                    return;
+                }
+            } else {
+                if (retStmt.rVal == null) {
+                    ErrorMsg (retStmt, "function requires return value type " + curDeclFunc.retType.ToString ());
+                    return;
+                }
+                CompValu rVal = GenerateFromRVal (retStmt.rVal);
+                rVal.PushVal (this, retStmt.rVal, curDeclFunc.retType);
+                ilGen.Emit (retStmt, OpCodes.Stloc, retValue);
+            }
+
+            /*
+             * Use a OpCodes.Leave instruction to break out of any try { } blocks.
+             * All Leave's inside script-defined try { } need call labels (see GenerateStmtTry()).
+             */
+            bool brokeOutOfTry = false;
+            for (TokenStmtBlock stmtBlock = curStmtBlock; stmtBlock != null; stmtBlock = stmtBlock.outerStmtBlock) {
+                if (stmtBlock.isTry) {
+                    brokeOutOfTry = true;
+                    break;
+                }
+            }
+            if (brokeOutOfTry) new CallLabel (this, retStmt);
+            ilGen.Emit (retStmt, OpCodes.Leave, retLabel);
+            if (brokeOutOfTry) openCallLabel = null;
+
+            /*
+             * 'return' statements never fall through.
+             */
+            mightGetHere = false;
+        }
+
+        /**
+         * @brief the statement is just an expression, most likely an assignment or a ++ or -- thing.
+         */
+        private void GenerateStmtRVal (TokenStmtRVal rValStmt)
+        {
+            if (!mightGetHere) return;
+
+            GenerateFromRVal (rValStmt.rVal);
+        }
+
+        /**
+         * @brief generate code for a 'state' statement that transitions state.
+         * It sets the new state by throwing a ScriptChangeStateException.
+         */
+        private void GenerateStmtState (TokenStmtState stateStmt)
+        {
+            if (!mightGetHere) return;
+
+            int index = 0;  // 'default' state
+
+            /*
+             * Set new state value by throwing an exception.
+             * These exceptions aren't catchable by script-level try { } catch { }.
+             */
+            if ((stateStmt.state != null) && !stateIndices.TryGetValue (stateStmt.state.val, out index)) {
+                // The moron XEngine compiles scripts that reference undefined states.
+                // So rather than produce a compile-time error, we'll throw an exception at runtime.
+                // ErrorMsg (stateStmt, "undefined state " + stateStmt.state.val);
+
+                // throw new UndefinedStateException (stateStmt.state.val);
+                ilGen.Emit (stateStmt, OpCodes.Ldstr, stateStmt.state.val);
+                ilGen.Emit (stateStmt, OpCodes.Newobj, scriptUndefinedStateExceptionConstructorInfo);
+            } else {
+                ilGen.Emit (stateStmt, OpCodes.Ldc_I4, index);  // new state's index
+                ilGen.Emit (stateStmt, OpCodes.Newobj, scriptChangeStateExceptionConstructorInfo);
+            }
+            ilGen.Emit (stateStmt, OpCodes.Throw);
+
+            /*
+             * 'state' statements never fall through.
+             */
+            mightGetHere = false;
+        }
+
+        /**
+         * @brief output code for a 'switch' statement
+         */
+        private void GenerateStmtSwitch (TokenStmtSwitch switchStmt)
+        {
+            if (!mightGetHere) return;
+
+            /*
+             * Output code to calculate index.
+             */
+            CompValu testRVal = GenerateFromRVal (switchStmt.testRVal);
+
+            /*
+             * Generate code based on string or integer index.
+             */
+            if ((testRVal.type is TokenTypeKey) || (testRVal.type is TokenTypeStr)) {
+                GenerateStmtSwitchStr (testRVal, switchStmt);
+            } else {
+                GenerateStmtSwitchInt (testRVal, switchStmt);
+            }
+        }
+
+        private void GenerateStmtSwitchInt (CompValu testRVal, TokenStmtSwitch switchStmt)
+        {
+            testRVal.PushVal (this, switchStmt.testRVal, tokenTypeInt);
+
+            BreakContTarg   oldBreakTarg = curBreakTarg;
+            ScriptMyLabel   defaultLabel = null;
+            TokenSwitchCase sortedCases  = null;
+            TokenSwitchCase defaultCase  = null;
+
+            curBreakTarg = new BreakContTarg (this, "switchbreak_" + switchStmt.Unique);
+
+            /*
+             * Build list of cases sorted by ascending values.
+             * There should not be any overlapping of values.
+             */
+            for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
+                thisCase.label = ilGen.DefineLabel ("case_" + thisCase.Unique);
+
+                /*
+                 * The default case if any, goes in its own separate slot.
+                 */
+                if (thisCase.rVal1 == null) {
+                    if (defaultCase != null) {
+                        ErrorMsg (thisCase, "only one default case allowed");
+                        ErrorMsg (defaultCase, "...prior default case");
+                        return;
+                    }
+                    defaultCase  = thisCase;
+                    defaultLabel = thisCase.label;
+                    continue;
+                }
+
+                /*
+                 * Evaluate case operands, they must be compile-time integer constants.
+                 */
+                CompValu rVal = GenerateFromRVal (thisCase.rVal1);
+                if (!IsConstIntExpr (rVal, out thisCase.val1)) {
+                    ErrorMsg (thisCase.rVal1, "must be compile-time char or integer constant");
+                    return;
+                }
+                thisCase.val2 = thisCase.val1;
+                if (thisCase.rVal2 != null) {
+                    rVal = GenerateFromRVal (thisCase.rVal2);
+                    if (!IsConstIntExpr (rVal, out thisCase.val2)) {
+                        ErrorMsg (thisCase.rVal2, "must be compile-time char or integer constant");
+                        return;
+                    }
+                }
+                if (thisCase.val2 < thisCase.val1) {
+                    ErrorMsg (thisCase.rVal2, "must be .ge. first value for the case");
+                    return;
+                }
+
+                /*
+                 * Insert into list, sorted by value.
+                 * Note that both limits are inclusive.
+                 */
+                TokenSwitchCase lastCase = null;
+                TokenSwitchCase nextCase;
+                for (nextCase = sortedCases; nextCase != null; nextCase = nextCase.nextSortedCase) {
+                    if (nextCase.val1 >  thisCase.val2) break;
+                    if (nextCase.val2 >= thisCase.val1) {
+                        ErrorMsg (thisCase, "value used by previous case");
+                        ErrorMsg (nextCase, "...previous case");
+                        return;
+                    }
+                    lastCase = nextCase;
+                }
+                thisCase.nextSortedCase = nextCase;
+                if (lastCase == null) {
+                    sortedCases = thisCase;
+                } else {
+                    lastCase.nextSortedCase = thisCase;
+                }
+            }
+
+            if (defaultLabel == null) {
+                defaultLabel = ilGen.DefineLabel ("default_" + switchStmt.Unique);
+            }
+
+            /*
+             * Output code to jump to the case statement's labels based on integer index on stack.
+             * Note that each case still has the integer index on stack when jumped to.
+             */
+            int offset = 0;
+            for (TokenSwitchCase thisCase = sortedCases; thisCase != null;) {
+
+                /*
+                 * Scan through list of cases to find the maximum number of cases who's numvalues-to-case ratio
+                 * is from 0.5 to 2.0.  If such a group is found, use a CIL switch for them.  If not, just use a
+                 * compare-and-branch for the current case.
+                 */
+                int numCases  = 0;
+                int numFound  = 0;
+                int lowValue  = thisCase.val1;
+                int numValues = 0;
+                for (TokenSwitchCase scanCase = thisCase; scanCase != null; scanCase = scanCase.nextSortedCase) {
+                    int nVals = scanCase.val2 - thisCase.val1 + 1;
+                    double ratio = (double)nVals / (double)(++ numCases);
+                    if ((ratio >= 0.5) && (ratio <= 2.0)) {
+                        numFound  = numCases;
+                        numValues = nVals;
+                    }
+                }
+                if (numFound > 1) {
+
+                    /*
+                     * There is a group of case's, starting with thisCase, that fall within our criteria, ie, 
+                     * that have a nice density of meaningful jumps.
+                     *
+                     * So first generate an array of jumps to the default label (explicit or implicit).
+                     */
+                    ScriptMyLabel[] labels = new ScriptMyLabel[numValues];
+                    for (int i = 0; i < numValues; i ++) {
+                        labels[i] = defaultLabel;
+                    }
+
+                    /*
+                     * Next, for each case in that group, fill in the corresponding array entries to jump to
+                     * that case's label.
+                     */
+                    do {
+                        for (int i = thisCase.val1; i <= thisCase.val2; i ++) {
+                            labels[i-lowValue] = thisCase.label;
+                        }
+                        thisCase = thisCase.nextSortedCase;
+                    } while (-- numFound > 0);
+
+                    /*
+                     * Subtract the low value and do the computed jump.
+                     * The OpCodes.Switch falls through if out of range (unsigned compare).
+                     */
+                    if (offset != lowValue) {
+                        ilGen.Emit (switchStmt, OpCodes.Ldc_I4, lowValue - offset);
+                        ilGen.Emit (switchStmt, OpCodes.Sub);
+                        offset = lowValue;
+                    }
+                    ilGen.Emit (switchStmt, OpCodes.Dup);
+                    ilGen.Emit (switchStmt, OpCodes.Switch, labels);
+                } else {
+
+                    /*
+                     * It's not economical to do with a computed jump, so output a subtract/compare/branch
+                     * for thisCase.
+                     */
+                    if (lowValue == thisCase.val2) {
+                        ilGen.Emit (switchStmt, OpCodes.Dup);
+                        ilGen.Emit (switchStmt, OpCodes.Ldc_I4, lowValue - offset);
+                        ilGen.Emit (switchStmt, OpCodes.Beq, thisCase.label);
+                    } else {
+                        if (offset != lowValue) {
+                            ilGen.Emit (switchStmt, OpCodes.Ldc_I4, lowValue - offset);
+                            ilGen.Emit (switchStmt, OpCodes.Sub);
+                            offset = lowValue;
+                        }
+                        ilGen.Emit (switchStmt, OpCodes.Dup);
+                        ilGen.Emit (switchStmt, OpCodes.Ldc_I4, thisCase.val2 - offset);
+                        ilGen.Emit (switchStmt, OpCodes.Ble_Un, thisCase.label);
+                    }
+                    thisCase = thisCase.nextSortedCase;
+                }
+            }
+            ilGen.Emit (switchStmt, OpCodes.Br, defaultLabel);
+
+            /*
+             * Output code for the cases themselves, in the order given by the programmer, 
+             * so they fall through as programmer wants.  This includes the default case, if any.
+             *
+             * Each label is jumped to with the index still on the stack.  So pop it off in case
+             * the case body does a goto outside the switch or a return.  If the case body might
+             * fall through to the next case or the bottom of the switch, push a zero so the stack
+             * matches in all cases.
+             */
+            for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
+                ilGen.MarkLabel (thisCase.label);   // the branch comes here
+                ilGen.Emit (thisCase, OpCodes.Pop); // pop the integer index off stack
+                mightGetHere = true;            // it's possible to get here
+                for (TokenStmt stmt = thisCase.stmts; stmt != null; stmt = (TokenStmt)(stmt.nextToken)) {
+                    GenerateStmt (stmt);        // output the case/explicit default body
+                }
+                if (mightGetHere) {
+                    ilGen.Emit (thisCase, OpCodes.Ldc_I4_0);
+                                    // in case we fall through, push a dummy integer index
+                }
+            }
+
+            /*
+             * If no explicit default case, output the default label here.
+             */
+            if (defaultCase == null) {
+                ilGen.MarkLabel (defaultLabel);
+                mightGetHere = true;
+            }
+
+            /*
+             * If the last case of the switch falls through out the bottom,
+             * we have to pop the index still on the stack.
+             */
+            if (mightGetHere) {
+                ilGen.Emit (switchStmt, OpCodes.Pop);
+            }
+
+            /*
+             * Output the 'break' statement target label.
+             * Note that the integer index is not on the stack at this point.
+             */
+            if (curBreakTarg.used) {
+                ilGen.MarkLabel (curBreakTarg.label);
+                mightGetHere = true;
+            }
+
+            curBreakTarg = oldBreakTarg;
+        }
+
+        private void GenerateStmtSwitchStr (CompValu testRVal, TokenStmtSwitch switchStmt)
+        {
+            BreakContTarg   oldBreakTarg = curBreakTarg;
+            ScriptMyLabel   defaultLabel = null;
+            TokenSwitchCase caseTreeTop  = null;
+            TokenSwitchCase defaultCase  = null;
+
+            curBreakTarg = new BreakContTarg (this, "switchbreak_" + switchStmt.Unique);
+
+            /*
+             * Make sure value is in a temp so we don't compute it more than once.
+             */
+            if (!(testRVal is CompValuTemp)) {
+                CompValuTemp temp = new CompValuTemp (testRVal.type, this);
+                testRVal.PushVal (this, switchStmt);
+                temp.Pop (this, switchStmt);
+                testRVal = temp;
+            }
+
+            /*
+             * Build tree of cases.
+             * There should not be any overlapping of values.
+             */
+            for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
+                thisCase.label = ilGen.DefineLabel ("case");
+
+                /*
+                 * The default case if any, goes in its own separate slot.
+                 */
+                if (thisCase.rVal1 == null) {
+                    if (defaultCase != null) {
+                        ErrorMsg (thisCase, "only one default case allowed");
+                        ErrorMsg (defaultCase, "...prior default case");
+                        return;
+                    }
+                    defaultCase  = thisCase;
+                    defaultLabel = thisCase.label;
+                    continue;
+                }
+
+                /*
+                 * Evaluate case operands, they must be compile-time string constants.
+                 */
+                CompValu rVal = GenerateFromRVal (thisCase.rVal1);
+                if (!IsConstStrExpr (rVal, out thisCase.str1)) {
+                    ErrorMsg (thisCase.rVal1, "must be compile-time string constant");
+                    continue;
+                }
+                thisCase.str2 = thisCase.str1;
+                if (thisCase.rVal2 != null) {
+                    rVal = GenerateFromRVal (thisCase.rVal2);
+                    if (!IsConstStrExpr (rVal, out thisCase.str2)) {
+                        ErrorMsg (thisCase.rVal2, "must be compile-time string constant");
+                        continue;
+                    }
+                }
+                if (String.Compare (thisCase.str2, thisCase.str1, StringComparison.Ordinal) < 0) {
+                    ErrorMsg (thisCase.rVal2, "must be .ge. first value for the case");
+                    continue;
+                }
+
+                /*
+                 * Insert into list, sorted by value.
+                 * Note that both limits are inclusive.
+                 */
+                caseTreeTop = InsertCaseInTree (caseTreeTop, thisCase);
+            }
+
+            /*
+             * Balance tree so we end up generating code that does O(log2 n) comparisons.
+             */
+            caseTreeTop = BalanceTree (caseTreeTop);
+
+            /*
+             * Output compare and branch instructions in a tree-like fashion so we do O(log2 n) comparisons.
+             */
+            if (defaultLabel == null) {
+                defaultLabel = ilGen.DefineLabel ("default");
+            }
+            OutputStrCase (testRVal, caseTreeTop, defaultLabel);
+
+            /*
+             * Output code for the cases themselves, in the order given by the programmer, 
+             * so they fall through as programmer wants.  This includes the default case, if any.
+             */
+            for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
+                ilGen.MarkLabel (thisCase.label);   // the branch comes here
+                mightGetHere = true;            // it's possible to get here
+                for (TokenStmt stmt = thisCase.stmts; stmt != null; stmt = (TokenStmt)(stmt.nextToken)) {
+                    GenerateStmt (stmt);        // output the case/explicit default body
+                }
+            }
+
+            /*
+             * If no explicit default case, output the default label here.
+             */
+            if (defaultCase == null) {
+                ilGen.MarkLabel (defaultLabel);
+                mightGetHere = true;
+            }
+
+            /*
+             * Output the 'break' statement target label.
+             */
+            if (curBreakTarg.used) {
+                ilGen.MarkLabel (curBreakTarg.label);
+                mightGetHere = true;
+            }
+
+            curBreakTarg = oldBreakTarg;
+        }
+
+        /**
+         * @brief Insert a case in a tree of cases
+         * @param r = root of existing cases to insert into
+         * @param n = new case being inserted
+         * @returns new root with new case inserted
+         */
+        private TokenSwitchCase InsertCaseInTree (TokenSwitchCase r, TokenSwitchCase n)
+        {
+            if (r == null) return n;
+
+            TokenSwitchCase t = r;
+            while (true) {
+                if (String.Compare (n.str2, t.str1, StringComparison.Ordinal) < 0) {
+                    if (t.lowerCase == null) {
+                        t.lowerCase = n;
+                        break;
+                    }
+                    t = t.lowerCase;
+                    continue;
+                }
+                if (String.Compare (n.str1, t.str2, StringComparison.Ordinal) > 0) {
+                    if (t.higherCase == null) {
+                        t.higherCase = n;
+                        break;
+                    }
+                    t = t.higherCase;
+                    continue;
+                }
+                ErrorMsg (n, "duplicate case");
+                ErrorMsg (r, "...duplicate of");
+                break;
+            }
+            return r;
+        }
+
+        /**
+         * @brief Balance a tree so left & right halves contain same number within +-1
+         * @param r = root of tree to balance
+         * @returns new root
+         */
+        private static TokenSwitchCase BalanceTree (TokenSwitchCase r)
+        {
+            if (r == null) return r;
+
+            int lc = CountTree (r.lowerCase);
+            int hc = CountTree (r.higherCase);
+            TokenSwitchCase n, x;
+
+            /*
+             * If lower side is heavy, move highest nodes from lower side to 
+             * higher side until balanced.
+             */
+            while (lc > hc + 1) {
+                x = ExtractHighest (r.lowerCase, out n);
+                n.lowerCase  = x;
+                n.higherCase = r;
+                r.lowerCase  = null;
+                r = n;
+                lc --;
+                hc ++;
+            }
+
+            /*
+             * If higher side is heavy, move lowest nodes from higher side to 
+             * lower side until balanced.
+             */
+            while (hc > lc + 1) {
+                x = ExtractLowest (r.higherCase, out n);
+                n.higherCase = x;
+                n.lowerCase  = r;
+                r.higherCase = null;
+                r = n;
+                lc ++;
+                hc --;
+            }
+
+            /*
+             * Now balance each side because they can be lopsided individually.
+             */
+            r.lowerCase  = BalanceTree (r.lowerCase);
+            r.higherCase = BalanceTree (r.higherCase);
+            return r;
+        }
+
+        /**
+         * @brief Get number of nodes in a tree
+         * @param n = root of tree to count
+         * @returns number of nodes including root
+         */
+        private static int CountTree (TokenSwitchCase n)
+        {
+            if (n == null) return 0;
+            return 1 + CountTree (n.lowerCase) + CountTree (n.higherCase);
+        }
+
+        // Extract highest node from a tree
+        // @param r = root of tree to extract highest from
+        // @returns new root after node has been extracted
+        //          n = node that was extracted from tree
+        private static TokenSwitchCase ExtractHighest (TokenSwitchCase r, out TokenSwitchCase n)
+        {
+            if (r.higherCase == null) {
+                n = r;
+                return r.lowerCase;
+            }
+            r.higherCase = ExtractHighest (r.higherCase, out n);
+            return r;
+        }
+
+        // Extract lowest node from a tree
+        // @param r = root of tree to extract lowest from
+        // @returns new root after node has been extracted
+        //          n = node that was extracted from tree
+        private static TokenSwitchCase ExtractLowest (TokenSwitchCase r, out TokenSwitchCase n)
+        {
+            if (r.lowerCase == null) {
+                n = r;
+                return r.higherCase;
+            }
+            r.lowerCase = ExtractLowest (r.lowerCase, out n);
+            return r;
+        }
+
+        /**
+         * Output code for string-style case of a switch/case to jump to the script code associated with the case.
+         * @param testRVal = value being switched on
+         * @param thisCase = case that the code is being output for
+         * @param defaultLabel = where the default clause is (or past all cases if none)
+         * Note:
+         *   Outputs code for this case and the lowerCase and higherCases if any.
+         *   If no lowerCase or higherCase, outputs a br to defaultLabel so this code never falls through.
+         */
+        private void OutputStrCase (CompValu testRVal, TokenSwitchCase thisCase, ScriptMyLabel defaultLabel)
+        {
+            /*
+             * If nothing lower on tree and there is a single case value, 
+             * just do one compare for equality.
+             */
+            if ((thisCase.lowerCase == null) && (thisCase.higherCase == null) && (thisCase.str1 == thisCase.str2)) {
+                testRVal.PushVal (this, thisCase, tokenTypeStr);
+                ilGen.Emit (thisCase, OpCodes.Ldstr,   thisCase.str1);
+                ilGen.Emit (thisCase, OpCodes.Ldc_I4,  (int)StringComparison.Ordinal);
+                ilGen.Emit (thisCase, OpCodes.Call,    stringCompareMethodInfo);
+                ilGen.Emit (thisCase, OpCodes.Brfalse, thisCase.label);
+                ilGen.Emit (thisCase, OpCodes.Br,      defaultLabel);
+                return;
+            }
+
+            /*
+             * Determine where to jump if switch value is lower than lower case value.
+             */
+            ScriptMyLabel lowerLabel = defaultLabel;
+            if (thisCase.lowerCase != null) {
+                lowerLabel = ilGen.DefineLabel ("lower");
+            }
+
+            /*
+             * If single case value, put comparison result in this temp.
+             */
+            CompValuTemp cmpv1 = null;
+            if (thisCase.str1 == thisCase.str2) {
+                cmpv1 = new CompValuTemp (tokenTypeInt, this);
+            }
+
+            /*
+             * If switch value .lt. lower case value, jump to lower label.
+             * Maybe save comparison result in a temp.
+             */
+            testRVal.PushVal (this, thisCase, tokenTypeStr);
+            ilGen.Emit (thisCase, OpCodes.Ldstr,  thisCase.str1);
+            ilGen.Emit (thisCase, OpCodes.Ldc_I4, (int)StringComparison.Ordinal);
+            ilGen.Emit (thisCase, OpCodes.Call,   stringCompareMethodInfo);
+            if (cmpv1 != null) {
+                ilGen.Emit (thisCase, OpCodes.Dup);
+                cmpv1.Pop (this, thisCase);
+            }
+            ilGen.Emit (thisCase, OpCodes.Ldc_I4_0);
+            ilGen.Emit (thisCase, OpCodes.Blt,    lowerLabel);
+
+            /*
+             * If switch value .le. higher case value, jump to case code.
+             * Maybe get comparison from the temp.
+             */
+            if (cmpv1 == null) {
+                testRVal.PushVal (this, thisCase, tokenTypeStr);
+                ilGen.Emit (thisCase, OpCodes.Ldstr,  thisCase.str2);
+                ilGen.Emit (thisCase, OpCodes.Ldc_I4, (int)StringComparison.Ordinal);
+                ilGen.Emit (thisCase, OpCodes.Call,   stringCompareMethodInfo);
+            } else {
+                cmpv1.PushVal (this, thisCase);
+            }
+            ilGen.Emit (thisCase, OpCodes.Ldc_I4_0);
+            ilGen.Emit (thisCase, OpCodes.Ble,    thisCase.label);
+
+            /*
+             * Output code for higher comparison if any.
+             */
+            if (thisCase.higherCase == null) {
+                ilGen.Emit (thisCase, OpCodes.Br, defaultLabel);
+            } else {
+                OutputStrCase (testRVal, thisCase.higherCase, defaultLabel);
+            }
+
+            /*
+             * Output code for lower comparison if any.
+             */
+            if (thisCase.lowerCase != null) {
+                ilGen.MarkLabel (lowerLabel);
+                OutputStrCase (testRVal, thisCase.lowerCase, defaultLabel);
+            }
+        }
+
+        /**
+         * @brief output code for a throw statement.
+         * @param throwStmt = throw statement token, including value to be thrown
+         */
+        private void GenerateStmtThrow (TokenStmtThrow throwStmt)
+        {
+            if (!mightGetHere) return;
+
+            /*
+             * 'throw' statements never fall through.
+             */
+            mightGetHere = false;
+
+            /*
+             * Output code for either a throw or a rethrow.
+             */
+            if (throwStmt.rVal == null) {
+                for (TokenStmtBlock blk = curStmtBlock; blk != null; blk = blk.outerStmtBlock) {
+                    if (curStmtBlock.isCatch) {
+                        ilGen.Emit (throwStmt, OpCodes.Rethrow);
+                        return;
+                    }
+                }
+                ErrorMsg (throwStmt, "rethrow allowed only in catch clause");
+            } else {
+                CompValu rVal = GenerateFromRVal (throwStmt.rVal);
+                rVal.PushVal (this, throwStmt.rVal, tokenTypeObj);
+                ilGen.Emit (throwStmt, OpCodes.Call, thrownExceptionWrapMethodInfo);
+                ilGen.Emit (throwStmt, OpCodes.Throw);
+            }
+        }
+
+        /**
+         * @brief output code for a try/catch/finally block
+         */
+        private void GenerateStmtTry (TokenStmtTry tryStmt)
+        {
+            if (!mightGetHere) return;
+
+            /*
+             * Reducer should make sure we have exactly one of catch or finally.
+             */
+            if ((tryStmt.catchStmt == null) && (tryStmt.finallyStmt == null)) {
+                throw new Exception ("must have a catch or a finally on try");
+            }
+            if ((tryStmt.catchStmt != null) && (tryStmt.finallyStmt != null)) {
+                throw new Exception ("can't have both catch and finally on same try");
+            }
+
+            /*
+             * Stack the call labels.
+             * Try blocks have their own series of call labels.
+             */
+            ScriptMyLocal saveCallNo = actCallNo;
+            LinkedList<CallLabel> saveCallLabels = actCallLabels;
+
+            /*
+             * Generate code for either try { } catch { } or try { } finally { }.
+             */
+            if (tryStmt.catchStmt   != null) GenerateStmtTryCatch   (tryStmt);
+            if (tryStmt.finallyStmt != null) GenerateStmtTryFinally (tryStmt);
+
+            /*
+             * Restore call labels.
+             */
+            actCallNo     = saveCallNo;
+            actCallLabels = saveCallLabels;
+        }
+
+
+        /**
+         * @brief output code for a try/catch block
+         *
+         *      int    __tryCallNo = -1;                                   // call number within try { } subblock
+         *      int    __catCallNo = -1;                                   // call number within catch { } subblock
+         *      Exception __catThrown = null;                              // caught exception
+         *    <oldCallLabel>:                                              // the outside world jumps here to restore us no matter ...
+         *      try {                                                      // ... where we actually were inside of try/catch
+         *          if (__tryCallNo >= 0) goto tryCallSw;                  // maybe go do restore
+         *          <try body using __tryCallNo>                           // execute script-defined code
+         *                                                                 // ...stack capture WILL run catch { } subblock
+         *          leave tryEnd;                                          // exits
+         *        tryThrow:<tryCallLabel>:
+         *          throw new ScriptRestoreCatchException(__catThrown);    // catch { } was running, jump to its beginning
+         *        tryCallSw:                                               // restoring...
+         *          switch (__tryCallNo) back up into <try body>           // not catching, jump back inside try
+         *      } catch (Exception exc) {
+         *          exc = ScriptRestoreCatchException.Unwrap(exc);         // unwrap possible ScriptRestoreCatchException
+         *          if (exc == null) goto catchRetro;                      // rethrow if IXMRUncatchable (eg, StackCaptureException)
+         *          __catThrown = exc;                                     // save what was thrown so restoring try { } will throw it again
+         *          catchVar = exc;                                        // set up script-visible variable
+         *          __tryCallNo = tryThrow:<tryCallLabel>
+         *          if (__catCallNo >= 0) goto catchCallSw;                // if restoring, go check below
+         *          <catch body using __catCallNo>                         // normal, execute script-defined code
+         *          leave tryEnd;                                          // all done, exit catch { }
+         *        catchRetro:
+         *          rethrow;
+         *        catchCallSw:
+         *          switch (__catCallNo) back up into <catch body>         // restart catch { } code wherever it was
+         *      }
+         *    tryEnd:
+         */
+        private void GenerateStmtTryCatch (TokenStmtTry tryStmt)
+        {
+            CompValuTemp tryCallNo = new CompValuTemp (tokenTypeInt, this);
+            CompValuTemp catCallNo = new CompValuTemp (tokenTypeInt, this);
+            CompValuTemp catThrown = new CompValuTemp (tokenTypeExc, this);
+
+            ScriptMyLabel tryCallSw   = ilGen.DefineLabel ("__tryCallSw_"   + tryStmt.Unique);
+            ScriptMyLabel catchRetro  = ilGen.DefineLabel ("__catchRetro_"  + tryStmt.Unique);
+            ScriptMyLabel catchCallSw = ilGen.DefineLabel ("__catchCallSw_" + tryStmt.Unique);
+            ScriptMyLabel tryEnd      = ilGen.DefineLabel ("__tryEnd_"      + tryStmt.Unique);
+
+            SetCallNo (tryStmt, tryCallNo, -1);
+            SetCallNo (tryStmt, catCallNo, -1);
+            ilGen.Emit (tryStmt, OpCodes.Ldnull);
+            catThrown.Pop (this, tryStmt);
+
+            new CallLabel (this, tryStmt);              //   <oldcalllabel>:
+            ilGen.BeginExceptionBlock ();               //     try {
+            openCallLabel = null;
+            if (DEBUG_TRYSTMT) {
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "enter try*: " + tryStmt.line + " callMode=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                PushXMRInst ();
+                ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
+                ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, " tryCallNo=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                tryCallNo.PushVal (this, tryStmt);
+                ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, " catThrown.IsNull=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                catThrown.PushVal (this, tryStmt);
+                ilGen.Emit (tryStmt, OpCodes.Ldnull);
+                ilGen.Emit (tryStmt, OpCodes.Ceq);
+                ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, " catCallNo=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                catCallNo.PushVal (this, tryStmt);
+                ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+            }
+
+            GetCallNo (tryStmt, tryCallNo);             //         if (__tryCallNo >= 0) goto tryCallSw;
+            ilGen.Emit (tryStmt, OpCodes.Ldc_I4_0);
+            ilGen.Emit (tryStmt, OpCodes.Bge, tryCallSw);
+
+            actCallNo = tryCallNo.localBuilder;                     // set up __tryCallNo for call labels
+            actCallLabels = new LinkedList<CallLabel> ();
+
+            GenerateStmtBlock (tryStmt.tryStmt);            // output the try block statement subblock
+
+            bool tryBlockFallsOutBottom = mightGetHere;
+            if (tryBlockFallsOutBottom) {
+                new CallLabel (this, tryStmt);          //       <tryCallLabel>:
+                ilGen.Emit (tryStmt, OpCodes.Leave, tryEnd);    //         leave tryEnd;
+                openCallLabel = null;
+            }
+
+            CallLabel tryThrow = new CallLabel (this, tryStmt); //       tryThrow:<tryCallLabel>:
+            if (DEBUG_TRYSTMT) {
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "tryThrow*: " + tryStmt.line + " catThrown=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                catThrown.PushVal (this, tryStmt);
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+            }
+            catThrown.PushVal (this, tryStmt);          //         throw new ScriptRestoreCatchException (__catThrown);
+            ilGen.Emit (tryStmt, OpCodes.Newobj, scriptRestoreCatchExceptionConstructorInfo);
+            ilGen.Emit (tryStmt, OpCodes.Throw);
+            openCallLabel = null;
+
+            ilGen.MarkLabel (tryCallSw);                //       tryCallSw:
+            if (DEBUG_TRYSTMT) {
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "tryCallSw*: " + tryStmt.line + " tryCallNo=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                tryCallNo.PushVal (this, tryStmt);
+                ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+            }
+            OutputCallNoSwitchStmt ();              //         switch (tryCallNo) ...
+
+            CompValuLocalVar catchVarLocExc = null;
+            CompValuTemp catchVarLocStr = null;
+
+            if (tryStmt.catchVar.type.ToSysType () == typeof (Exception)) {
+                catchVarLocExc = new CompValuLocalVar (tryStmt.catchVar.type, tryStmt.catchVar.name.val, this);
+            } else if (tryStmt.catchVar.type.ToSysType () == typeof (String)) {
+                catchVarLocStr = new CompValuTemp (tryStmt.catchVar.type, this);
+            }
+
+            ScriptMyLocal excLocal = ilGen.DeclareLocal (typeof (String), "catchstr_" + tryStmt.Unique);
+
+            ilGen.BeginCatchBlock (typeof (Exception));     // start of the catch block that can catch any exception
+            if (DEBUG_TRYSTMT) {
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, "enter catch*: " + tryStmt.line + " callMode=");
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
+                PushXMRInst ();
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldfld, callModeFieldInfo);
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, " catCallNo=");
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
+                catCallNo.PushVal (this, tryStmt);
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, " exc=");
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup);
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, "\n");
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
+            }
+            ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, scriptRestoreCatchExceptionUnwrap);
+                                        // exc = ScriptRestoreCatchException.Unwrap (exc);
+            ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup);        // rethrow if IXMRUncatchable (eg, StackCaptureException)
+            ilGen.Emit (tryStmt.catchStmt, OpCodes.Brfalse, catchRetro);
+            if (tryStmt.catchVar.type.ToSysType () == typeof (Exception)) {
+                tryStmt.catchVar.location = catchVarLocExc;
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup);
+                catThrown.Pop (this, tryStmt);              // store exception object in catThrown
+                catchVarLocExc.Pop (this, tryStmt.catchVar.name);      // also store in script-visible variable
+            } else if (tryStmt.catchVar.type.ToSysType () == typeof (String)) {
+                tryStmt.catchVar.location = catchVarLocStr;
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup);
+                catThrown.Pop (this, tryStmt);              // store exception object in catThrown
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, catchExcToStrMethodInfo);
+
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Stloc, excLocal);
+                catchVarLocStr.PopPre (this, tryStmt.catchVar.name);
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldloc, excLocal);
+                catchVarLocStr.PopPost (this, tryStmt.catchVar.name, tokenTypeStr);
+            } else {
+                throw new Exception ("bad catch var type " + tryStmt.catchVar.type.ToString ());
+            }
+
+            SetCallNo (tryStmt, tryCallNo, tryThrow.index);     // __tryCallNo = tryThrow so it knows to do 'throw catThrown' on restore
+
+            GetCallNo (tryStmt, catCallNo);             // if (__catCallNo >= 0) goto catchCallSw;
+            ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldc_I4_0);
+            ilGen.Emit (tryStmt.catchStmt, OpCodes.Bge, catchCallSw);
+
+            actCallNo = catCallNo.localBuilder;                 // set up __catCallNo for call labels
+            actCallLabels.Clear ();
+            mightGetHere = true;                    // if we can get to the 'try' assume we can get to the 'catch'
+            GenerateStmtBlock (tryStmt.catchStmt);          // output catch clause statement subblock
+
+            if (mightGetHere) {
+                new CallLabel (this, tryStmt.catchStmt);
+                ilGen.Emit (tryStmt.catchStmt, OpCodes.Leave, tryEnd);
+                openCallLabel = null;
+            }
+
+            ilGen.MarkLabel (catchRetro);               // not a script-visible exception, rethrow it
+            ilGen.Emit (tryStmt.catchStmt, OpCodes.Pop);
+            ilGen.Emit (tryStmt.catchStmt, OpCodes.Rethrow);
+
+            ilGen.MarkLabel (catchCallSw);
+            OutputCallNoSwitchStmt ();              // restoring, jump back inside script-defined body
+
+            ilGen.EndExceptionBlock ();
+            ilGen.MarkLabel (tryEnd);
+
+            mightGetHere |= tryBlockFallsOutBottom;         // also get here if try body falls out bottom
+        }
+
+        /**
+         * @brief output code for a try/finally block
+         *
+         * This is such a mess because there is hidden state for the finally { } that we have to recreate.
+         * The finally { } can be entered either via an exception being thrown in the try { } or a leave 
+         * being executed in the try { } whose target is outside the try { } finally { }.
+         *
+         * For the thrown exception case, we slip in a try { } catch { } wrapper around the original try { }
+         * body.  This will sense any thrown exception that would execute the finally { }.  Then we have our
+         * try { } throw the exception on restore which gets the finally { } called and on its way again.
+         *
+         * For the leave case, we prefix all leave instructions with a call label and we explicitly chain
+         * all leaves through each try { } that has an associated finally { } that the leave would unwind 
+         * through.  This gets each try { } to simply jump to the correct leave instruction which immediately 
+         * invokes the corresponding finally { } and then chains to the next leave instruction on out until 
+         * it gets to its target.
+         *
+         *      int    __finCallNo = -1;                                     // call number within finally { } subblock
+         *      int    __tryCallNo = -1;                                     // call number within try { } subblock
+         *      Exception __catThrown = null;                                // caught exception
+         *    <oldCallLabel>:                                                // the outside world jumps here to restore us no matter ...
+         *      try {                                                        // ... where we actually were inside of try/finally
+         *          try {
+         *              if (__tryCallNo >= 0) goto tryCallSw;                // maybe go do restore
+         *              <try body using __tryCallNo>                         // execute script-defined code
+         *                                                                   // ...stack capture WILL run catch/finally { } subblock
+         *              leave tryEnd;                                        // executes finally { } subblock and exits
+         *            tryThrow:<tryCallLabel>:
+         *              throw new ScriptRestoreCatchException(__catThrown);  // catch { } was running, jump to its beginning
+         *            tryCallSw:                                             // restoring...
+         *              switch (__tryCallNo) back up into <try body>         // jump back inside try, ...
+         *                                                                   // ... maybe to a leave if we were doing finally { } subblock
+         *          } catch (Exception exc) {                                // in case we're getting to finally { } via a thrown exception:
+         *              exc = ScriptRestoreCatchException.Unwrap(exc);       // unwrap possible ScriptRestoreCatchException
+         *              if (callMode == CallMode_SAVE) goto catchRetro;      // don't touch anything if capturing stack
+         *              __catThrown = exc;                                   // save exception so try { } can throw it on restore
+         *              __tryCallNo = tryThrow:<tryCallLabel>;               // tell try { } to throw it on restore
+         *            catchRetro:
+         *              rethrow;                                             // in any case, go on to finally { } subblock now
+         *          }
+         *      } finally {
+         *          if (callMode == CallMode_SAVE) goto finEnd;              // don't touch anything if capturing stack
+         *          if (__finCallNo >= 0) goto finCallSw;                    // maybe go do restore
+         *          <finally body using __finCallNo>                         // normal, execute script-defined code
+         *        finEnd:
+         *          endfinally                                               // jump to leave/throw target or next outer finally { }
+         *        finCallSw:
+         *          switch (__finCallNo) back up into <finally body>         // restoring, restart finally { } code wherever it was
+         *      }
+         *    tryEnd:
+         */
+        private void GenerateStmtTryFinally (TokenStmtTry tryStmt)
+        {
+            CompValuTemp finCallNo = new CompValuTemp (tokenTypeInt, this);
+            CompValuTemp tryCallNo = new CompValuTemp (tokenTypeInt, this);
+            CompValuTemp catThrown = new CompValuTemp (tokenTypeExc, this);
+
+            ScriptMyLabel tryCallSw   = ilGen.DefineLabel (       "__tryCallSw_"   + tryStmt.Unique);
+            ScriptMyLabel catchRetro  = ilGen.DefineLabel (       "__catchRetro_"  + tryStmt.Unique);
+            ScriptMyLabel finCallSw   = ilGen.DefineLabel (       "__finCallSw_"   + tryStmt.Unique);
+            BreakContTarg finEnd      = new BreakContTarg (this, "__finEnd_"      + tryStmt.Unique);
+            ScriptMyLabel tryEnd      = ilGen.DefineLabel (       "__tryEnd_"      + tryStmt.Unique);
+
+            SetCallNo (tryStmt, finCallNo, -1);
+            SetCallNo (tryStmt, tryCallNo, -1);
+            ilGen.Emit (tryStmt, OpCodes.Ldnull);
+            catThrown.Pop (this, tryStmt);
+
+            new CallLabel (this, tryStmt);              //   <oldcalllabel>:
+            ilGen.BeginExceptionBlock ();               //     try {
+            ilGen.BeginExceptionBlock ();               //     try {
+            openCallLabel = null;
+            if (DEBUG_TRYSTMT) {
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "enter try*: " + tryStmt.line + " callMode=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                PushXMRInst ();
+                ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
+                ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, " tryCallNo=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                tryCallNo.PushVal (this, tryStmt);
+                ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, " finCallNo=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                finCallNo.PushVal (this, tryStmt);
+                ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, " catThrown.IsNull=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                catThrown.PushVal (this, tryStmt);
+                ilGen.Emit (tryStmt, OpCodes.Ldnull);
+                ilGen.Emit (tryStmt, OpCodes.Ceq);
+                ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+            }
+
+            GetCallNo (tryStmt, tryCallNo);             //         if (__tryCallNo >= 0) goto tryCallSw;
+            ilGen.Emit (tryStmt, OpCodes.Ldc_I4_0);
+            ilGen.Emit (tryStmt, OpCodes.Bge, tryCallSw);
+
+            actCallNo = tryCallNo.localBuilder;                     // set up __tryCallNo for call labels
+            actCallLabels = new LinkedList<CallLabel> ();
+
+            GenerateStmtBlock (tryStmt.tryStmt);            // output the try block statement subblock
+
+            if (mightGetHere) {
+                new CallLabel (this, tryStmt);          //       <newCallLabel>:
+                ilGen.Emit (tryStmt, OpCodes.Leave, tryEnd);    //         leave tryEnd;
+                openCallLabel = null;
+            }
+
+            foreach (IntermediateLeave iLeave in tryStmt.iLeaves.Values) {
+                ilGen.MarkLabel (iLeave.jumpIntoLabel);     //       intr2_exit:
+                new CallLabel (this, tryStmt);          //         tryCallNo = n;
+                ilGen.Emit (tryStmt, OpCodes.Leave, iLeave.jumpAwayLabel);  //    __callNo_n_: leave int1_exit;
+                openCallLabel = null;
+            }
+
+            CallLabel tryThrow = new CallLabel (this, tryStmt); //       tryThrow:<tryCallLabel>:
+            if (DEBUG_TRYSTMT) {
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "tryThrow*: " + tryStmt.line + " catThrown=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                catThrown.PushVal (this, tryStmt);
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+            }
+            catThrown.PushVal (this, tryStmt);          //         throw new ScriptRestoreCatchException (__catThrown);
+            ilGen.Emit (tryStmt, OpCodes.Newobj, scriptRestoreCatchExceptionConstructorInfo);
+            ilGen.Emit (tryStmt, OpCodes.Throw);
+            openCallLabel = null;
+
+            ilGen.MarkLabel (tryCallSw);                //       tryCallSw:
+            OutputCallNoSwitchStmt ();              //         switch (tryCallNo) ...
+                                        //     }
+
+            ilGen.BeginCatchBlock (typeof (Exception));     // start of the catch block that can catch any exception
+            if (DEBUG_TRYSTMT) {
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "enter catch*: " + tryStmt.line + " callMode=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                PushXMRInst ();
+                ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
+                ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, " exc=");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Dup);
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+                ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
+                ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
+            }
+            ilGen.Emit (tryStmt, OpCodes.Call, scriptRestoreCatchExceptionUnwrap);  // exc = ScriptRestoreCatchException.Unwrap (exc);
+            PushXMRInst ();                     // if (callMode == CallMode_SAVE) goto catchRetro;
+            ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
+            ilGen.Emit (tryStmt, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_SAVE);
+            ilGen.Emit (tryStmt, OpCodes.Beq, catchRetro);
+
+            catThrown.Pop (this, tryStmt);              // __catThrown = exc;
+            SetCallNo (tryStmt, tryCallNo, tryThrow.index);     // __tryCallNo = tryThrow:<tryCallLabel>;
+            ilGen.Emit (tryStmt, OpCodes.Rethrow);
+
+            ilGen.MarkLabel (catchRetro);               // catchRetro:
+            ilGen.Emit (tryStmt, OpCodes.Pop);
+            ilGen.Emit (tryStmt, OpCodes.Rethrow);          //    rethrow;
+
+            ilGen.EndExceptionBlock ();             // }
+
+            ilGen.BeginFinallyBlock ();             // start of the finally block
+
+            PushXMRInst ();                     // if (callMode == CallMode_SAVE) goto finEnd;
+            ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
+            ilGen.Emit (tryStmt, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_SAVE);
+            ilGen.Emit (tryStmt, OpCodes.Beq, finEnd.label);
+
+            GetCallNo (tryStmt, finCallNo);             // if (__finCallNo >= 0) goto finCallSw;
+            ilGen.Emit (tryStmt, OpCodes.Ldc_I4_0);
+            ilGen.Emit (tryStmt, OpCodes.Bge, finCallSw);
+
+            actCallNo = finCallNo.localBuilder;                 // set up __finCallNo for call labels
+            actCallLabels.Clear ();
+            mightGetHere = true;                    // if we can get to the 'try' assume we can get to the 'finally'
+            GenerateStmtBlock (tryStmt.finallyStmt);        // output finally clause statement subblock
+
+            ilGen.MarkLabel (finEnd.label);             // finEnd:
+            ilGen.Emit (tryStmt, OpCodes.Endfinally);       //    return out to next finally { } or catch { } or leave target
+
+            ilGen.MarkLabel (finCallSw);                // restore mode, switch (finCallNo) ...
+            OutputCallNoSwitchStmt ();
+
+            ilGen.EndExceptionBlock ();
+            ilGen.MarkLabel (tryEnd);
+
+            mightGetHere |= finEnd.used;                // get here if finally body falls through or has a break statement
+        }
+
+        /**
+         * @brief Generate code to initialize a variable to its default value.
+         */
+        private void GenerateStmtVarIniDef (TokenStmtVarIniDef varIniDefStmt)
+        {
+            if (!mightGetHere) return;
+
+            CompValu left = GenerateFromLVal (varIniDefStmt.var);
+            left.PopPre (this, varIniDefStmt);
+            PushDefaultValue (left.type);
+            left.PopPost (this, varIniDefStmt);
+        }
+
+        /**
+         * @brief generate code for a 'while' statement including the loop body.
+         */
+        private void GenerateStmtWhile (TokenStmtWhile whileStmt)
+        {
+            if (!mightGetHere) return;
+
+            BreakContTarg oldBreakTarg = curBreakTarg;
+            BreakContTarg oldContTarg  = curContTarg;
+            ScriptMyLabel loopLabel    = ilGen.DefineLabel ("whileloop_" + whileStmt.Unique);
+
+            curBreakTarg = new BreakContTarg (this, "whilebreak_" + whileStmt.Unique);
+            curContTarg  = new BreakContTarg (this, "whilecont_"  + whileStmt.Unique);
+
+            ilGen.MarkLabel (loopLabel);                                          // loop:
+            CompValu testRVal = GenerateFromRVal (whileStmt.testRVal);            //   testRVal = while test expression
+            if (!IsConstBoolExprTrue (testRVal)) {
+                testRVal.PushVal (this, whileStmt.testRVal, tokenTypeBool);   //   if (!testRVal)
+                ilGen.Emit (whileStmt, OpCodes.Brfalse, curBreakTarg.label);  //      goto break
+                curBreakTarg.used = true;
+            }
+            GenerateStmt (whileStmt.bodyStmt);                                    //   while body statement
+            if (curContTarg.used) {
+                ilGen.MarkLabel (curContTarg.label);                          // cont:
+                mightGetHere = true;
+            }
+            if (mightGetHere) {
+                EmitCallCheckRun (whileStmt, false);                          //   __sw.CheckRun()
+                ilGen.Emit (whileStmt, OpCodes.Br, loopLabel);                //   goto loop
+            }
+            mightGetHere = curBreakTarg.used;
+            if (mightGetHere) {
+                ilGen.MarkLabel (curBreakTarg.label);                         // done:
+            }
+
+            curBreakTarg = oldBreakTarg;
+            curContTarg  = oldContTarg;
+        }
+
+        /**
+         * @brief process a local variable declaration statement, possibly with initialization expression.
+         *        Note that the function header processing allocated stack space (CompValuTemp) for the
+         *        variable and now all we do is write its initialization value.
+         */
+        private void GenerateDeclVar (TokenDeclVar declVar)
+        {
+            /*
+             * Script gave us an initialization value, so just store init value in var like an assignment statement.
+             * If no init given, set it to its default value.
+             */
+            CompValu local = declVar.location;
+            if (declVar.init != null) {
+                CompValu rVal = GenerateFromRVal (declVar.init, local.GetArgTypes ());
+                local.PopPre  (this, declVar);
+                rVal.PushVal  (this, declVar.init, declVar.type);
+                local.PopPost (this, declVar);
+            } else {
+                local.PopPre  (this, declVar);
+                PushDefaultValue (declVar.type);
+                local.PopPost (this, declVar);
+            }
+        }
+
+        /**
+         * @brief Get the type and location of an L-value (eg, variable)
+         * @param lVal    = L-value expression to evaluate
+         * @param argsig  = null: it's a field/property
+         *                  else: select overload method that fits these arg types
+         */
+        private CompValu GenerateFromLVal (TokenLVal lVal)
+        {
+            return GenerateFromLVal (lVal, null);
+        }
+        private CompValu GenerateFromLVal (TokenLVal lVal, TokenType[] argsig)
+        {
+            if (lVal is TokenLValArEle)     return GenerateFromLValArEle     ((TokenLValArEle)lVal);
+            if (lVal is TokenLValBaseField) return GenerateFromLValBaseField ((TokenLValBaseField)lVal, argsig);
+            if (lVal is TokenLValIField)    return GenerateFromLValIField    ((TokenLValIField)lVal,    argsig);
+            if (lVal is TokenLValName)      return GenerateFromLValName      ((TokenLValName)lVal,      argsig);
+            if (lVal is TokenLValSField)    return GenerateFromLValSField    ((TokenLValSField)lVal,    argsig);
+            throw new Exception ("bad lval class");
+        }
+
+        /**
+         * @brief we have an L-value token that is an element within an array.
+         * @returns a CompValu giving the type and location of the element of the array.
+         */
+        private CompValu GenerateFromLValArEle (TokenLValArEle lVal)
+        {
+            CompValu subCompValu;
+
+            /*
+             * Compute location of array itself.
+             */
+            CompValu baseCompValu = GenerateFromRVal (lVal.baseRVal);
+
+            /*
+             * Maybe it is a fixed array access.
+             */
+            string basetypestring = baseCompValu.type.ToString ();
+            if (basetypestring.EndsWith ("]")) {
+                TokenRVal subRVal = lVal.subRVal;
+                int nSubs = 1;
+                if (subRVal is TokenRValList) {
+                    nSubs   = ((TokenRValList)subRVal).nItems;
+                    subRVal = ((TokenRValList)subRVal).rVal;
+                }
+
+                int rank = basetypestring.IndexOf (']') - basetypestring.IndexOf ('[');
+                if (nSubs != rank) {
+                    ErrorMsg (lVal.baseRVal, "expect " + rank + " subscript" + ((rank == 1) ? "" : "s") + " but have " + nSubs);
+                }
+                CompValu[] subCompValus = new CompValu[rank];
+                int i;
+                for (i = 0; (subRVal != null) && (i < rank); i ++) {
+                    subCompValus[i] = GenerateFromRVal (subRVal);
+                    subRVal = (TokenRVal)subRVal.nextToken;
+                }
+                while (i < rank) subCompValus[i++] = new CompValuInteger (new TokenTypeInt (lVal.subRVal), 0);
+                return new CompValuFixArEl (this, baseCompValu, subCompValus);
+            }
+
+            /*
+             * Maybe it is accessing the $idxprop property of a script-defined class.
+             */
+            if (baseCompValu.type is TokenTypeSDTypeClass) {
+                TokenName name = new TokenName (lVal, "$idxprop");
+                TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseCompValu.type;
+                TokenDeclSDTypeClass sdtDecl = sdtType.decl;
+                TokenDeclVar idxProp = FindThisMember (sdtDecl, name, null);
+                if (idxProp == null) {
+                    ErrorMsg (lVal, "no index property in class " + sdtDecl.longName.val);
+                    return new CompValuVoid (lVal);
+                }
+                if ((idxProp.sdtFlags & ScriptReduce.SDT_STATIC) != 0) {
+                    ErrorMsg (lVal, "non-static reference to static member " + idxProp.name.val);
+                    return new CompValuVoid (idxProp);
+                }
+                CheckAccess (idxProp, name);
+
+                TokenType[] argTypes = IdxPropArgTypes (idxProp);
+                CompValu[] compValus = IdxPropCompValus (lVal, argTypes.Length);
+                return new CompValuIdxProp (idxProp, baseCompValu, argTypes, compValus);
+
+            }
+
+            /*
+             * Maybe they are accessing $idxprop property of a script-defined interface.
+             */
+            if (baseCompValu.type is TokenTypeSDTypeInterface) {
+                TokenName name = new TokenName (lVal, "$idxprop");
+                TokenTypeSDTypeInterface sdtType = (TokenTypeSDTypeInterface)baseCompValu.type;
+                TokenDeclVar idxProp = FindInterfaceMember (sdtType, name, null, ref baseCompValu);
+                if (idxProp == null) {
+                    ErrorMsg (lVal, "no index property defined for interface " + sdtType.decl.longName.val);
+                    return baseCompValu;
+                }
+
+                TokenType[] argTypes = IdxPropArgTypes (idxProp);
+                CompValu[] compValus = IdxPropCompValus (lVal, argTypes.Length);
+                return new CompValuIdxProp (idxProp, baseCompValu, argTypes, compValus);
+            }
+
+            /*
+             * Maybe it is extracting a character from a string.
+             */
+            if ((baseCompValu.type is TokenTypeKey) || (baseCompValu.type is TokenTypeStr)) {
+                subCompValu = GenerateFromRVal (lVal.subRVal);
+                return new CompValuStrChr (new TokenTypeChar (lVal), baseCompValu, subCompValu);
+            }
+
+            /*
+             * Maybe it is extracting an element from a list.
+             */
+            if (baseCompValu.type is TokenTypeList) {
+                subCompValu = GenerateFromRVal (lVal.subRVal);
+                return new CompValuListEl (new TokenTypeObject (lVal), baseCompValu, subCompValu);
+            }
+
+            /*
+             * Access should be to XMR_Array otherwise.
+             */
+            if (!(baseCompValu.type is TokenTypeArray)) {
+                ErrorMsg (lVal, "taking subscript of non-array");
+                return baseCompValu;
+            }
+            subCompValu = GenerateFromRVal (lVal.subRVal);
+            return new CompValuArEle (new TokenTypeObject (lVal), baseCompValu, subCompValu);
+        }
+
+        /**
+         * @brief Get number and type of arguments required by an index property.
+         */
+        private static TokenType[] IdxPropArgTypes (TokenDeclVar idxProp)
+        {
+            TokenType[] argTypes;
+            if (idxProp.getProp != null) {
+                int nArgs = idxProp.getProp.argDecl.varDict.Count;
+                argTypes = new TokenType[nArgs];
+                foreach (TokenDeclVar var in idxProp.getProp.argDecl.varDict) {
+                    argTypes[var.vTableIndex] = var.type;
+                }
+            } else {
+                int nArgs = idxProp.setProp.argDecl.varDict.Count - 1;
+                argTypes = new TokenType[nArgs];
+                foreach (TokenDeclVar var in idxProp.setProp.argDecl.varDict) {
+                    if (var.vTableIndex < nArgs) {
+                        argTypes[var.vTableIndex] = var.type;
+                    }
+                }
+            }
+            return argTypes;
+        }
+
+        /**
+         * @brief Get number and computed value of index property arguments.
+         * @param lVal = list of arguments
+         * @param nArgs = number of arguments required
+         * @returns null: argument count mismatch
+         *          else: array of index property argument values
+         */
+        private CompValu[] IdxPropCompValus (TokenLValArEle lVal, int nArgs)
+        {
+            TokenRVal subRVal = lVal.subRVal;
+            int nSubs = 1;
+            if (subRVal is TokenRValList) {
+                nSubs   = ((TokenRValList)subRVal).nItems;
+                subRVal = ((TokenRValList)subRVal).rVal;
+            }
+
+            if (nSubs != nArgs) {
+                ErrorMsg (lVal, "index property requires " + nArgs + " subscript(s)");
+                return null;
+            }
+
+            CompValu[] subCompValus = new CompValu[nArgs];
+            for (int i = 0; i < nArgs; i ++) {
+                subCompValus[i] = GenerateFromRVal (subRVal);
+                subRVal = (TokenRVal)subRVal.nextToken;
+            }
+            return subCompValus;
+        }
+
+        /**
+         * @brief using 'base' within a script-defined instance method to refer to an instance field/method 
+         *        of the class being extended.
+         */
+        private CompValu GenerateFromLValBaseField (TokenLValBaseField baseField, TokenType[] argsig)
+        {
+            string fieldName = baseField.fieldName.val;
+
+            TokenDeclSDType sdtDecl = curDeclFunc.sdtClass;
+            if ((sdtDecl == null) || ((curDeclFunc.sdtFlags & ScriptReduce.SDT_STATIC) != 0)) {
+                ErrorMsg (baseField, "cannot use 'base' outside instance method body");
+                return new CompValuVoid (baseField);
+            }
+            if (!IsSDTInstMethod ()) {
+                ErrorMsg (baseField, "cannot access instance member of base class from static method");
+                return new CompValuVoid (baseField);
+            }
+
+            TokenDeclVar declVar = FindThisMember (sdtDecl.extends, baseField.fieldName, argsig);
+            if (declVar != null) {
+                CheckAccess (declVar, baseField.fieldName);
+                TokenType baseType = declVar.sdtClass.MakeRefToken (baseField);
+                CompValu  basePtr  = new CompValuArg (baseType, 0);
+                return AccessInstanceMember (declVar, basePtr, baseField, true);
+            }
+
+            ErrorMsg (baseField, "no member " + fieldName + ArgSigString (argsig) + " rootward of " + sdtDecl.longName.val);
+            return new CompValuVoid (baseField);
+        }
+
+        /**
+         * @brief We have an L-value token that is an instance field/method within a struct.
+         * @returns a CompValu giving the type and location of the field/method in the struct.
+         */
+        private CompValu GenerateFromLValIField (TokenLValIField lVal, TokenType[] argsig)
+        {
+            CompValu baseRVal = GenerateFromRVal (lVal.baseRVal);
+            string fieldName = lVal.fieldName.val + ArgSigString (argsig);
+
+            /*
+             * Maybe they are accessing an instance field, method or property of a script-defined class.
+             */
+            if (baseRVal.type is TokenTypeSDTypeClass) {
+                TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseRVal.type;
+                TokenDeclSDTypeClass sdtDecl = sdtType.decl;
+                TokenDeclVar declVar = FindThisMember (sdtDecl, lVal.fieldName, argsig);
+                if (declVar != null) {
+                    CheckAccess (declVar, lVal.fieldName);
+                    return AccessInstanceMember (declVar, baseRVal, lVal, false);
+                }
+                ErrorMsg (lVal.fieldName, "no member " + fieldName + " in class " + sdtDecl.longName.val);
+                return new CompValuVoid (lVal.fieldName);
+            }
+
+            /*
+             * Maybe they are accessing a method or property of a script-defined interface.
+             */
+            if (baseRVal.type is TokenTypeSDTypeInterface) {
+                TokenTypeSDTypeInterface sdtType = (TokenTypeSDTypeInterface)baseRVal.type;
+                TokenDeclVar declVar = FindInterfaceMember (sdtType, lVal.fieldName, argsig, ref baseRVal);
+                if (declVar != null) {
+                    return new CompValuIntfMember (declVar, baseRVal);
+                }
+                ErrorMsg (lVal.fieldName, "no member " + fieldName + " in interface " + sdtType.decl.longName.val);
+                return new CompValuVoid (lVal.fieldName);
+            }
+
+            /*
+             * Since we only have a few built-in types with fields, just pound them out.
+             */
+            if (baseRVal.type is TokenTypeArray) {
+
+                // no arguments, no parentheses, just the field name, returning integer
+                // but internally, it is a call to a method()
+                if (fieldName == "count") {
+                    return new CompValuIntInstROProp (tokenTypeInt, baseRVal, arrayCountMethodInfo);
+                }
+
+                // no arguments but with the parentheses, returning void
+                if (fieldName == "clear()") {
+                    return new CompValuIntInstMeth (XMR_Array.clearDelegate, baseRVal, arrayClearMethodInfo);
+                }
+
+                // single integer argument, returning an object
+                if (fieldName == "index(integer)") {
+                    return new CompValuIntInstMeth (XMR_Array.indexDelegate, baseRVal, arrayIndexMethodInfo);
+                }
+                if (fieldName == "value(integer)") {
+                    return new CompValuIntInstMeth (XMR_Array.valueDelegate, baseRVal, arrayValueMethodInfo);
+                }
+            }
+            if (baseRVal.type is TokenTypeRot) {
+                FieldInfo fi = null;
+                if (fieldName == "x") fi = rotationXFieldInfo;
+                if (fieldName == "y") fi = rotationYFieldInfo;
+                if (fieldName == "z") fi = rotationZFieldInfo;
+                if (fieldName == "s") fi = rotationSFieldInfo;
+                if (fi != null) {
+                    return new CompValuField (new TokenTypeFloat (lVal), baseRVal, fi);
+                }
+            }
+            if (baseRVal.type is TokenTypeVec) {
+                FieldInfo fi = null;
+                if (fieldName == "x") fi = vectorXFieldInfo;
+                if (fieldName == "y") fi = vectorYFieldInfo;
+                if (fieldName == "z") fi = vectorZFieldInfo;
+                if (fi != null) {
+                    return new CompValuField (new TokenTypeFloat (lVal), baseRVal, fi);
+                }
+            }
+
+            ErrorMsg (lVal, "type " + baseRVal.type.ToString () + " does not define member " + fieldName);
+            return baseRVal;
+        }
+
+        /**
+         * @brief We have an L-value token that is a function, method or variable name.
+         * @param lVal = name we are looking for
+         * @param argsig = null: just look for name as a variable
+         *                 else: look for name as a function/method being called with the given argument types
+         *                       eg, "(string,integer,list)"
+         * @returns a CompValu giving the type and location of the function, method or variable.
+         */
+        private CompValu GenerateFromLValName (TokenLValName lVal, TokenType[] argsig)
+        {
+            /*
+             * Look in variable stack then look for built-in constants and functions.
+             */
+            TokenDeclVar var = FindNamedVar (lVal, argsig);
+            if (var == null) {
+                ErrorMsg (lVal, "undefined constant/function/variable " + lVal.name.val + ArgSigString (argsig));
+                return new CompValuVoid (lVal);
+            }
+
+            /*
+             * Maybe it has an implied 'this.' on the front.
+             */
+            if ((var.sdtClass != null) && ((var.sdtFlags & ScriptReduce.SDT_STATIC) == 0)) {
+
+                if (!IsSDTInstMethod ()) {
+                    ErrorMsg (lVal, "cannot access instance member of class from static method");
+                    return new CompValuVoid (lVal);
+                }
+
+                /*
+                 * Don't allow something such as:
+                 *
+                 *    class A {
+                 *        integer I;
+                 *        class B {
+                 *            Print ()
+                 *            {
+                 *                llOwnerSay ("I=" + (string)I); <- access to I not allowed inside class B.
+                 *                                                  explicit reference required as we don't
+                 *                                                  have a valid reference to class A.
+                 *            }
+                 *        }
+                 *    }
+                 *
+                 * But do allow something such as:
+                 *
+                 *    class A {
+                 *        integer I;
+                 *    }
+                 *    class B : A {
+                 *        Print ()
+                 *        {
+                 *            llOwnerSay ("I=" + (string)I);
+                 *        }
+                 *    }
+                 */
+                for (TokenDeclSDType c = curDeclFunc.sdtClass; c != var.sdtClass; c = c.extends) {
+                    if (c == null) {
+                        // our arg0 points to an instance of curDeclFunc.sdtClass, not var.sdtClass
+                        ErrorMsg (lVal, "cannot access instance member of outer class with implied 'this'");
+                        break;
+                    }
+                }
+
+                CompValu thisCompValu = new CompValuArg (var.sdtClass.MakeRefToken (lVal), 0);
+                return AccessInstanceMember (var, thisCompValu, lVal, false);
+            }
+
+            /*
+             * It's a local variable, static field, global, constant, etc.
+             */
+            return var.location;
+        }
+
+        /**
+         * @brief Access a script-defined type's instance member
+         * @param declVar = which member (field,method,property) to access
+         * @param basePtr = points to particular object instance
+         * @param ignoreVirt = true: access declVar's method directly; else: maybe use vTable
+         * @returns where the field/method/property is located
+         */
+        private CompValu AccessInstanceMember (TokenDeclVar declVar, CompValu basePtr, Token errorAt, bool ignoreVirt)
+        {
+            if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) {
+                ErrorMsg (errorAt, "non-static reference to static member " + declVar.name.val);
+                return new CompValuVoid (declVar);
+            }
+            return new CompValuInstMember (declVar, basePtr, ignoreVirt);
+        }
+
+        /**
+         * @brief we have an L-value token that is a static member within a struct.
+         * @returns a CompValu giving the type and location of the member in the struct.
+         */
+        private CompValu GenerateFromLValSField (TokenLValSField lVal, TokenType[] argsig)
+        {
+            TokenType stType = lVal.baseType;
+            string fieldName = lVal.fieldName.val + ArgSigString (argsig);
+
+            /*
+             * Maybe they are accessing a static member of a script-defined class.
+             */
+            if (stType is TokenTypeSDTypeClass) {
+                TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)stType;
+                TokenDeclVar declVar = FindThisMember (sdtType.decl, lVal.fieldName, argsig);
+                if (declVar != null) {
+                    CheckAccess (declVar, lVal.fieldName);
+                    if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) == 0) {
+                        ErrorMsg (lVal.fieldName, "static reference to non-static member " + fieldName);
+                        return new CompValuVoid (lVal.fieldName);
+                    }
+                    return declVar.location;
+                }
+            }
+
+            ErrorMsg (lVal.fieldName, "no member " + fieldName + " in " + stType.ToString ());
+            return new CompValuVoid (lVal.fieldName);
+        }
+
+        /**
+         * @brief generate code from an RVal expression and return its type and where the result is stored.
+         * For anything that has side-effects, statements are generated that perform the computation then
+         * the result it put in a temp var and the temp var name is returned.
+         * For anything without side-effects, they are returned as an equivalent sequence of Emits.
+         * @param rVal = rVal token to be evaluated
+         * @param argsig = null: not being used in an function/method context
+         *                 else: string giving argument types, eg, "(string,integer,list,vector)"
+         *                       that can be used to select among overloaded methods
+         * @returns resultant type and location
+         */
+        private CompValu GenerateFromRVal (TokenRVal rVal)
+        {
+            return GenerateFromRVal (rVal, null);
+        }
+        private CompValu GenerateFromRVal (TokenRVal rVal, TokenType[] argsig)
+        {
+            errorMessageToken = rVal;
+
+            /*
+             * Maybe the expression can be converted to a constant.
+             */
+            bool didOne;
+            do {
+                didOne = false;
+                rVal = rVal.TryComputeConstant (LookupBodyConstants, ref didOne);
+            } while (didOne);
+
+            /*
+             * Generate code for the computation and return resulting type and location.
+             */
+            CompValu cVal = null;
+            if (rVal is TokenRValAsnPost)   cVal = GenerateFromRValAsnPost   ((TokenRValAsnPost)rVal);
+            if (rVal is TokenRValAsnPre)    cVal = GenerateFromRValAsnPre    ((TokenRValAsnPre)rVal);
+            if (rVal is TokenRValCall)      cVal = GenerateFromRValCall      ((TokenRValCall)rVal);
+            if (rVal is TokenRValCast)      cVal = GenerateFromRValCast      ((TokenRValCast)rVal);
+            if (rVal is TokenRValCondExpr)  cVal = GenerateFromRValCondExpr  ((TokenRValCondExpr)rVal);
+            if (rVal is TokenRValConst)     cVal = GenerateFromRValConst     ((TokenRValConst)rVal);
+            if (rVal is TokenRValInitDef)   cVal = GenerateFromRValInitDef   ((TokenRValInitDef)rVal);
+            if (rVal is TokenRValIsType)    cVal = GenerateFromRValIsType    ((TokenRValIsType)rVal);
+            if (rVal is TokenRValList)      cVal = GenerateFromRValList      ((TokenRValList)rVal);
+            if (rVal is TokenRValNewArIni)  cVal = GenerateFromRValNewArIni  ((TokenRValNewArIni)rVal);
+            if (rVal is TokenRValOpBin)     cVal = GenerateFromRValOpBin     ((TokenRValOpBin)rVal);
+            if (rVal is TokenRValOpUn)      cVal = GenerateFromRValOpUn      ((TokenRValOpUn)rVal);
+            if (rVal is TokenRValParen)     cVal = GenerateFromRValParen     ((TokenRValParen)rVal);
+            if (rVal is TokenRValRot)       cVal = GenerateFromRValRot       ((TokenRValRot)rVal);
+            if (rVal is TokenRValThis)      cVal = GenerateFromRValThis      ((TokenRValThis)rVal);
+            if (rVal is TokenRValUndef)     cVal = GenerateFromRValUndef     ((TokenRValUndef)rVal);
+            if (rVal is TokenRValVec)       cVal = GenerateFromRValVec       ((TokenRValVec)rVal);
+            if (rVal is TokenLVal)          cVal = GenerateFromLVal          ((TokenLVal)rVal, argsig);
+
+            if (cVal == null) throw new Exception ("bad rval class " + rVal.GetType ().ToString ());
+
+            /*
+             * Sanity check.
+             */
+            if (!youveAnError) {
+                if (cVal.type == null) throw new Exception ("cVal has no type " + cVal.GetType ());
+                string cValType = cVal.type.ToString ();
+                string rValType = rVal.GetRValType (this, argsig).ToString ();
+                if (cValType == "bool") cValType = "integer";
+                if (rValType == "bool") rValType = "integer";
+                if (cValType != rValType) {
+                    throw new Exception ("cVal.type " + cValType + " != rVal.type " + rValType +
+                                         "  (" + rVal.GetType ().Name + " " + rVal.SrcLoc + ")");
+                }
+            }
+
+            return cVal;
+        }
+
+        /**
+         * @brief compute the result of a binary operator (eg, add, subtract, multiply, lessthan)
+         * @param token = binary operator token, includes the left and right operands
+         * @returns where the resultant R-value is as something that doesn't have side effects
+         */
+        private CompValu GenerateFromRValOpBin (TokenRValOpBin token)
+        {
+            CompValu left, right;
+            string opcodeIndex = token.opcode.ToString ();
+
+            /*
+             * Comma operators are special, as they say to compute the left-hand value and 
+             * discard it, then compute the right-hand argument and that is the result.
+             */
+            if (opcodeIndex == ",") {
+
+                /*
+                 * Compute left-hand operand but throw away result.
+                 */
+                GenerateFromRVal (token.rValLeft);
+
+                /*
+                 * Compute right-hand operand and that is the value of the expression.
+                 */
+                return GenerateFromRVal (token.rValRight);
+            }
+
+            /*
+             * Simple overwriting assignments are their own special case,
+             * as we want to cast the R-value to the type of the L-value.
+             * And in the case of delegates, we want to use the arg signature
+             * of the delegate to select which overloaded method to use.
+             */
+            if (opcodeIndex == "=") {
+                if (!(token.rValLeft is TokenLVal)) {
+                    ErrorMsg (token, "invalid L-value for =");
+                    return GenerateFromRVal (token.rValLeft);
+                }
+                left  = GenerateFromLVal ((TokenLVal)token.rValLeft);
+                right = Trivialize (GenerateFromRVal (token.rValRight, left.GetArgTypes ()), token.rValRight);
+                left.PopPre (this, token.rValLeft);
+                right.PushVal (this, token.rValRight, left.type);  // push (left.type)right
+                left.PopPost (this, token.rValLeft);               // pop to left
+                return left;
+            }
+
+            /*
+             * There are String.Concat() methods available for 2, 3 and 4 operands.
+             * So see if we have a string concat op and optimize if so.
+             */
+            if ((opcodeIndex == "+") || 
+                ((opcodeIndex == "+=") && 
+                 (token.rValLeft is TokenLVal) && 
+                 (token.rValLeft.GetRValType (this, null) is TokenTypeStr))) {
+
+                /*
+                 * We are adding something.  Maybe it's a bunch of strings together.
+                 */
+                List<TokenRVal> scorvs = new List<TokenRVal> ();
+                if (StringConcatOperands (token.rValLeft, token.rValRight, scorvs, token.opcode)) {
+
+                    /*
+                     * Evaluate all the operands, right-to-left on purpose per LSL scripting.
+                     */
+                    int i;
+                    int n = scorvs.Count;
+                    CompValu[] scocvs = new CompValu[n];
+                    for (i = n; -- i >= 0;) {
+                        scocvs[i] = GenerateFromRVal (scorvs[i]);
+                        if (i > 0) scocvs[i] = Trivialize (scocvs[i], scorvs[i]);
+                    }
+
+                    /*
+                     * Figure out where to put the result.
+                     * A temp if '+', or back in original L-value if '+='.
+                     */
+                    CompValu retcv;
+                    if (opcodeIndex == "+") {
+                        retcv = new CompValuTemp (new TokenTypeStr (token.opcode), this);
+                    } else {
+                        retcv = GenerateFromLVal ((TokenLVal)token.rValLeft);
+                    }
+                    retcv.PopPre (this, token);
+
+                    /*
+                     * Call the String.Concat() methods, passing operands in left-to-right order.
+                     * Force a cast to string (retcv.type) for each operand.
+                     */
+                    ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
+                    while (i + 3 < n) {
+                        ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
+                        ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
+                        ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
+                        ilGen.Emit (scorvs[i], OpCodes.Call, stringConcat4MethodInfo);
+                    }
+                    if (i + 2 < n) {
+                        ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
+                        ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
+                        ilGen.Emit (scorvs[i], OpCodes.Call, stringConcat3MethodInfo);
+                    }
+                    if (i + 1 < n) {
+                        ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
+                        ilGen.Emit (scorvs[i], OpCodes.Call, stringConcat2MethodInfo);
+                    }
+
+                    /*
+                     * Put the result where we want it and return where we put it.
+                     */
+                    retcv.PopPost (this, token);
+                    return retcv;
+                }
+            }
+
+            /*
+             * If "&&&", it is a short-circuiting AND.
+             * Compute left-hand operand and if true, compute right-hand operand.
+             */
+            if (opcodeIndex == "&&&") {
+                bool leftVal, rightVal;
+                left = GenerateFromRVal (token.rValLeft);
+                if (!IsConstBoolExpr (left, out leftVal)) {
+                    ScriptMyLabel falseLabel = ilGen.DefineLabel ("ssandfalse");
+                    left.PushVal (this, tokenTypeBool);
+                    ilGen.Emit (token, OpCodes.Brfalse, falseLabel);
+                    right = GenerateFromRVal (token.rValRight);
+                    if (!IsConstBoolExpr (right, out rightVal)) {
+                        right.PushVal (this, tokenTypeBool);
+                        goto donessand;
+                    }
+                    if (!rightVal) {
+                        ilGen.MarkLabel (falseLabel);
+                        return new CompValuInteger (new TokenTypeInt (token.rValLeft), 0);
+                    }
+                    ilGen.Emit (token, OpCodes.Ldc_I4_1);
+                donessand:
+                    ScriptMyLabel doneLabel = ilGen.DefineLabel ("ssanddone");
+                    ilGen.Emit (token, OpCodes.Br, doneLabel);
+                    ilGen.MarkLabel (falseLabel);
+                    ilGen.Emit (token, OpCodes.Ldc_I4_0);
+                    ilGen.MarkLabel (doneLabel);
+                    CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
+                    retRVal.Pop (this, token);
+                    return retRVal;
+                }
+
+                if (!leftVal) {
+                    return new CompValuInteger (new TokenTypeInt (token.rValLeft), 0);
+                }
+
+                right = GenerateFromRVal (token.rValRight);
+                if (!IsConstBoolExpr (right, out rightVal)) {
+                    right.PushVal (this, tokenTypeBool);
+                    CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
+                    retRVal.Pop (this, token);
+                    return retRVal;
+                }
+                return new CompValuInteger (new TokenTypeInt (token), rightVal ? 1 : 0);
+            }
+
+            /*
+             * If "|||", it is a short-circuiting OR.
+             * Compute left-hand operand and if false, compute right-hand operand.
+             */
+            if (opcodeIndex == "|||") {
+                bool leftVal, rightVal;
+                left = GenerateFromRVal (token.rValLeft);
+                if (!IsConstBoolExpr (left, out leftVal)) {
+                    ScriptMyLabel trueLabel = ilGen.DefineLabel ("ssortrue");
+                    left.PushVal (this, tokenTypeBool);
+                    ilGen.Emit (token, OpCodes.Brtrue, trueLabel);
+                    right = GenerateFromRVal (token.rValRight);
+                    if (!IsConstBoolExpr (right, out rightVal)) {
+                        right.PushVal (this, tokenTypeBool);
+                        goto donessor;
+                    }
+                    if (rightVal) {
+                        ilGen.MarkLabel (trueLabel);
+                        return new CompValuInteger (new TokenTypeInt (token.rValLeft), 1);
+                    }
+                    ilGen.Emit (token, OpCodes.Ldc_I4_0);
+                donessor:
+                    ScriptMyLabel doneLabel = ilGen.DefineLabel ("ssanddone");
+                    ilGen.Emit (token, OpCodes.Br, doneLabel);
+                    ilGen.MarkLabel (trueLabel);
+                    ilGen.Emit (token, OpCodes.Ldc_I4_1);
+                    ilGen.MarkLabel (doneLabel);
+                    CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
+                    retRVal.Pop (this, token);
+                    return retRVal;
+                }
+
+                if (leftVal) {
+                    return new CompValuInteger (new TokenTypeInt (token.rValLeft), 1);
+                }
+
+                right = GenerateFromRVal (token.rValRight);
+                if (!IsConstBoolExpr (right, out rightVal)) {
+                    right.PushVal (this, tokenTypeBool);
+                    CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
+                    retRVal.Pop (this, token);
+                    return retRVal;
+                }
+                return new CompValuInteger (new TokenTypeInt (token), rightVal ? 1 : 0);
+            }
+
+            /*
+             * Computation of some sort, compute right-hand operand value then left-hand value
+             * because LSL is supposed to be right-to-left evaluation.
+             */
+            right = Trivialize (GenerateFromRVal (token.rValRight), token.rValRight);
+
+            /*
+             * If left is a script-defined class and there is a method with the operator's name,
+             * convert this to a call to that method with the right value as its single parameter.
+             * Except don't if the right value is 'undef' so they can always compare to undef.
+             */
+            TokenType leftType = token.rValLeft.GetRValType (this, null);
+            if ((leftType is TokenTypeSDTypeClass) && !(right.type is TokenTypeUndef)) {
+                TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)leftType;
+                TokenDeclSDTypeClass sdtDecl = sdtType.decl;
+                TokenType[] argsig = new TokenType[] { right.type };
+                TokenName funcName = new TokenName (token.opcode, "$op" + opcodeIndex);
+                TokenDeclVar declFunc = FindThisMember (sdtDecl, funcName, argsig);
+                if (declFunc != null) {
+                    CheckAccess (declFunc, funcName);
+                    left = GenerateFromRVal (token.rValLeft);
+                    CompValu method = AccessInstanceMember (declFunc, left, token, false);
+                    CompValu[] argRVals = new CompValu[] { right };
+                    return GenerateACall (method, argRVals, token);
+                }
+            }
+
+            /*
+             * Formulate key string for binOpStrings = (lefttype)(operator)(righttype)
+             */
+            string leftIndex = leftType.ToString ();
+            string rightIndex = right.type.ToString ();
+            string key = leftIndex + opcodeIndex + rightIndex;
+
+            /*
+             * If that key exists in table, then the operation is defined between those types
+             * ... and it produces an R-value of type as given in the table.
+             */
+            BinOpStr binOpStr;
+            if (BinOpStr.defined.TryGetValue (key, out binOpStr)) {
+
+                /*
+                 * If table contained an explicit assignment type like +=, output the statement without
+                 * casting the L-value, then return the L-value as the resultant value.
+                 *
+                 * Make sure we don't include comparisons (such as ==, >=, etc).
+                 * Nothing like +=, -=, %=, etc, generate a boolean, only the comparisons.
+                 */
+                if ((binOpStr.outtype != typeof (bool)) && opcodeIndex.EndsWith ("=") && (opcodeIndex != "!=")) {
+                    if (!(token.rValLeft is TokenLVal)) {
+                        ErrorMsg (token.rValLeft, "invalid L-value");
+                        return GenerateFromRVal (token.rValLeft);
+                    }
+                    left = GenerateFromLVal ((TokenLVal)token.rValLeft);
+                    binOpStr.emitBO (this, token, left, right, left);
+                    return left;
+                }
+
+                /*
+                 * It's of the form left binop right.
+                 * Compute left, perform operation then put result in a temp.
+                 */
+                left = GenerateFromRVal (token.rValLeft);
+                CompValu retRVal = new CompValuTemp (TokenType.FromSysType (token.opcode, binOpStr.outtype), this);
+                binOpStr.emitBO (this, token, left, right, retRVal);
+                return retRVal;
+            }
+
+            /*
+             * Nothing in the table, check for comparing object pointers because of the myriad of types possible.
+             * This will compare list pointers, null pointers, script-defined type pointers, array pointers, etc.
+             * It will show equal iff the memory addresses are equal and that is good enough.
+             */
+            if (!leftType.ToSysType().IsValueType && !right.type.ToSysType().IsValueType && ((opcodeIndex == "==") || (opcodeIndex == "!="))) {
+                CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
+                left = GenerateFromRVal (token.rValLeft);
+                left.PushVal (this, token.rValLeft);
+                right.PushVal (this, token.rValRight);
+                ilGen.Emit (token, OpCodes.Ceq);
+                if (opcodeIndex == "!=") {
+                    ilGen.Emit (token, OpCodes.Ldc_I4_1);
+                    ilGen.Emit (token, OpCodes.Xor);
+                }
+                retRVal.Pop (this, token);
+                return retRVal;
+            }
+
+            /*
+             * If the opcode ends with "=", it may be something like "+=".
+             * So look up the key as if we didn't have the "=" to tell us if the operation is legal.
+             * Also, the binary operation's output type must be the same as the L-value type.
+             * Likewise, integer += float not allowed because result is float, but float += integer is ok.
+             */
+            if (opcodeIndex.EndsWith ("=")) {
+                key = leftIndex + opcodeIndex.Substring (0, opcodeIndex.Length - 1) + rightIndex;
+                if (BinOpStr.defined.TryGetValue (key, out binOpStr)) {
+                    if (!(token.rValLeft is TokenLVal)) {
+                        ErrorMsg (token, "invalid L-value for <op>=");
+                        return GenerateFromRVal (token.rValLeft);
+                    }
+                    if (!binOpStr.rmwOK) {
+                        ErrorMsg (token, "<op>= not allowed: " + leftIndex + " " + opcodeIndex + " " + rightIndex);
+                        return new CompValuVoid (token);
+                    }
+
+                    /*
+                     * Now we know for something like %= that left%right is legal for the types given.
+                     */
+                    left = GenerateFromLVal ((TokenLVal)token.rValLeft);
+                    if (binOpStr.outtype == leftType.ToSysType ()) {
+                        binOpStr.emitBO (this, token, left, right, left);
+                    } else {
+                        CompValu temp = new CompValuTemp (TokenType.FromSysType (token, binOpStr.outtype), this);
+                        binOpStr.emitBO (this, token, left, right, temp);
+                        left.PopPre (this, token);
+                        temp.PushVal (this, token, leftType);
+                        left.PopPost (this, token);
+                    }
+                    return left;
+                }
+            }
+
+            /*
+             * Can't find it, oh well.
+             */
+            ErrorMsg (token, "op not defined: " + leftIndex + " " + opcodeIndex + " " + rightIndex);
+            return new CompValuVoid (token);
+        }
+
+        /**
+         * @brief Queue the given operands to the end of the scos list.
+         *        If it can be broken down into more string concat operands, do so.
+         *        Otherwise, just push it as one operand.
+         * @param leftRVal  = left-hand operand of a '+' operation
+         * @param rightRVal = right-hand operand of a '+' operation
+         * @param scos      = left-to-right list of operands for the string concat so far
+         * @param addop     = the add operator token (either '+' or '+=')
+         * @returns false: neither operand is a string, nothing added to scos
+         *           true: scos = updated with leftRVal then rightRVal added onto the end, possibly broken down further
+         */
+        private bool StringConcatOperands (TokenRVal leftRVal, TokenRVal rightRVal, List<TokenRVal> scos, TokenKw addop)
+        {
+            /*
+             * If neither operand is a string (eg, float+integer), then the result isn't going to be a string.
+             */
+            TokenType leftType  = leftRVal.GetRValType  (this, null);
+            TokenType rightType = rightRVal.GetRValType (this, null);
+            if (!(leftType is TokenTypeStr) && !(rightType is TokenTypeStr)) return false;
+
+            /*
+             * Also, list+string => list so reject that too.
+             * Also, string+list => list so reject that too.
+             */
+            if (leftType  is TokenTypeList) return false;
+            if (rightType is TokenTypeList) return false;
+
+            /*
+             * Append values to the end of the list in left-to-right order.
+             * If value is formed from a something+something => string, 
+             * push them as separate values, otherwise push as one value.
+             */
+            StringConcatOperand (leftType,  leftRVal,  scos);
+            StringConcatOperand (rightType, rightRVal, scos);
+
+            /*
+             * Maybe constant strings can be concatted.
+             */
+            try {
+                int len;
+                while (((len = scos.Count) >= 2) && 
+                       ((leftRVal  = scos[len-2]) is TokenRValConst) && 
+                       ((rightRVal = scos[len-1]) is TokenRValConst)) {
+                    object sum = addop.binOpConst (((TokenRValConst)leftRVal).val, 
+                                                   ((TokenRValConst)rightRVal).val);
+                    scos[len-2] = new TokenRValConst (addop, sum);
+                    scos.RemoveAt (len - 1);
+                }
+            } catch {
+            }
+
+            /*
+             * We pushed some string stuff.
+             */
+            return true;
+        }
+
+        /**
+         * @brief Queue the given operand to the end of the scos list.
+         *        If it can be broken down into more string concat operands, do so.
+         *        Otherwise, just push it as one operand.
+         * @param type = rVal's resultant type
+         * @param rVal = operand to examine
+         * @param scos = left-to-right list of operands for the string concat so far
+         * @returns with scos = updated with rVal added onto the end, possibly broken down further
+         */
+        private void StringConcatOperand (TokenType type, TokenRVal rVal, List<TokenRVal> scos)
+        {
+            bool didOne;
+            do {
+                didOne = false;
+                rVal   = rVal.TryComputeConstant (LookupBodyConstants, ref didOne);
+            } while (didOne);
+
+            if (!(type is TokenTypeStr)) goto pushasis;
+            if (!(rVal is TokenRValOpBin)) goto pushasis;
+            TokenRValOpBin rValOpBin = (TokenRValOpBin)rVal;
+            if (!(rValOpBin.opcode is TokenKwAdd)) goto pushasis;
+            if (StringConcatOperands (rValOpBin.rValLeft, rValOpBin.rValRight, scos, rValOpBin.opcode)) return;
+            pushasis:
+            scos.Add (rVal);
+        }
+
+        /**
+         * @brief compute the result of an unary operator
+         * @param token = unary operator token, includes the operand
+         * @returns where the resultant R-value is
+         */
+        private CompValu GenerateFromRValOpUn (TokenRValOpUn token)
+        {
+            CompValu inRVal = GenerateFromRVal (token.rVal);
+
+            /*
+             * Script-defined types can define their own methods to handle unary operators.
+             */
+            if (inRVal.type is TokenTypeSDTypeClass) {
+                TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)inRVal.type;
+                TokenDeclSDTypeClass sdtDecl = sdtType.decl;
+                TokenName funcName = new TokenName (token.opcode, "$op" + token.opcode.ToString ());
+                TokenDeclVar declFunc = FindThisMember (sdtDecl, funcName, zeroArgs);
+                if (declFunc != null) {
+                    CheckAccess (declFunc, funcName);
+                    CompValu method = AccessInstanceMember (declFunc, inRVal, token, false);
+                    return GenerateACall (method, zeroCompValus, token);
+                }
+            }
+
+            /*
+             * Otherwise use the default.
+             */
+            return UnOpGenerate (inRVal, token.opcode);
+        }
+
+        /**
+         * @brief postfix operator -- this returns the type and location of the resultant value
+         */
+        private CompValu GenerateFromRValAsnPost (TokenRValAsnPost asnPost)
+        {
+            CompValu lVal = GenerateFromLVal (asnPost.lVal);
+
+            /*
+             * Make up a temp to save original value in.
+             */
+            CompValuTemp result = new CompValuTemp (lVal.type, this);
+
+            /*
+             * Prepare to pop incremented value back into variable being incremented.
+             */
+            lVal.PopPre (this, asnPost.lVal);
+
+            /*
+             * Copy original value to temp and leave value on stack.
+             */
+            lVal.PushVal (this, asnPost.lVal);
+            ilGen.Emit (asnPost.lVal, OpCodes.Dup);
+            result.Pop (this, asnPost.lVal);
+
+            /*
+             * Perform the ++/--.
+             */
+            if ((lVal.type is TokenTypeChar) || (lVal.type is TokenTypeInt)) {
+                ilGen.Emit (asnPost, OpCodes.Ldc_I4_1);
+            } else if (lVal.type is TokenTypeFloat) {
+                ilGen.Emit (asnPost, OpCodes.Ldc_R4, 1.0f);
+            } else {
+                lVal.PopPost (this, asnPost.lVal);
+                ErrorMsg (asnPost, "invalid type for " + asnPost.postfix.ToString ());
+                return lVal;
+            }
+            switch (asnPost.postfix.ToString ()) {
+                case "++": {
+                    ilGen.Emit (asnPost, OpCodes.Add);
+                    break;
+                }
+                case "--": {
+                    ilGen.Emit (asnPost, OpCodes.Sub);
+                    break;
+                }
+                default: throw new Exception ("unknown asnPost op");
+            }
+
+            /*
+             * Store new value in original variable.
+             */
+            lVal.PopPost (this, asnPost.lVal);
+
+            return result;
+        }
+
+        /**
+         * @brief prefix operator -- this returns the type and location of the resultant value
+         */
+        private CompValu GenerateFromRValAsnPre (TokenRValAsnPre asnPre)
+        {
+            CompValu lVal = GenerateFromLVal (asnPre.lVal);
+
+            /*
+             * Make up a temp to put result in.
+             */
+            CompValuTemp result = new CompValuTemp (lVal.type, this);
+
+            /*
+             * Prepare to pop incremented value back into variable being incremented.
+             */
+            lVal.PopPre (this, asnPre.lVal);
+
+            /*
+             * Push original value.
+             */
+            lVal.PushVal (this, asnPre.lVal);
+
+            /*
+             * Perform the ++/--.
+             */
+            if ((lVal.type is TokenTypeChar) || (lVal.type is TokenTypeInt)) {
+                ilGen.Emit (asnPre, OpCodes.Ldc_I4_1);
+            } else if (lVal.type is TokenTypeFloat) {
+                ilGen.Emit (asnPre, OpCodes.Ldc_R4, 1.0f);
+            } else {
+                lVal.PopPost (this, asnPre.lVal);
+                ErrorMsg (asnPre, "invalid type for " + asnPre.prefix.ToString ());
+                return lVal;
+            }
+            switch (asnPre.prefix.ToString ()) {
+                case "++": {
+                    ilGen.Emit (asnPre, OpCodes.Add);
+                    break;
+                }
+                case "--": {
+                    ilGen.Emit (asnPre, OpCodes.Sub);
+                    break;
+                }
+                default: throw new Exception ("unknown asnPre op");
+            }
+
+            /*
+             * Store new value in temp variable, keeping new value on stack.
+             */
+            ilGen.Emit (asnPre.lVal, OpCodes.Dup);
+            result.Pop (this, asnPre.lVal);
+
+            /*
+             * Store new value in original variable.
+             */
+            lVal.PopPost (this, asnPre.lVal);
+
+            return result;
+        }
+
+        /**
+         * @brief Generate code that calls a function or object's method.
+         * @returns where the call's return value is stored (a TokenTypeVoid if void)
+         */
+        private CompValu GenerateFromRValCall (TokenRValCall call)
+        {
+            CompValu method;
+            CompValu[] argRVals;
+            int i, nargs;
+            TokenRVal arg;
+            TokenType[] argTypes;
+
+            /*
+             * Compute the values of all the function's call arguments.
+             * Save where the computation results are in the argRVals[] array.
+             * Might as well build the argument signature from the argument types, too.
+             */
+            nargs = call.nArgs;
+            argRVals = new CompValu[nargs];
+            argTypes = new TokenType[nargs];
+            if (nargs > 0) {
+                i = 0;
+                for (arg = call.args; arg != null; arg = (TokenRVal)arg.nextToken) {
+                    argRVals[i] = GenerateFromRVal (arg);
+                    argTypes[i] = argRVals[i].type;
+                    i ++;
+                }
+            }
+
+            /*
+             * Get function/method's entrypoint that matches the call argument types.
+             */
+            method = GenerateFromRVal (call.meth, argTypes);
+            if (method == null) return null;
+
+            return GenerateACall (method, argRVals, call);
+        }
+
+        /**
+         * @brief Generate call to a function/method.
+         * @param method = function/method being called
+         * @param argVRVals = its call parameters (zero length if none)
+         * @param call = where in source code call is being made from (for error messages)
+         * @returns type and location of return value (CompValuVoid if none)
+         */
+        private CompValu GenerateACall (CompValu method, CompValu[] argRVals, Token call)
+        {
+            CompValuTemp result;
+            int i, nArgs;
+            TokenType retType;
+            TokenType[] argTypes;
+
+            /*
+             * Must be some kind of callable.
+             */
+            retType = method.GetRetType ();  // TokenTypeVoid if void; null means a variable
+            if (retType == null) {
+                ErrorMsg (call, "must be a delegate, function or method");
+                return new CompValuVoid (call);
+            }
+
+            /*
+             * Get a location for return value.
+             */
+            if (retType is TokenTypeVoid) {
+                result = new CompValuVoid (call);
+            } else {
+                result = new CompValuTemp (retType, this);
+            }
+
+            /*
+             * Make sure all arguments are trivial, ie, don't involve their own call labels.
+             * For any that aren't, output code to calculate the arg and put in a temporary.
+             */
+            nArgs = argRVals.Length;
+            for (i = 0; i < nArgs; i ++) {
+                if (!argRVals[i].IsReadTrivial (this, call)) {
+                    argRVals[i] = Trivialize (argRVals[i], call);
+                }
+            }
+
+            /*
+             * Inline functions know how to generate their own call.
+             */
+            if (method is CompValuInline) {
+                CompValuInline inline = (CompValuInline)method;
+                inline.declInline.CodeGen (this, call, result, argRVals);
+                return result;
+            }
+
+            /*
+             * Push whatever the function/method needs as a this argument, if anything.
+             */
+            method.CallPre (this, call);
+
+            /*
+             * Push the script-visible args, left-to-right.
+             */
+            argTypes = method.GetArgTypes ();
+            for (i = 0; i < nArgs; i ++) {
+                if (argTypes == null) {
+                    argRVals[i].PushVal (this, call);
+                } else {
+                    argRVals[i].PushVal (this, call, argTypes[i]);
+                }
+            }
+
+            /*
+             * Now output call instruction.
+             */
+            method.CallPost (this, call);
+
+            /*
+             * Deal with the return value (if any), by putting it in 'result'.
+             */
+            result.Pop (this, call, retType);
+            return result;
+        }
+
+        /**
+         * @brief This is needed to avoid nesting call labels around non-trivial properties.
+         *        It should be used for the second (and later) operands.
+         *        Note that a 'call' is considered an operator, so all arguments of a call
+         *        should be trivialized, but the method itself does not need to be.
+         */
+        public CompValu Trivialize (CompValu operand, Token errorAt)
+        {
+            if (operand.IsReadTrivial (this, errorAt)) return operand;
+            CompValuTemp temp = new CompValuTemp (operand.type, this);
+            operand.PushVal (this, errorAt);
+            temp.Pop (this, errorAt);
+            return temp;
+        }
+
+        /**
+         * @brief Generate code that casts a value to a particular type.
+         * @returns where the result of the conversion is stored.
+         */
+        private CompValu GenerateFromRValCast (TokenRValCast cast)
+        {
+            /*
+             * If casting to a delegate type, use the argment signature 
+             * of the delegate to help select the function/method, eg, 
+             *    '(delegate string(integer))ToString'
+             * will select 'string ToString(integer x)'
+             * instaead of 'string ToString(float x)' or anything else
+             */
+            TokenType[] argsig = null;
+            TokenType outType = cast.castTo;
+            if (outType is TokenTypeSDTypeDelegate) {
+                argsig = ((TokenTypeSDTypeDelegate)outType).decl.GetArgTypes ();
+            }
+
+            /*
+             * Generate the value that is being cast.
+             * If the value is already the requested type, just use it as is.
+             */
+            CompValu inRVal = GenerateFromRVal (cast.rVal, argsig);
+            if (inRVal.type == outType) return inRVal;
+
+            /*
+             * Different type, generate casting code, putting the result in a temp of the output type.
+             */
+            CompValu outRVal = new CompValuTemp (outType, this);
+            outRVal.PopPre (this, cast);
+            inRVal.PushVal (this, cast, outType, true);
+            outRVal.PopPost (this, cast);
+            return outRVal;
+        }
+
+        /**
+         * @brief Compute conditional expression value.
+         * @returns type and location of computed value.
+         */
+        private CompValu GenerateFromRValCondExpr (TokenRValCondExpr rValCondExpr)
+        {
+            bool condVal;
+            CompValu condValu = GenerateFromRVal (rValCondExpr.condExpr);
+            if (IsConstBoolExpr (condValu, out condVal)) {
+                return GenerateFromRVal (condVal ? rValCondExpr.trueExpr : rValCondExpr.falseExpr);
+            }
+
+            ScriptMyLabel falseLabel = ilGen.DefineLabel ("condexfalse");
+            ScriptMyLabel doneLabel  = ilGen.DefineLabel ("condexdone");
+
+            condValu.PushVal (this, rValCondExpr.condExpr, tokenTypeBool);
+            ilGen.Emit (rValCondExpr, OpCodes.Brfalse, falseLabel);
+
+            CompValu trueValu = GenerateFromRVal (rValCondExpr.trueExpr);
+            trueValu.PushVal (this, rValCondExpr.trueExpr);
+            ilGen.Emit (rValCondExpr, OpCodes.Br, doneLabel);
+
+            ilGen.MarkLabel (falseLabel);
+            CompValu falseValu = GenerateFromRVal (rValCondExpr.falseExpr);
+            falseValu.PushVal (this, rValCondExpr.falseExpr);
+
+            if (trueValu.type.GetType () != falseValu.type.GetType ()) {
+                ErrorMsg (rValCondExpr, "? operands " + trueValu.type.ToString () + " : " +
+                              falseValu.type.ToString () + " must be of same type");
+            }
+
+            ilGen.MarkLabel (doneLabel);
+            CompValuTemp retRVal = new CompValuTemp (trueValu.type, this);
+            retRVal.Pop (this, rValCondExpr);
+            return retRVal;
+        }
+
+        /**
+         * @brief Constant in the script somewhere
+         * @returns where the constants value is stored
+         */
+        private CompValu GenerateFromRValConst (TokenRValConst rValConst)
+        {
+            switch (rValConst.type) {
+                case TokenRValConstType.CHAR: {
+                    return new CompValuChar (new TokenTypeChar (rValConst), (char)(rValConst.val));
+                }
+                case TokenRValConstType.FLOAT: {
+                    return new CompValuFloat (new TokenTypeFloat (rValConst), (double)(rValConst.val));
+                }
+                case TokenRValConstType.INT: {
+                    return new CompValuInteger (new TokenTypeInt (rValConst), (int)(rValConst.val));
+                }
+                case TokenRValConstType.KEY: {
+                    return new CompValuString (new TokenTypeKey (rValConst), (string)(rValConst.val));
+                }
+                case TokenRValConstType.STRING: {
+                    return new CompValuString (new TokenTypeStr (rValConst), (string)(rValConst.val));
+                }
+            }
+            throw new Exception ("unknown constant type " + rValConst.val.GetType ());
+        }
+
+        /**
+         * @brief generate a new list object
+         * @param rValList = an rVal to create it from
+         */
+        private CompValu GenerateFromRValList (TokenRValList rValList)
+        {
+            /*
+             * Compute all element values and remember where we put them.
+             * Do it right-to-left as customary for LSL scripts.
+             */
+            int i = 0;
+            TokenRVal lastRVal = null;
+            for (TokenRVal val = rValList.rVal; val != null; val = (TokenRVal)val.nextToken) {
+                i ++;
+                val.prevToken = lastRVal;
+                lastRVal = val;
+            }
+            CompValu[] vals = new CompValu[i];
+            for (TokenRVal val = lastRVal; val != null; val = (TokenRVal)val.prevToken) {
+                vals[--i] = GenerateFromRVal (val);
+            }
+
+            /*
+             * This is the temp that will hold the created list.
+             */
+            CompValuTemp newList = new CompValuTemp (new TokenTypeList (rValList.rVal), this);
+
+            /*
+             * Create a temp object[] array to hold all the initial values.
+             */
+            ilGen.Emit (rValList, OpCodes.Ldc_I4, rValList.nItems);
+            ilGen.Emit (rValList, OpCodes.Newarr, typeof (object));
+
+            /*
+             * Populate the array.
+             */
+            i = 0;
+            for (TokenRVal val = rValList.rVal; val != null; val = (TokenRVal)val.nextToken) {
+
+                /*
+                 * Get pointer to temp array object.
+                 */
+                ilGen.Emit (rValList, OpCodes.Dup);
+
+                /*
+                 * Get index in that array.
+                 */
+                ilGen.Emit (rValList, OpCodes.Ldc_I4, i);
+
+                /*
+                 * Store initialization value in array location.
+                 * However, floats and ints need to be converted to LSL_Float and LSL_Integer,
+                 * or things like llSetPayPrice() will puque when they try to cast the elements
+                 * to LSL_Float or LSL_Integer.  Likewise with string/LSL_String.
+                 *
+                 * Maybe it's already LSL-boxed so we don't do anything with it except make sure
+                 * it is an object, not a struct.
+                 */
+                CompValu eRVal = vals[i++];
+                eRVal.PushVal (this, val);
+                if (eRVal.type.ToLSLWrapType () == null) {
+                    if (eRVal.type is TokenTypeFloat) {
+                        ilGen.Emit (val, OpCodes.Newobj, lslFloatConstructorInfo);
+                        ilGen.Emit (val, OpCodes.Box, typeof (LSL_Float));
+                    } else if (eRVal.type is TokenTypeInt) {
+                        ilGen.Emit (val, OpCodes.Newobj, lslIntegerConstructorInfo);
+                        ilGen.Emit (val, OpCodes.Box, typeof (LSL_Integer));
+                    } else if ((eRVal.type is TokenTypeKey) || (eRVal.type is TokenTypeStr)) {
+                        ilGen.Emit (val, OpCodes.Newobj, lslStringConstructorInfo);
+                        ilGen.Emit (val, OpCodes.Box, typeof (LSL_String));
+                    } else if (eRVal.type.ToSysType ().IsValueType) {
+                        ilGen.Emit (val, OpCodes.Box, eRVal.type.ToSysType ());
+                    }
+                } else if (eRVal.type.ToLSLWrapType ().IsValueType) {
+
+                    // Convert the LSL value structs to an object of the LSL-boxed type
+                    ilGen.Emit (val, OpCodes.Box, eRVal.type.ToLSLWrapType ());
+                }
+                ilGen.Emit (val, OpCodes.Stelem, typeof (object));
+            }
+
+            /*
+             * Create new list object from temp initial value array (whose ref is still on the stack).
+             */
+            ilGen.Emit (rValList, OpCodes.Newobj, lslListConstructorInfo);
+            newList.Pop (this, rValList);
+            return newList;
+        }
+
+        /**
+         * @brief New array allocation with initializer expressions.
+         */
+        private CompValu GenerateFromRValNewArIni (TokenRValNewArIni rValNewArIni)
+        {
+            return MallocAndInitArray (rValNewArIni.arrayType, rValNewArIni.valueList);
+        }
+
+        /**
+         * @brief Mallocate and initialize an array from its initialization list.
+         * @param arrayType = type of the array to be allocated and initialized
+         * @param values    = initialization value list used to size and initialize the array.
+         * @returns memory location of the resultant initialized array.
+         */
+        private CompValu MallocAndInitArray (TokenType arrayType, TokenList values)
+        {
+            TokenDeclSDTypeClass arrayDecl = ((TokenTypeSDTypeClass)arrayType).decl;
+            TokenType eleType = arrayDecl.arrayOfType;
+            int rank = arrayDecl.arrayOfRank;
+
+            // Get size of each of the dimensions by scanning the initialization value list
+            int[] dimSizes = new int[rank];
+            FillInDimSizes (dimSizes, 0, rank, values);
+
+            // Figure out where the array's $new() method is
+            TokenType[] newargsig = new TokenType[rank];
+            for (int k = 0; k < rank; k ++) {
+                newargsig[k] = tokenTypeInt;
+            }
+            TokenDeclVar newMeth = FindThisMember (arrayDecl, new TokenName (null, "$new"), newargsig);
+
+            // Output a call to malloc the array with all default values
+            //    array = ArrayType.$new (dimSizes[0], dimSizes[1], ...)
+            CompValuTemp array = new CompValuTemp (arrayType, this);
+            PushXMRInst ();
+            for (int k = 0; k < rank; k ++) {
+                ilGen.Emit (values, OpCodes.Ldc_I4, dimSizes[k]);
+            }
+            ilGen.Emit (values, OpCodes.Call, newMeth.ilGen);
+            array.Pop (this, arrayType);
+
+            // Figure out where the array's Set() method is
+            TokenType[] setargsig = new TokenType[rank+1];
+            for (int k = 0; k < rank; k ++) {
+                setargsig[k] = tokenTypeInt;
+            }
+            setargsig[rank] = eleType;
+            TokenDeclVar setMeth = FindThisMember (arrayDecl, new TokenName (null, "Set"), setargsig);
+
+            // Fill in the array with the initializer values
+            FillInInitVals (array, setMeth, dimSizes, 0, rank, values, eleType);
+
+            // The array is our resultant value
+            return array;
+        }
+
+        /**
+         * @brief Compute an array's dimensions given its initialization value list
+         * @param dimSizes = filled in with array's dimensions
+         * @param dimNo    = what dimension the 'values' list applies to
+         * @param rank     = total number of dimensions of the array
+         * @param values   = list of values to initialize the array's 'dimNo' dimension with
+         * @returns with dimSizes[dimNo..rank-1] filled in
+         */
+        private static void FillInDimSizes (int[] dimSizes, int dimNo, int rank, TokenList values)
+        {
+            // the size of a dimension is the largest number of initializer elements at this level
+            // for dimNo 0, this is the number of elements in the top-level list
+            if (dimSizes[dimNo] < values.tl.Count) dimSizes[dimNo] = values.tl.Count;
+
+            // see if there is another dimension to calculate
+            if (++ dimNo < rank) {
+
+                // its size is the size of the largest initializer list at the next inner level
+                foreach (Token val in values.tl) {
+                    if (val is TokenList) {
+                        TokenList subvals = (TokenList)val;
+                        FillInDimSizes (dimSizes, dimNo, rank, subvals);
+                    }
+                }
+            }
+        }
+
+        /**
+         * @brief Output code to fill in array's initialization values
+         * @param array      = array to be filled in
+         * @param setMeth    = the array's Set() method
+         * @param subscripts = holds subscripts being built
+         * @param dimNo      = which dimension the 'values' are for
+         * @param values     = list of initialization values for dimension 'dimNo'
+         * @param rank       = number of dimensions of 'array'
+         * @param values     = list of values to initialize the array's 'dimNo' dimension with
+         * @param eleType    = the element's type
+         * @returns with code emitted to initialize array's [subscripts[0], ..., subscripts[dimNo-1], *, *, ...]
+         *                                                          dimNo and up completely filled ---^
+         */
+        private void FillInInitVals (CompValu array, TokenDeclVar setMeth, int[] subscripts, int dimNo, int rank, TokenList values, TokenType eleType)
+        {
+            subscripts[dimNo] = 0;
+            foreach (Token val in values.tl) {
+                CompValu initValue = null;
+
+                /*
+                 * If it is a sublist, process it.
+                 *    If we don't have enough subscripts yet, hopefully that sublist will have enough.
+                 *    If we already have enough subscripts, then that sublist can be for an element of this supposedly jagged array.
+                 */
+                if (val is TokenList) {
+                    TokenList sublist = (TokenList)val;
+                    if (dimNo + 1 < rank) {
+
+                        /*
+                         * We don't have enough subscripts yet, hopefully the sublist has the rest.
+                         */
+                        FillInInitVals (array, setMeth, subscripts, dimNo + 1, rank, sublist, eleType);
+                    } else if ((eleType is TokenTypeSDTypeClass) && (((TokenTypeSDTypeClass)eleType).decl.arrayOfType == null)) {
+
+                        /*
+                         * If we aren't a jagged array either, we can't do anything with the sublist.
+                         */
+                        ErrorMsg (val, "too many brace levels");
+                    } else {
+
+                        /*
+                         * We are a jagged array, so malloc a subarray and initialize it with the sublist.
+                         * Then we can use that subarray to fill this array's element.
+                         */
+                        initValue = MallocAndInitArray (eleType, sublist);
+                    }
+                }
+
+                /*
+                 * If it is a value expression, then output code to compute the value.
+                 */
+                if (val is TokenRVal) {
+                    if (dimNo + 1 < rank) {
+                        ErrorMsg ((Token)val, "not enough brace levels");
+                    } else {
+                        initValue = GenerateFromRVal ((TokenRVal)val);
+                    }
+                }
+
+                /*
+                 * If there is an initValue, output "array.Set (subscript[0], subscript[1], ..., initValue)"
+                 */
+                if (initValue != null) {
+                    array.PushVal (this, val);
+                    for (int i = 0; i <= dimNo; i ++) {
+                        ilGen.Emit (val, OpCodes.Ldc_I4, subscripts[i]);
+                    }
+                    initValue.PushVal (this, val, eleType);
+                    ilGen.Emit (val, OpCodes.Call, setMeth.ilGen);
+                }
+
+                /*
+                 * That subscript is processed one way or another, on to the next.
+                 */
+                subscripts[dimNo] ++;
+            }
+        }
+
+        /**
+         * @brief parenthesized expression
+         * @returns type and location of the result of the computation.
+         */
+        private CompValu GenerateFromRValParen (TokenRValParen rValParen)
+        {
+            return GenerateFromRVal (rValParen.rVal);
+        }
+
+        /**
+         * @brief create a rotation object from the x,y,z,w value expressions.
+         */
+        private CompValu GenerateFromRValRot (TokenRValRot rValRot)
+        {
+            CompValu xRVal, yRVal, zRVal, wRVal;
+
+            xRVal = Trivialize (GenerateFromRVal (rValRot.xRVal), rValRot);
+            yRVal = Trivialize (GenerateFromRVal (rValRot.yRVal), rValRot);
+            zRVal = Trivialize (GenerateFromRVal (rValRot.zRVal), rValRot);
+            wRVal = Trivialize (GenerateFromRVal (rValRot.wRVal), rValRot);
+            return new CompValuRot (new TokenTypeRot (rValRot), xRVal, yRVal, zRVal, wRVal);
+        }
+
+        /**
+         * @brief Using 'this' as a pointer to the current script-defined instance object.
+         *        The value is located in arg #0 of the current instance method.
+         */
+        private CompValu GenerateFromRValThis (TokenRValThis zhis)
+        {
+            if (!IsSDTInstMethod ()) {
+                ErrorMsg (zhis, "cannot access instance member of class from static method");
+                return new CompValuVoid (zhis);
+            }
+            return new CompValuArg (curDeclFunc.sdtClass.MakeRefToken (zhis), 0);
+        }
+
+        /**
+         * @brief 'undefined' constant.
+         *        If this constant gets written to an array element, it will delete that element from the array.
+         *        If the script retrieves an element by key that is not defined, it will get this value.
+         *        This value can be stored in and retrieved from variables of type 'object' or script-defined classes.
+         *        It is a runtime error to cast this value to any other type, eg, 
+         *        we don't allow list or string variables to be null pointers.
+         */
+        private CompValu GenerateFromRValUndef (TokenRValUndef rValUndef)
+        {
+            return new CompValuNull (new TokenTypeUndef (rValUndef));
+        }
+
+        /**
+         * @brief create a vector object from the x,y,z value expressions.
+         */
+        private CompValu GenerateFromRValVec (TokenRValVec rValVec)
+        {
+            CompValu xRVal, yRVal, zRVal;
+
+            xRVal = Trivialize (GenerateFromRVal (rValVec.xRVal), rValVec);
+            yRVal = Trivialize (GenerateFromRVal (rValVec.yRVal), rValVec);
+            zRVal = Trivialize (GenerateFromRVal (rValVec.zRVal), rValVec);
+            return new CompValuVec (new TokenTypeVec (rValVec), xRVal, yRVal, zRVal);
+        }
+
+        /**
+         * @brief Generate code to get the default initialization value for a variable.
+         */
+        private CompValu GenerateFromRValInitDef (TokenRValInitDef rValInitDef)
+        {
+            TokenType type = rValInitDef.type;
+
+            if (type is TokenTypeChar) {
+                return new CompValuChar (type, (char)0);
+            }
+            if (type is TokenTypeRot) {
+                CompValuFloat x = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.x);
+                CompValuFloat y = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.y);
+                CompValuFloat z = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.z);
+                CompValuFloat s = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.s);
+                return new CompValuRot (type, x, y, z, s);
+            }
+            if ((type is TokenTypeKey) || (type is TokenTypeStr)) {
+                return new CompValuString (type, "");
+            }
+            if (type is TokenTypeVec) {
+                CompValuFloat x = new CompValuFloat (type, ScriptBaseClass.ZERO_VECTOR.x);
+                CompValuFloat y = new CompValuFloat (type, ScriptBaseClass.ZERO_VECTOR.y);
+                CompValuFloat z = new CompValuFloat (type, ScriptBaseClass.ZERO_VECTOR.z);
+                return new CompValuVec (type, x, y, z);
+            }
+            if (type is TokenTypeInt) {
+                return new CompValuInteger (type, 0);
+            }
+            if (type is TokenTypeFloat) {
+                return new CompValuFloat (type, 0);
+            }
+            if (type is TokenTypeVoid) {
+                return new CompValuVoid (type);
+            }
+
+            /*
+             * Default for 'object' type is 'undef'.
+             * Likewise for script-defined classes and interfaces.
+             */
+            if ((type is TokenTypeObject) || (type is TokenTypeSDTypeClass) || (type is TokenTypeSDTypeDelegate) || 
+                (type is TokenTypeSDTypeInterface) || (type is TokenTypeExc)) {
+                return new CompValuNull (type);
+            }
+
+            /*
+             * array and list
+             */
+            CompValuTemp temp = new CompValuTemp (type, this);
+            PushDefaultValue (type);
+            temp.Pop (this, rValInitDef, type);
+            return temp;
+        }
+
+        /**
+         * @brief Generate code to process an <rVal> is <type> expression, and produce a boolean value.
+         */
+        private CompValu GenerateFromRValIsType (TokenRValIsType rValIsType)
+        {
+            /*
+             * Expression we want to know the type of.
+             */
+            CompValu val = GenerateFromRVal (rValIsType.rValExp);
+
+            /*
+             * Pass it in to top-level type expression decoder.
+             */
+            return GenerateFromTypeExp (val, rValIsType.typeExp);
+        }
+
+        /**
+         * @brief See if the type of the given value matches the type expression.
+         * @param val = where the value to be evaluated is stored
+         * @param typeExp = script tokens representing type expression
+         * @returns location where the boolean result is stored
+         */
+        private CompValu GenerateFromTypeExp (CompValu val, TokenTypeExp typeExp)
+        {
+            if (typeExp is TokenTypeExpBinOp) {
+                CompValu left  = GenerateFromTypeExp (val, ((TokenTypeExpBinOp)typeExp).leftOp);
+                CompValu right = GenerateFromTypeExp (val, ((TokenTypeExpBinOp)typeExp).rightOp);
+                CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
+                Token op = ((TokenTypeExpBinOp)typeExp).binOp;
+                left.PushVal (this, ((TokenTypeExpBinOp)typeExp).leftOp);
+                right.PushVal (this, ((TokenTypeExpBinOp)typeExp).rightOp);
+                if (op is TokenKwAnd) {
+                    ilGen.Emit (typeExp, OpCodes.And);
+                } else if (op is TokenKwOr) {
+                    ilGen.Emit (typeExp, OpCodes.Or);
+                } else {
+                    throw new Exception ("unknown TokenTypeExpBinOp " + op.GetType ());
+                }
+                result.Pop (this, typeExp);
+                return result;
+            }
+            if (typeExp is TokenTypeExpNot) {
+                CompValu interm = GenerateFromTypeExp (val, ((TokenTypeExpNot)typeExp).typeExp);
+                CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
+                interm.PushVal (this, ((TokenTypeExpNot)typeExp).typeExp, tokenTypeBool);
+                ilGen.Emit (typeExp, OpCodes.Ldc_I4_1);
+                ilGen.Emit (typeExp, OpCodes.Xor);
+                result.Pop (this, typeExp);
+                return result;
+            }
+            if (typeExp is TokenTypeExpPar) {
+                return GenerateFromTypeExp (val, ((TokenTypeExpPar)typeExp).typeExp);
+            }
+            if (typeExp is TokenTypeExpType) {
+                CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
+                val.PushVal (this, typeExp);
+                ilGen.Emit (typeExp, OpCodes.Isinst, ((TokenTypeExpType)typeExp).typeToken.ToSysType ());
+                ilGen.Emit (typeExp, OpCodes.Ldnull);
+                ilGen.Emit (typeExp, OpCodes.Ceq);
+                ilGen.Emit (typeExp, OpCodes.Ldc_I4_1);
+                ilGen.Emit (typeExp, OpCodes.Xor);
+                result.Pop (this, typeExp);
+                return result;
+            }
+            if (typeExp is TokenTypeExpUndef) {
+                CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
+                val.PushVal (this, typeExp);
+                ilGen.Emit (typeExp, OpCodes.Ldnull);
+                ilGen.Emit (typeExp, OpCodes.Ceq);
+                result.Pop (this, typeExp);
+                return result;
+            }
+            throw new Exception ("unknown TokenTypeExp type " + typeExp.GetType ());
+        }
+
+        /**
+         * @brief Push the default (null) value for a particular variable
+         * @param var = variable to get the default value for
+         * @returns with value pushed on stack
+         */
+        public void PushVarDefaultValue (TokenDeclVar var)
+        {
+            PushDefaultValue (var.type);
+        }
+        public void PushDefaultValue (TokenType type)
+        {
+            if (type is TokenTypeArray) {
+                PushXMRInst ();                // instance
+                ilGen.Emit (type, OpCodes.Newobj, xmrArrayConstructorInfo);
+                return;
+            }
+            if (type is TokenTypeChar) {
+                ilGen.Emit (type, OpCodes.Ldc_I4_0);
+                return;
+            }
+            if (type is TokenTypeList) {
+                ilGen.Emit (type, OpCodes.Ldc_I4_0);
+                ilGen.Emit (type, OpCodes.Newarr, typeof (object));
+                ilGen.Emit (type, OpCodes.Newobj, lslListConstructorInfo);
+                return;
+            }
+            if (type is TokenTypeRot) {
+                // Mono is tOO stOOpid to allow: ilGen.Emit (OpCodes.Ldsfld, zeroRotationFieldInfo);
+                ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.x);
+                ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.y);
+                ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.z);
+                ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.s);
+                ilGen.Emit (type, OpCodes.Newobj, lslRotationConstructorInfo);
+                return;
+            }
+            if ((type is TokenTypeKey) || (type is TokenTypeStr)) {
+                ilGen.Emit (type, OpCodes.Ldstr, "");
+                return;
+            }
+            if (type is TokenTypeVec) {
+                // Mono is tOO stOOpid to allow: ilGen.Emit (OpCodes.Ldsfld, zeroVectorFieldInfo);
+                ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_VECTOR.x);
+                ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_VECTOR.y);
+                ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_VECTOR.z);
+                ilGen.Emit (type, OpCodes.Newobj, lslVectorConstructorInfo);
+                return;
+            }
+            if (type is TokenTypeInt) {
+                ilGen.Emit (type, OpCodes.Ldc_I4_0);
+                return;
+            }
+            if (type is TokenTypeFloat) {
+                ilGen.Emit (type, OpCodes.Ldc_R4, 0.0f);
+                return;
+            }
+
+            /*
+             * Default for 'object' type is 'undef'.
+             * Likewise for script-defined classes and interfaces.
+             */
+            if ((type is TokenTypeObject) || (type is TokenTypeSDTypeClass) || (type is TokenTypeSDTypeInterface) || (type is TokenTypeExc)) {
+                ilGen.Emit (type, OpCodes.Ldnull);
+                return;
+            }
+
+            /*
+             * Void is pushed as the default return value of a void function.
+             * So just push nothing as expected of void functions.
+             */
+            if (type is TokenTypeVoid) {
+                return;
+            }
+
+            /*
+             * Default for 'delegate' type is 'undef'.
+             */
+            if (type is TokenTypeSDTypeDelegate) {
+                ilGen.Emit (type, OpCodes.Ldnull);
+                return;
+            }
+
+            throw new Exception ("unknown type " + type.GetType ().ToString ());
+        }
+
+        /**
+         * @brief Determine if the expression has a constant boolean value
+         *        and if so, if the value is true or false.
+         * @param expr = expression to evaluate
+         * @returns true: expression is contant and has boolean value true
+         *         false: otherwise
+         */
+        private bool IsConstBoolExprTrue (CompValu expr)
+        {
+            bool constVal;
+            return IsConstBoolExpr (expr, out constVal) && constVal;
+        }
+
+        private bool IsConstBoolExpr (CompValu expr, out bool constVal)
+        {
+            if (expr is CompValuChar) {
+                constVal = ((CompValuChar)expr).x != 0;
+                return true;
+            }
+            if (expr is CompValuFloat) {
+                constVal = ((CompValuFloat)expr).x != (double)0;
+                return true;
+            }
+            if (expr is CompValuInteger) {
+                constVal = ((CompValuInteger)expr).x != 0;
+                return true;
+            }
+            if (expr is CompValuString) {
+                string s = ((CompValuString)expr).x;
+                constVal = s != "";
+                if (constVal && (expr.type is TokenTypeKey)) {
+                    constVal = s != ScriptBaseClass.NULL_KEY;
+                }
+                return true;
+            }
+
+            constVal = false;
+            return false;
+        }
+
+        /**
+         * @brief Determine if the expression has a constant integer value
+         *        and if so, return the integer value.
+         * @param expr = expression to evaluate
+         * @returns true: expression is contant and has integer value
+         *         false: otherwise
+         */
+        private bool IsConstIntExpr (CompValu expr, out int constVal)
+        {
+            if (expr is CompValuChar) {
+                constVal = (int)((CompValuChar)expr).x;
+                return true;
+            }
+            if (expr is CompValuInteger) {
+                constVal = ((CompValuInteger)expr).x;
+                return true;
+            }
+
+            constVal = 0;
+            return false;
+        }
+
+        /**
+         * @brief Determine if the expression has a constant string value
+         *        and if so, return the string value.
+         * @param expr = expression to evaluate
+         * @returns true: expression is contant and has string value
+         *         false: otherwise
+         */
+        private bool IsConstStrExpr (CompValu expr, out string constVal)
+        {
+            if (expr is CompValuString) {
+                constVal = ((CompValuString)expr).x;
+                return true;
+            }
+            constVal = "";
+            return false;
+        }
+
+        /**
+         * @brief create table of legal event handler prototypes.
+         *        This is used to make sure script's event handler declrations are valid.
+         */
+        private static VarDict CreateLegalEventHandlers ()
+        {
+            /*
+             * Get handler prototypes with full argument lists.
+             */
+            VarDict leh = new InternalFuncDict (typeof (IEventHandlers), false);
+
+            /*
+             * We want the scripts to be able to declare their handlers with
+             * fewer arguments than the full argument lists.  So define additional 
+             * prototypes with fewer arguments.
+             */
+            TokenDeclVar[] fullArgProtos = new TokenDeclVar[leh.Count];
+            int i = 0;
+            foreach (TokenDeclVar fap in leh) fullArgProtos[i++] = fap;
+
+            foreach (TokenDeclVar fap in fullArgProtos) {
+                TokenArgDecl fal = fap.argDecl;
+                int fullArgCount = fal.vars.Length;
+                for (i = 0; i < fullArgCount; i ++) {
+                    TokenArgDecl shortArgList = new TokenArgDecl (null);
+                    for (int j = 0; j < i; j ++) {
+                        TokenDeclVar var = fal.vars[j];
+                        shortArgList.AddArg (var.type, var.name);
+                    }
+                    TokenDeclVar shortArgProto = new TokenDeclVar (null, null, null);
+                    shortArgProto.name         = new TokenName (null, fap.GetSimpleName ());
+                    shortArgProto.retType      = fap.retType;
+                    shortArgProto.argDecl      = shortArgList;
+                    leh.AddEntry (shortArgProto);
+                }
+            }
+
+            return leh;
+        }
+
+        /**
+         * @brief Emit a call to CheckRun(), (voluntary multitasking switch)
+         */
+        public void EmitCallCheckRun (Token errorAt, bool stack)
+        {
+            if (curDeclFunc.IsFuncTrivial (this)) throw new Exception (curDeclFunc.fullName + " is supposed to be trivial");
+            new CallLabel (this, errorAt);                               // jump here when stack restored
+            PushXMRInst ();                                              // instance
+            ilGen.Emit (errorAt, OpCodes.Call, stack ? checkRunStackMethInfo : checkRunQuickMethInfo);
+            openCallLabel = null;
+        }
+
+        /**
+         * @brief Emit code to push a callNo var on the stack.
+         */
+        public void GetCallNo (Token errorAt, ScriptMyLocal callNoVar)
+        {
+            ilGen.Emit (errorAt, OpCodes.Ldloc, callNoVar);
+            //ilGen.Emit (errorAt, OpCodes.Ldloca, callNoVar);
+            //ilGen.Emit (errorAt, OpCodes.Volatile);
+            //ilGen.Emit (errorAt, OpCodes.Ldind_I4);
+        }
+        public void GetCallNo (Token errorAt, CompValu callNoVar)
+        {
+            callNoVar.PushVal (this, errorAt);
+            //callNoVar.PushRef (this, errorAt);
+            //ilGen.Emit (errorAt, OpCodes.Volatile);
+            //ilGen.Emit (errorAt, OpCodes.Ldind_I4);
+        }
+
+        /**
+         * @brief Emit code to set a callNo var to a given constant.
+         */
+        public void SetCallNo (Token errorAt, ScriptMyLocal callNoVar, int val)
+        {
+            ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
+            ilGen.Emit (errorAt, OpCodes.Stloc, callNoVar);
+            //ilGen.Emit (errorAt, OpCodes.Ldloca, callNoVar);
+            //ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
+            //ilGen.Emit (errorAt, OpCodes.Volatile);
+            //ilGen.Emit (errorAt, OpCodes.Stind_I4);
+        }
+        public void SetCallNo (Token errorAt, CompValu callNoVar, int val)
+        {
+            callNoVar.PopPre (this, errorAt);
+            ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
+            callNoVar.PopPost (this, errorAt);
+            //callNoVar.PushRef (this, errorAt);
+            //ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
+            //ilGen.Emit (errorAt, OpCodes.Volatile);
+            //ilGen.Emit (errorAt, OpCodes.Stind_I4);
+        }
+
+        /**
+         * @brief handle a unary operator, such as -x.
+         */
+        private CompValu UnOpGenerate (CompValu inRVal, Token opcode)
+        {
+            /*
+             * - Negate
+             */
+            if (opcode is TokenKwSub) {
+                if (inRVal.type is TokenTypeFloat) {
+                    CompValuTemp outRVal = new CompValuTemp (new TokenTypeFloat (opcode), this);
+                    inRVal.PushVal (this, opcode, outRVal.type);  // push value to negate, make sure not LSL-boxed
+                    ilGen.Emit (opcode, OpCodes.Neg);     // compute the negative
+                    outRVal.Pop (this, opcode);           // pop into result
+                    return outRVal;                       // tell caller where we put it
+                }
+                if (inRVal.type is TokenTypeInt) {
+                    CompValuTemp outRVal = new CompValuTemp (new TokenTypeInt (opcode), this);
+                    inRVal.PushVal (this, opcode, outRVal.type);  // push value to negate, make sure not LSL-boxed
+                    ilGen.Emit (opcode, OpCodes.Neg);     // compute the negative
+                    outRVal.Pop (this, opcode);           // pop into result
+                    return outRVal;                       // tell caller where we put it
+                }
+                if (inRVal.type is TokenTypeRot) {
+                    CompValuTemp outRVal = new CompValuTemp (inRVal.type, this);
+                    inRVal.PushVal (this, opcode);        // push rotation, then call negate routine
+                    ilGen.Emit (opcode, OpCodes.Call, lslRotationNegateMethodInfo);
+                    outRVal.Pop (this, opcode);           // pop into result
+                    return outRVal;                       // tell caller where we put it
+                }
+                if (inRVal.type is TokenTypeVec) {
+                    CompValuTemp outRVal = new CompValuTemp (inRVal.type, this);
+                    inRVal.PushVal (this, opcode);        // push vector, then call negate routine
+                    ilGen.Emit (opcode, OpCodes.Call, lslVectorNegateMethodInfo);
+                    outRVal.Pop (this, opcode);           // pop into result
+                    return outRVal;                       // tell caller where we put it
+                }
+                ErrorMsg (opcode, "can't negate a " + inRVal.type.ToString ());
+                return inRVal;
+            }
+
+            /*
+             * ~ Complement (bitwise integer)
+             */
+            if (opcode is TokenKwTilde) {
+                if (inRVal.type is TokenTypeInt) {
+                    CompValuTemp outRVal = new CompValuTemp (new TokenTypeInt (opcode), this);
+                    inRVal.PushVal (this, opcode, outRVal.type);  // push value to negate, make sure not LSL-boxed
+                    ilGen.Emit (opcode, OpCodes.Not);     // compute the complement
+                    outRVal.Pop (this, opcode);           // pop into result
+                    return outRVal;                       // tell caller where we put it
+                }
+                ErrorMsg (opcode, "can't complement a " + inRVal.type.ToString ());
+                return inRVal;
+            }
+
+            /*
+             * ! Not (boolean)
+             *
+             * We stuff the 0/1 result in an int because I've seen x+!y in scripts
+             * and we don't want to have to create tables to handle int+bool and
+             * everything like that.
+             */
+            if (opcode is TokenKwExclam) {
+                CompValuTemp outRVal = new CompValuTemp (new TokenTypeInt (opcode), this);
+                inRVal.PushVal (this, opcode, tokenTypeBool);  // anything converts to boolean
+                ilGen.Emit (opcode, OpCodes.Ldc_I4_1);         // then XOR with 1 to flip it
+                ilGen.Emit (opcode, OpCodes.Xor);
+                outRVal.Pop (this, opcode);                    // pop into result
+                return outRVal;                                // tell caller where we put it
+            }
+
+            throw new Exception ("unhandled opcode " + opcode.ToString ());
+        }
+
+        /**
+         * @brief This is called while trying to compute the value of constant initializers.
+         *        It is passed a name and that name is looked up in the constant tables.
+         */
+        private TokenRVal LookupInitConstants (TokenRVal rVal, ref bool didOne)
+        {
+            /*
+             * If it is a static field of a script-defined type, look it up and hopefully we find a constant there.
+             */
+            TokenDeclVar gblVar;
+            if (rVal is TokenLValSField) {
+                TokenLValSField lvsf = (TokenLValSField)rVal;
+                if (lvsf.baseType is TokenTypeSDTypeClass) {
+                    TokenDeclSDTypeClass sdtClass = ((TokenTypeSDTypeClass)lvsf.baseType).decl;
+                    gblVar = sdtClass.members.FindExact (lvsf.fieldName.val, null);
+                    if (gblVar != null) {
+                        if (gblVar.constant && (gblVar.init is TokenRValConst)) {
+                            didOne = true;
+                            return gblVar.init;
+                        }
+                    }
+                }
+                return rVal;
+            }
+
+            /*
+             * Only other thing we handle is stand-alone names.
+             */
+            if (!(rVal is TokenLValName)) return rVal;
+            string name = ((TokenLValName)rVal).name.val;
+
+            /*
+             * If we are doing the initializations for a script-defined type,
+             * look for the constant among the fields for that type.
+             */
+            if (currentSDTClass != null) {
+                gblVar = currentSDTClass.members.FindExact (name, null);
+                if (gblVar != null) {
+                    if (gblVar.constant && (gblVar.init is TokenRValConst)) {
+                        didOne = true;
+                        return gblVar.init;
+                    }
+                    return rVal;
+                }
+            }
+
+            /*
+             * Look it up as a script-defined global variable.
+             * Then if the variable is defined as a constant and has a constant value,
+             * we are successful.  If it is defined as something else, return failure.
+             */
+            gblVar = tokenScript.variablesStack.FindExact (name, null);
+            if (gblVar != null) {
+                if (gblVar.constant && (gblVar.init is TokenRValConst)) {
+                    didOne = true;
+                    return gblVar.init;
+                }
+                return rVal;
+            }
+
+            /*
+             * Maybe it is a built-in symbolic constant.
+             */
+            ScriptConst scriptConst = ScriptConst.Lookup (name);
+            if (scriptConst != null) {
+                rVal = CompValuConst2RValConst (scriptConst.rVal, rVal);
+                if (rVal is TokenRValConst) {
+                    didOne = true;
+                    return rVal;
+                }
+            }
+
+            /*
+             * Don't know what it is, return failure.
+             */
+            return rVal;
+        }
+
+        /**
+         * @brief This is called while trying to compute the value of constant expressions.
+         *        It is passed a name and that name is looked up in the constant tables.
+         */
+        private TokenRVal LookupBodyConstants (TokenRVal rVal, ref bool didOne)
+        {
+            /*
+             * If it is a static field of a script-defined type, look it up and hopefully we find a constant there.
+             */
+            TokenDeclVar gblVar;
+            if (rVal is TokenLValSField) {
+                TokenLValSField lvsf = (TokenLValSField)rVal;
+                if (lvsf.baseType is TokenTypeSDTypeClass) {
+                    TokenDeclSDTypeClass sdtClass = ((TokenTypeSDTypeClass)lvsf.baseType).decl;
+                    gblVar = sdtClass.members.FindExact (lvsf.fieldName.val, null);
+                    if ((gblVar != null) && gblVar.constant && (gblVar.init is TokenRValConst)) {
+                        didOne = true;
+                        return gblVar.init;
+                    }
+                }
+                return rVal;
+            }
+
+            /*
+             * Only other thing we handle is stand-alone names.
+             */
+            if (!(rVal is TokenLValName)) return rVal;
+            string name = ((TokenLValName)rVal).name.val;
+
+            /*
+             * Scan through the variable stack and hopefully we find a constant there.
+             * But we stop as soon as we get a match because that's what the script is referring to.
+             */
+            CompValu val;
+            for (VarDict vars = ((TokenLValName)rVal).stack; vars != null; vars = vars.outerVarDict) {
+                TokenDeclVar var = vars.FindExact (name, null);
+                if (var != null) {
+                    val = var.location;
+                    goto foundit;
+                }
+
+                TokenDeclSDTypeClass baseClass = vars.thisClass;
+                if (baseClass != null) {
+                    while ((baseClass = baseClass.extends) != null) {
+                        var = baseClass.members.FindExact (name, null);
+                        if (var != null) {
+                            val = var.location;
+                            goto foundit;
+                        }
+                    }
+                }
+            }
+
+            /*
+             * Maybe it is a built-in symbolic constant.
+             */
+            ScriptConst scriptConst = ScriptConst.Lookup (name);
+            if (scriptConst != null) {
+                val = scriptConst.rVal;
+                goto foundit;
+            }
+
+            /*
+             * Don't know what it is, return failure.
+             */
+            return rVal;
+
+            /*
+             * Found a CompValu.  If it's a simple constant, then use it.
+             * Otherwise tell caller we failed to simplify.
+             */
+        foundit:
+            rVal = CompValuConst2RValConst (val, rVal);
+            if (rVal is TokenRValConst) {
+                didOne = true;
+            }
+            return rVal;
+        }
+
+        private static TokenRVal CompValuConst2RValConst (CompValu val, TokenRVal rVal)
+        {
+            if (val is CompValuChar)    rVal = new TokenRValConst (rVal, ((CompValuChar)val).x);
+            if (val is CompValuFloat)   rVal = new TokenRValConst (rVal, ((CompValuFloat)val).x);
+            if (val is CompValuInteger) rVal = new TokenRValConst (rVal, ((CompValuInteger)val).x);
+            if (val is CompValuString)  rVal = new TokenRValConst (rVal, ((CompValuString)val).x);
+            return rVal;
+        }
+
+        /**
+         * @brief Generate code to push XMRInstanceSuperType pointer on stack.
+         */
+        public void PushXMRInst ()
+        {
+            if (instancePointer == null) {
+                ilGen.Emit (null, OpCodes.Ldarg_0);
+            } else {
+                ilGen.Emit (null, OpCodes.Ldloc, instancePointer);
+            }
+        }
+
+        /**
+         * @returns true: Ldarg_0 gives XMRSDTypeClObj pointer
+         *                - this is the case for instance methods
+         *         false: Ldarg_0 gives XMR_Instance pointer
+         *                - this is the case for both global functions and static methods
+         */
+        public bool IsSDTInstMethod ()
+        {
+            return (curDeclFunc.sdtClass != null) && 
+                   ((curDeclFunc.sdtFlags & ScriptReduce.SDT_STATIC) == 0);
+        }
+
+        /**
+         * @brief Look for a simply named function or variable (not a field or method)
+         */
+        public TokenDeclVar FindNamedVar (TokenLValName lValName, TokenType[] argsig)
+        {
+            /*
+             * Look in variable stack for the given name.
+             */
+            for (VarDict vars = lValName.stack; vars != null; vars = vars.outerVarDict) {
+
+                // first look for it possibly with an argument signature
+                // so we pick the correct overloaded method
+                TokenDeclVar var = FindSingleMember (vars, lValName.name, argsig);
+                if (var != null) return var;
+
+                // if that fails, try it without the argument signature.
+                // delegates get entered like any other variable, ie, 
+                // no signature on their name.
+                if (argsig != null) {
+                    var = FindSingleMember (vars, lValName.name, null);
+                    if (var != null) return var;
+                }
+
+                // if this is the frame for some class members, try searching base class members too
+                TokenDeclSDTypeClass baseClass = vars.thisClass;
+                if (baseClass != null) {
+                    while ((baseClass = baseClass.extends) != null) {
+                        var = FindSingleMember (baseClass.members, lValName.name, argsig);
+                        if (var != null) return var;
+                        if (argsig != null) {
+                            var = FindSingleMember (baseClass.members, lValName.name, null);
+                            if (var != null) return var;
+                        }
+                    }
+                }
+            }
+
+            /*
+             * If not found, try one of the built-in constants or functions.
+             */
+            if (argsig == null) {
+                ScriptConst scriptConst = ScriptConst.Lookup (lValName.name.val);
+                if (scriptConst != null) {
+                    TokenDeclVar var = new TokenDeclVar (lValName.name, null, tokenScript);
+                    var.name     = lValName.name;
+                    var.type     = scriptConst.rVal.type;
+                    var.location = scriptConst.rVal;
+                    return var;
+                }
+            } else {
+                TokenDeclVar inline = FindSingleMember (TokenDeclInline.inlineFunctions, lValName.name, argsig);
+                if (inline != null) return inline;
+            }
+
+            return null;
+        }
+
+
+        /**
+         * @brief Find a member of an interface.
+         * @param sdType = interface type
+         * @param name = name of member to find
+         * @param argsig = null: field/property; else: script-visible method argument types
+         * @param baseRVal = pointer to interface object
+         * @returns null: no such member
+         *          else: pointer to member
+         *                baseRVal = possibly modified to point to type-casted interface object
+         */
+        private TokenDeclVar FindInterfaceMember (TokenTypeSDTypeInterface sdtType, TokenName name, TokenType[] argsig, ref CompValu baseRVal)
+        {
+            TokenDeclSDTypeInterface sdtDecl = sdtType.decl;
+            TokenDeclSDTypeInterface impl;
+            TokenDeclVar declVar = sdtDecl.FindIFaceMember (this, name, argsig, out impl);
+            if ((declVar != null) && (impl != sdtDecl)) {
+
+                /*
+                 * Accessing a method or propterty of another interface that the primary interface says it implements.
+                 * In this case, we have to cast from the primary interface to that secondary interface.
+                 *
+                 * interface IEnumerable {
+                 *     IEnumerator GetEnumerator ();
+                 * }
+                 * interface ICountable : IEnumerable {
+                 *     integer GetCount ();
+                 * }
+                 * class List : ICountable {
+                 *     public GetCount () : ICountable { ... }
+                 *     public GetEnumerator () : IEnumerable { ... }
+                 * }
+                 *
+                 *     ICountable aList = new List ();
+                 *     IEnumerator anEnumer = aList.GetEnumerator ();   << we are here
+                 *                                                      << baseRVal = aList
+                 *                                                      << sdtDecl = ICountable
+                 *                                                      << impl = IEnumerable
+                 *                                                      << name = GetEnumerator
+                 *                                                      << argsig = ()
+                 * So we have to cast aList from ICountable to IEnumerable.
+                 */
+
+                // make type token for the secondary interface type
+                TokenType subIntfType = impl.MakeRefToken (name);
+
+                // make a temp variable of the secondary interface type
+                CompValuTemp castBase = new CompValuTemp (subIntfType, this);
+
+                // output code to cast from the primary interface to the secondary interface
+                // this is 2 basic steps:
+                // 1) cast from primary interface object -> class object
+                //    ...gets it from interfaceObject.delegateArray[0].Target
+                // 2) cast from class object -> secondary interface object
+                //    ...gets it from classObject.sdtcITable[interfaceIndex]
+                baseRVal.PushVal (this, name, subIntfType);
+
+                // save result of casting in temp
+                castBase.Pop (this, name);
+
+                // return temp reference
+                baseRVal = castBase;
+            }
+
+            return declVar;
+        }
+
+        /**
+         * @brief Find a member of a script-defined type class.
+         * @param sdtType = reference to class declaration
+         * @param name = name of member to find
+         * @param argsig = argument signature used to select among overloaded members
+         * @returns null: no such member found
+         *          else: the member found
+         */
+        public TokenDeclVar FindThisMember (TokenTypeSDTypeClass sdtType, TokenName name, TokenType[] argsig)
+        {
+            return FindThisMember (sdtType.decl, name, argsig);
+        }
+        public TokenDeclVar FindThisMember (TokenDeclSDTypeClass sdtDecl, TokenName name, TokenType[] argsig)
+        {
+            for (TokenDeclSDTypeClass sdtd = sdtDecl; sdtd != null; sdtd = sdtd.extends) {
+                TokenDeclVar declVar = FindSingleMember (sdtd.members, name, argsig);
+                if (declVar != null) return declVar;
+            }
+            return null;
+        }
+
+        /**
+         * @brief Look for a single member that matches the given name and argument signature
+         * @param where = which dictionary to look in
+         * @param name = basic name of the field or method, eg, "Printable"
+         * @param argsig = argument types the method is being called with, eg, "(string)"
+         *                 or null to find a field
+         * @returns null: no member found
+         *          else: the member found
+         */
+        public TokenDeclVar FindSingleMember (VarDict where, TokenName name, TokenType[] argsig)
+        {
+            TokenDeclVar[] members = where.FindCallables (name.val, argsig);
+            if (members == null) return null;
+            if (members.Length > 1) {
+                ErrorMsg (name, "more than one matching member");
+                for (int i = 0; i < members.Length; i ++) {
+                    ErrorMsg (members[i], "  " + members[i].argDecl.GetArgSig ());
+                }
+            }
+            return members[0];
+        }
+
+        /**
+         * @brief Find an exact function name and argument signature match.
+         *        Also verify that the return value type is an exact match.
+         * @param where = which method dictionary to look in
+         * @param name = basic name of the method, eg, "Printable"
+         * @param ret = expected return value type
+         * @param argsig = argument types the method is being called with, eg, "(string)"
+         * @returns null: no exact match found
+         *          else: the matching function
+         */
+        private TokenDeclVar FindExactWithRet (VarDict where, TokenName name, TokenType ret, TokenType[] argsig)
+        {
+            TokenDeclVar func = where.FindExact (name.val, argsig);
+            if ((func != null) && (func.retType.ToString () != ret.ToString ())) {
+                ErrorMsg (name, "return type mismatch, have " + func.retType.ToString () + ", expect " + ret.ToString ());
+            }
+            if (func != null) CheckAccess (func, name);
+            return func;
+        }
+
+        /**
+         * @brief Check the private/protected/public access flags of a member.
+         */
+        private void CheckAccess (TokenDeclVar var, Token errorAt)
+        {
+            TokenDeclSDType nested;
+            TokenDeclSDType definedBy  = var.sdtClass;
+            TokenDeclSDType accessedBy = curDeclFunc.sdtClass;
+
+            /*******************************\
+             *  Check member-level access  *
+            \*******************************/
+
+            /*
+             * Note that if accessedBy is null, ie, accessing from global function (or event handlers),
+             * anything tagged as SDT_PRIVATE or SDT_PROTECTED will fail.
+             */
+
+            /*
+             * Private means accessed by the class that defined the member or accessed by a nested class
+             * of the class that defined the member.
+             */
+            if ((var.sdtFlags & ScriptReduce.SDT_PRIVATE) != 0) {
+                for (nested = accessedBy; nested != null; nested = nested.outerSDType) {
+                    if (nested == definedBy) goto acc1ok;
+                }
+                ErrorMsg (errorAt, "private member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName);
+                return;
+            }
+
+            /*
+             * Protected means:
+             *   If being accessed by an inner class, the inner class has access to it if the inner class derives 
+             *   from the declaring class.  It also has access to it if an outer class derives from the declaring 
+             *   class.
+             */
+            if ((var.sdtFlags & ScriptReduce.SDT_PROTECTED) != 0) {
+                for (nested = accessedBy; nested != null; nested = nested.outerSDType) {
+                    for (TokenDeclSDType rootward = nested; rootward != null; rootward = rootward.extends) {
+                        if (rootward == definedBy) goto acc1ok;
+                    }
+                }
+                ErrorMsg (errorAt, "protected member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName);
+                return;
+            }
+        acc1ok:
+
+            /******************************\
+             *  Check class-level access  *
+            \******************************/
+
+            /*
+             * If being accessed by same or inner class than where defined, it is ok.
+             *
+             *      class DefiningClass {
+             *          varBeingAccessed;
+             *                         .
+             *                         .
+             *                         .
+             *                  class AccessingClass {
+             *                      functionDoingAccess() { }
+             *                  }
+             *                         .
+             *                         .
+             *                         .
+             *      }
+             */
+            nested = accessedBy;
+            while (true) {
+                if (nested == definedBy) return;
+                if (nested == null) break;
+                nested = (TokenDeclSDTypeClass)nested.outerSDType;
+            }
+
+            /*
+             * It is being accessed by an outer class than where defined, 
+             * check for a 'private' or 'protected' class tag that blocks.
+             */
+            do {
+
+                /*
+                 * If the field's class is defined directly inside the accessing class,
+                 * access is allowed regardless of class-level private or protected tags.
+                 *
+                 *      class AccessingClass {
+                 *          functionDoingAccess() { }
+                 *          class DefiningClass {
+                 *              varBeingAccessed;
+                 *          }
+                 *      }
+                 */
+                if (definedBy.outerSDType == accessedBy) return;
+
+                /*
+                 * If the field's class is defined two or more levels inside the accessing class, 
+                 * access is denied if the defining class is tagged private.
+                 *
+                 *      class AccessingClass {
+                 *          functionDoingAccess() { }
+                 *                         .
+                 *                         .
+                 *                         .
+                 *                  class IntermediateClass {
+                 *                      private class DefiningClass {
+                 *                          varBeingAccessed;
+                 *                      }
+                 *                  }
+                 *                         .
+                 *                         .
+                 *                         .
+                 *      }
+                 */
+                if ((definedBy.accessLevel & ScriptReduce.SDT_PRIVATE) != 0) {
+                    ErrorMsg (errorAt, "member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName + 
+                                       " because of private class " + definedBy.longName.val);
+                    return;
+                }
+
+                /*
+                 * Likewise, if DefiningClass is tagged protected, the AccessingClass must derive from the
+                 * IntermediateClass or access is denied.
+                 */
+                if ((definedBy.accessLevel & ScriptReduce.SDT_PROTECTED) != 0) {
+                    for (TokenDeclSDType extends = accessedBy; extends != definedBy.outerSDType; extends = extends.extends) {
+                        if (extends == null) {
+                            ErrorMsg (errorAt, "member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName + 
+                                               " because of protected class " + definedBy.longName.val);
+                            return;
+                        }
+                    }
+                }
+
+                /*
+                 * Check next outer level.
+                 */
+                definedBy = definedBy.outerSDType;
+            } while (definedBy != null);
+        }
+
+        /**
+         * @brief Convert a list of argument types to printable string, eg, "(list,string,float,integer)"
+         *        If given a null, return "" indicating it is a field not a method
+         */
+        public static string ArgSigString (TokenType[] argsig)
+        {
+            if (argsig == null) return "";
+            StringBuilder sb = new StringBuilder ("(");
+            for (int i = 0; i < argsig.Length; i ++) {
+                if (i > 0) sb.Append (",");
+                sb.Append (argsig[i].ToString ());
+            }
+            sb.Append (")");
+            return sb.ToString ();
+        }
+
+        /**
+         * @brief output error message and remember that we did
+         */
+        public void ErrorMsg (Token token, string message)
+        {
+            if ((token == null) || (token.emsg == null)) token = errorMessageToken;
+            if (!youveAnError || (token.file != lastErrorFile) || (token.line > lastErrorLine)) {
+                token.ErrorMsg (message);
+                youveAnError  = true;
+                lastErrorFile = token.file;
+                lastErrorLine = token.line;
+            }
+        }
+
+        /**
+         * @brief Find a private static method.
+         * @param owner = class the method is part of
+         * @param name = name of method to find
+         * @param args = array of argument types
+         * @returns pointer to method
+         */
+        public static MethodInfo GetStaticMethod (Type owner, string name, Type[] args)
+        {
+            MethodInfo mi = owner.GetMethod (name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, args, null);
+            if (mi == null) {
+                throw new Exception ("undefined method " + owner.ToString () + "." + name);
+            }
+            return mi;
+        }
+
+        // http://wiki.secondlife.com/wiki/Rotation 'negate a rotation' says just negate .s component
+        // but http://wiki.secondlife.com/wiki/LSL_Language_Test (lslangtest1.lsl) says negate all 4 values
+        public static LSL_Rotation LSLRotationNegate (LSL_Rotation r) { return new LSL_Rotation (-r.x,-r.y,-r.z,-r.s); }
+        public static LSL_Vector   LSLVectorNegate   (LSL_Vector v)   { return -v; }
+        public static string       CatchExcToStr     (Exception exc)  { return exc.ToString(); }
+        //public static void       ConsoleWrite      (string str)     { Console.Write(str); }
+
+        /**
+         * @brief Defines an internal label that is used as a target for 'break' and 'continue' statements.
+         */
+        private class BreakContTarg {
+            public bool used;
+            public ScriptMyLabel label;
+            public TokenStmtBlock block;
+
+            public BreakContTarg (ScriptCodeGen scg, string name) {
+                used  = false;                         // assume it isn't referenced at all
+                label = scg.ilGen.DefineLabel (name);  // label that the break/continue jumps to
+                block = scg.curStmtBlock;              // { ... } that the break/continue label is in
+            }
+        }
+    }
+
+    /**
+     * @brief Marker interface indicates an exception that can't be caught by a script-level try/catch.
+     */
+    public interface IXMRUncatchable { }
+
+    /**
+     * @brief Thrown by a script when it attempts to change to an undefined state.
+     * These can be detected at compile time but the moron XEngine compiles
+     * such things, so we compile them as runtime errors.
+     */
+    [SerializableAttribute]
+    public class ScriptUndefinedStateException : Exception, ISerializable {
+        public string stateName;
+        public ScriptUndefinedStateException (string stateName) : base ("undefined state " + stateName) {
+            this.stateName = stateName;
+        }
+        protected ScriptUndefinedStateException (SerializationInfo info, StreamingContext context) : base (info, context)
+        { }
+    }
+
+    /**
+     * @brief Created by a throw statement.
+     */
+    [SerializableAttribute]
+    public class ScriptThrownException : Exception, ISerializable {
+        public object thrown;
+
+        /**
+         * @brief Called by a throw statement to wrap the object in a unique
+         *        tag that capable of capturing a stack trace.  Script can 
+         *        unwrap it by calling xmrExceptionThrownValue().
+         */
+        public static Exception Wrap (object thrown)
+        {
+            return new ScriptThrownException (thrown);
+        }
+        private ScriptThrownException (object thrown) : base (thrown.ToString ())
+        {
+            this.thrown = thrown;
+        }
+
+        /**
+         * @brief Used by serialization/deserialization.
+         */
+        protected ScriptThrownException (SerializationInfo info, StreamingContext context) : base (info, context)
+        { }
+    }
+
+    /**
+     * @brief Thrown by a script when it attempts to change to a defined state.
+     */
+    [SerializableAttribute]
+    public class ScriptChangeStateException : Exception, ISerializable, IXMRUncatchable {
+        public int newState;
+        public ScriptChangeStateException (int newState) {
+            this.newState = newState;
+        }
+        protected ScriptChangeStateException (SerializationInfo info, StreamingContext context) : base (info, context)
+        { }
+    }
+
+    /**
+     * @brief We are restoring to the body of a catch { } so we need to 
+     *        wrap the original exception in an outer exception, so the 
+     *        system won't try to refill the stack trace.
+     *
+     *        We don't mark this one serializable as it should never get 
+     *        serialized out.  It only lives from the throw to the very 
+     *        beginning of the catch handler where it is promptly unwrapped.
+     *        No CheckRun() call can possibly intervene.
+     */
+    public class ScriptRestoreCatchException : Exception {
+
+        // old code uses these
+        private object e;
+        public ScriptRestoreCatchException (object e) {
+            this.e = e;
+        }
+        public static object Unwrap (object o)
+        {
+            if (o is IXMRUncatchable) return null;
+            if (o is ScriptRestoreCatchException) return ((ScriptRestoreCatchException)o).e;
+            return o;
+        }
+
+        // new code uses these
+        private Exception ee;
+        public ScriptRestoreCatchException (Exception ee) {
+            this.ee = ee;
+        }
+        public static Exception Unwrap (Exception oo)
+        {
+            if (oo is IXMRUncatchable) return null;
+            if (oo is ScriptRestoreCatchException) return ((ScriptRestoreCatchException)oo).ee;
+            return oo;
+        }
+    }
+
+    [SerializableAttribute]
+    public class ScriptBadCallNoException : Exception {
+        public ScriptBadCallNoException (int callNo) : base ("bad callNo " + callNo) { }
+        protected ScriptBadCallNoException (SerializationInfo info, StreamingContext context) : base (info, context)
+        { }
+    }
+
+    public class CVVMismatchException : Exception {
+        public int oldcvv;
+        public int newcvv;
+
+        public CVVMismatchException (int oldcvv, int newcvv) : base ("object version is " + oldcvv.ToString () + 
+                                                             " but accept only " + newcvv.ToString ())
+        {
+            this.oldcvv = oldcvv;
+            this.newcvv = newcvv;
+        }
+    }
+}

+ 2637 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs

@@ -0,0 +1,2637 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+
+
+/**
+ * @brief Wrapper class for ScriptMyILGen to do simple optimizations.
+ *        The main one is to figure out which locals are active at the labels
+ *        so the stack capture/restore code doesn't have to do everything.
+ *        Second is it removes unnecessary back-to-back stloc/ldloc's.
+ */
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    /**
+     * @brief This is a list that keeps track of types pushed on the evaluation stack.
+     */
+    public class StackDepth : List<Type> {
+        public List<bool> isBoxeds = new List<bool> ();
+
+        /**
+         * @brief Clear both stacks.
+         */
+        public new void Clear ()
+        {
+            base.Clear ();
+            isBoxeds.Clear ();
+        }
+
+        /**
+         * @brief Pop call parameters and validate the types.
+         */
+        public void Pop (ParameterInfo[] pis)
+        {
+            int n = pis.Length;
+            int c = this.Count;
+            if (n > c) throw new Exception ("stack going negative");
+            for (int i = n; -- i >= 0;) {
+                -- c;
+                ExpectedVsOnStack (pis[i].ParameterType, this[c], isBoxeds[c]);
+            }
+            Pop (n);
+        }
+
+        /**
+         * @brief Pop values and validate the types.
+         */
+        public void Pop (Type[] ts)
+        {
+            int n = ts.Length;
+            int c = this.Count;
+            if (n > c) throw new Exception ("stack going negative");
+            for (int i = ts.Length; -- i >= 0;) {
+                -- c;
+                ExpectedVsOnStack (ts[i], this[c], isBoxeds[c]);
+            }
+            Pop (n);
+        }
+
+        /**
+         * @brief Pop a single value and validate the type.
+         */
+        public void Pop (Type t)
+        {
+            int c = this.Count;
+            if (c < 1) throw new Exception ("stack going negative");
+            ExpectedVsOnStack (t, this[c-1], isBoxeds[c-1]);
+            Pop (1);
+        }
+
+        /**
+         * @brief Pop a single value and validate that it is a numeric type.
+         */
+        public Type PopNumVal ()
+        {
+            int c = this.Count;
+            if (c < 1) throw new Exception ("stack going negative");
+            Type st = this[--c];
+            if (st == null) {
+                throw new Exception ("stack has null, expecting a numeric");
+            }
+            if (isBoxeds[c]) {
+                throw new Exception ("stack is boxed " + st.Name + ", expecting a numeric");
+            }
+            if ((st != typeof (bool)) && (st != typeof (char))  && (st != typeof (int)) && 
+                (st != typeof (long)) && (st != typeof (float)) && (st != typeof (double))) {
+                throw new Exception ("stack has " + st.Name + ", expecting a numeric");
+            }
+            return Pop (1);
+        }
+
+        /**
+         * @brief Pop a single value and validate that it is a reference type
+         */
+        public Type PopRef ()
+        {
+            int c = this.Count;
+            if (c < 1) throw new Exception ("stack going negative");
+            Type st = this[--c];
+            if ((st != null) && !isBoxeds[c] && st.IsValueType) {
+                throw new Exception ("stack has " + st.Name + ", expecting a ref type");
+            }
+            return Pop (1);
+        }
+
+        /**
+         * @brief Pop a single value and validate that it is a value type
+         */
+        public Type PopValue ()
+        {
+            int c = this.Count;
+            if (c < 1) throw new Exception ("stack going negative");
+            Type st = this[--c];
+            if (st == null) {
+                throw new Exception ("stack has null, expecting a value type");
+            }
+            if (!st.IsValueType) {
+                throw new Exception ("stack has " + st.Name + ", expecting a value type");
+            }
+            if (isBoxeds[c]) {
+                throw new Exception ("stack has boxed " + st.Name + ", expecting an unboxed value type");
+            }
+            return Pop (1);
+        }
+
+        // ex = what is expected to be on stack
+        // st = what is actually on stack (null for ldnull)
+        // stBoxed = stack value is boxed
+        public static void ExpectedVsOnStack (Type ex, Type st, bool stBoxed)
+        {
+            // ldnull pushed on stack can go into any pointer type
+            if (st == null) {
+                if (ex.IsByRef || ex.IsPointer || ex.IsClass || ex.IsInterface) return;
+                throw new Exception ("stack has null, expect " + ex.Name);
+            }
+
+            // simple case of expecting an object
+            // ...so the stack can have object,string, etc
+            // but we cant allow int = boxed int here
+            if (ex.IsAssignableFrom (st) && !stBoxed) return;
+
+            // case of expecting an enum on the stack
+            // but all the CIL code knows about are ints etc
+            // so convert the Enum type to integer or whatever
+            // and that should be assignable from what's on stack
+            if (ex.IsEnum && typeof (int).IsAssignableFrom (st)) return;
+
+            // bool, char, int are interchangeable on the stack
+            if ((ex == typeof (bool) || ex == typeof (char) || ex == typeof (int)) && 
+                (st == typeof (bool) || st == typeof (char) || st == typeof (int))) return;
+
+            // float and double are interchangeable on the stack
+            if ((ex == typeof (float) || ex == typeof (double)) && 
+                (st == typeof (float) || st == typeof (double))) return;
+
+            // object can accept any boxed type
+            if ((ex == typeof (object)) && stBoxed) return;
+
+            // otherwise, it is disallowed
+            throw new Exception ("stack has " + StackTypeString (st, stBoxed) + ", expect " + ex.Name);
+        }
+
+        /**
+         * @brief Pop values without any validation.
+         */
+        public Type Pop (int n)
+        {
+            if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
+            Type lastPopped = null;
+            int c = this.Count;
+            if (n > c) throw new Exception ("stack going negative");
+            if (n > 0) {
+                lastPopped = this[c-n];
+                this.RemoveRange (c - n, n);
+                isBoxeds.RemoveRange (c - n, n);
+            }
+            if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
+            return lastPopped;
+        }
+
+        /**
+         * @brief Peek at the n'th stack value.
+         *        n = 0 : top of stack
+         *            1 : next to top
+         *                ...
+         */
+        public Type Peek (int n)
+        {
+            int c = this.Count;
+            if (n > c - 1) throw new Exception ("stack going negative");
+            if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
+            return this[c-n-1];
+        }
+        public bool PeekBoxed (int n)
+        {
+            int c = isBoxeds.Count;
+            if (n > c - 1) throw new Exception ("stack going negative");
+            if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
+            return isBoxeds[c-n-1];
+        }
+
+        /**
+         * @brief Push a single value of the given type.
+         */
+        public void Push (Type t)
+        {
+            Push (t, false);
+        }
+        public void Push (Type t, bool isBoxed)
+        {
+            if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
+            this.Add (t);
+            isBoxeds.Add (isBoxed);
+        }
+
+        /**
+         * @brief See if the types at a given label exactly match those on the stack.
+         *        We should have the stack types be the same no matter how we branched 
+         *        or fell through to a particular label.
+         */
+        public void Matches (ScriptMyLabel label)
+        {
+            Type[] ts       = label.stackDepth;
+            bool[] tsBoxeds = label.stackBoxeds;
+            int i;
+
+            if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
+
+            if (ts == null) {
+                label.stackDepth  = this.ToArray ();
+                label.stackBoxeds = isBoxeds.ToArray ();
+            } else if (ts.Length != this.Count) {
+                throw new Exception ("stack depth mismatch");
+            } else {
+                for (i = this.Count; -- i >= 0;) {
+                    if (tsBoxeds[i] != this.isBoxeds[i]) goto mismatch;
+                    if (ts[i] == this[i]) continue;
+                    if ((ts[i]   == typeof (bool) || ts[i]   == typeof (char) || ts[i]   == typeof (int)) && 
+                        (this[i] == typeof (bool) || this[i] == typeof (char) || this[i] == typeof (int))) continue;
+                    if ((ts[i]   == typeof (double) || ts[i]   == typeof (float)) && 
+                        (this[i] == typeof (double) || this[i] == typeof (float))) continue;
+                    goto mismatch;
+                }
+            }
+            return;
+        mismatch:
+            throw new Exception ("stack type mismatch: " + StackTypeString (ts[i], tsBoxeds[i]) + " vs " + StackTypeString (this[i], this.isBoxeds[i]));
+        }
+
+        private static string StackTypeString (Type ts, bool isBoxed)
+        {
+            if (!isBoxed) return ts.Name;
+            return "[" + ts.Name + "]";
+        }
+    }
+
+    /**
+     * @brief One of these per opcode and label in the function plus other misc markers.
+     *        They form the CIL instruction stream of the function.
+     */
+    public abstract class GraphNode {
+        private static readonly bool DEBUG = false;
+
+        public const int OPINDENT =  4;
+        public const int OPDEBLEN = 12;
+
+        public ScriptCollector coll;
+        public GraphNodeBeginExceptionBlock tryBlock;  // start of enclosing try block
+                                                       // valid in the try section
+                                                       // null in the catch/finally sections
+                                                       // null outside of try block
+                                                       // for the try node itself, links to outer try block
+        public GraphNodeBeginExceptionBlock excBlock;  // start of enclosing try block
+                                                       // valid in the try/catch/finally sections
+                                                       // null outside of try/catch/finally block
+                                                       // for the try node itself, links to outer try block
+
+        /*
+         * List of nodes in order as originally given.
+         */
+        public GraphNode nextLin, prevLin;
+        public int linSeqNo;
+
+        /**
+         * @brief Save pointer to collector.
+         */
+        public GraphNode (ScriptCollector coll)
+        {
+            this.coll = coll;
+        }
+
+        /**
+         * @brief Chain graph node to end of linear list.
+         */
+        public virtual void ChainLin ()
+        {
+            coll.lastLin.nextLin = this;
+            this.prevLin  = coll.lastLin;
+            coll.lastLin  = this;
+            this.tryBlock = coll.curTryBlock;
+            this.excBlock = coll.curExcBlock;
+
+            if (DEBUG) {
+                StringBuilder sb = new StringBuilder ("ChainLin*:");
+                sb.Append (coll.stackDepth.Count.ToString("D2"));
+                sb.Append (' ');
+                this.DebString (sb);
+                Console.WriteLine (sb.ToString ());
+            }
+        }
+
+        /**
+         * @brief Append full info to debugging string for printing out the instruction.
+         */
+        public void DebStringExt (StringBuilder sb)
+        {
+            int x = sb.Length;
+            sb.Append (this.linSeqNo.ToString ().PadLeft (5));
+            sb.Append (": ");
+            this.DebString (sb);
+
+            if (this.ReadsLocal  () != null) ScriptCollector.PadToLength (sb, x + 60, " [read]");
+            if (this.WritesLocal () != null) ScriptCollector.PadToLength (sb, x + 68, " [write]");
+            ScriptCollector.PadToLength (sb, x + 72, " ->");
+            bool first = true;
+            foreach (GraphNode nn in this.NextNodes) {
+                if (first) {
+                    sb.Append (nn.linSeqNo.ToString ().PadLeft (5));
+                    first = false;
+                } else {
+                    sb.Append (',');
+                    sb.Append (nn.linSeqNo);
+                }
+            }
+        }
+
+        /**
+         * @brief See if it's possible for it to fall through to the next inline (nextLin) instruction.
+         */
+        public virtual bool CanFallThrough ()
+        {
+            return true;
+        }
+
+        /**
+         * @brief Append to debugging string for printing out the instruction.
+         */
+        public abstract void DebString (StringBuilder sb);
+        public override string ToString ()
+        {
+            StringBuilder sb = new StringBuilder ();
+            this.DebString (sb);
+            return sb.ToString ();
+        }
+
+        /**
+         * @brief See if this instruction reads a local variable.
+         */
+        public virtual ScriptMyLocal ReadsLocal  () { return null; }
+
+        /**
+         * @brief See if this instruction writes a local variable.
+         */
+        public virtual ScriptMyLocal WritesLocal () { return null; }
+
+        /**
+         * @brief Write this instruction out to the wrapped object file.
+         */
+        public abstract void WriteOutOne (ScriptMyILGen ilGen);
+
+        /**
+         * @brief Iterate through all the possible next nodes, including the next inline node, if any.
+         *        The next inline code is excluded if the instruction never falls through, eg, return, unconditional branch.
+         *        It includes a possible conditional branch to the beginning of the corresponding catch/finally of every 
+         *        instruction in a try section.
+         */
+        private System.Collections.Generic.IEnumerable<GraphNode> nextNodes, nextNodesCatchFinally;
+        public System.Collections.Generic.IEnumerable<GraphNode> NextNodes
+        { get {
+            if (nextNodes == null) {
+                nextNodes = GetNNEnumerable ();
+                nextNodesCatchFinally = new NNEnumerableCatchFinally (this);
+            }
+            return nextNodesCatchFinally;
+        } }
+
+        /**
+         * @brief This acts as a wrapper around all the other NNEnumerable's below.
+         *        It assumes every instruction in a try { } can throw an exception so it 
+         *        says that every instruction in a try { } can conditionally branch to 
+         *        the beginning of the corresponding catch { } or finally { }.
+         */
+        private class NNEnumerableCatchFinally : System.Collections.Generic.IEnumerable<GraphNode> {
+            private GraphNode gn;
+            public NNEnumerableCatchFinally (GraphNode gn)
+            {
+                this.gn = gn;
+            }
+            System.Collections.Generic.IEnumerator<GraphNode> System.Collections.Generic.IEnumerable<GraphNode>.GetEnumerator ()
+            {
+                return new NNEnumeratorCatchFinally (gn);
+            }
+            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+            {
+                return new NNEnumeratorCatchFinally (gn);
+            }
+        }
+        private class NNEnumeratorCatchFinally : NNEnumeratorBase {
+            private GraphNode gn;
+            private int index = 0;
+            private System.Collections.Generic.IEnumerator<GraphNode> realEnumerator;
+            public NNEnumeratorCatchFinally (GraphNode gn)
+            {
+                this.gn = gn;
+                this.realEnumerator = gn.nextNodes.GetEnumerator ();
+            }
+            public override bool MoveNext ()
+            {
+                /*
+                 * First off, return any targets the instruction can come up with.
+                 */
+                if (realEnumerator.MoveNext ()) {
+                    nn = realEnumerator.Current;
+                    return true;
+                }
+
+                /*
+                 * Then if this instruction is in a try section, say this instruction 
+                 * can potentially branch to the beginning of the corresponding 
+                 * catch/finally.
+                 */
+                if ((index == 0) && (gn.tryBlock != null)) {
+                    index ++;
+                    nn = gn.tryBlock.catchFinallyBlock;
+                    return true;
+                }
+
+                /*
+                 * That's all we can do.
+                 */
+                nn = null;
+                return false;
+            }
+            public override void Reset ()
+            {
+                realEnumerator.Reset ();
+                index = 0;
+                nn = null;
+            }
+        }
+
+        /**
+         * @brief This default iterator always returns the next inline node as the one-and-only next node.
+         *        Other instructions need to override it if they can possibly do other than that.
+         */
+
+        /**
+         * @brief GetNNEnumerable() gets the nextnode enumerable part of a GraphNode,
+         *        which in turn gives the list of nodes that can possibly be next in 
+         *        a flow-control sense.  It simply instantiates the NNEnumerator sub-
+         *        class which does the actual enumeration.
+         */
+        protected virtual System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
+        {
+            return new NNEnumerable (this, typeof (NNEnumerator));
+        }
+
+        private class NNEnumerator : NNEnumeratorBase {
+            private GraphNode gn;
+            private int index;
+            public NNEnumerator (GraphNode gn)
+            {
+                this.gn = gn;
+            }
+            public override bool MoveNext ()
+            {
+                switch (index) {
+                    case 0: {
+                        index ++;
+                        nn = gn.nextLin;
+                        return nn != null;
+                    }
+                    case 1: {
+                        nn = null;
+                        return false;
+                    }
+                }
+                throw new Exception ();
+            }
+            public override void Reset ()
+            {
+                index = 0;
+                nn = null;
+            }
+        }
+    }
+
+    /**
+     * @brief Things that derive from this are the beginning of a block.
+     *        A block of code is that which begins with a label or is the beginning of all code
+     *        and it contains no labels, ie, it can't be jumped into other than at its beginning.
+     */
+    public abstract class GraphNodeBlock : GraphNode {
+        public List<ScriptMyLocal> localsWrittenBeforeRead = new List<ScriptMyLocal> ();
+        public List<ScriptMyLocal> localsReadBeforeWritten = new List<ScriptMyLocal> ();
+        public int hasBeenResolved;
+        public GraphNodeBlock (ScriptCollector coll) : base (coll) { }
+    }
+
+    /**
+     * @brief This placeholder is at the beginning of the code so the first few instructions 
+     *        belong to some block.
+     */
+    public class GraphNodeBegin : GraphNodeBlock {
+        public GraphNodeBegin (ScriptCollector coll) : base (coll) { }
+        public override void DebString (StringBuilder sb) { sb.Append ("begin"); }
+        public override void WriteOutOne (ScriptMyILGen ilGen) { }
+    }
+
+    /**
+     * @brief Beginning of try block.
+     */
+    public class GraphNodeBeginExceptionBlock : GraphNodeBlock {
+        public GraphNodeBeginExceptionBlock outerTryBlock;      // next outer try opcode or null
+        public GraphNodeCatchFinallyBlock   catchFinallyBlock;  // start of associated catch or finally
+        public GraphNodeEndExceptionBlock   endExcBlock;        // end of associated catch or finally
+        public int excBlkSeqNo;                                 // debugging
+
+        public GraphNodeBeginExceptionBlock (ScriptCollector coll) : base (coll)
+        { }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            // we should always start try blocks with nothing on stack
+            // ...as CLI wipes stack for various conditions
+            if (coll.stackDepth.Count != 0) {
+                throw new Exception ("stack depth " + coll.stackDepth.Count);
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("  beginexceptionblock_");
+            sb.Append (excBlkSeqNo);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.BeginExceptionBlock ();
+        }
+    }
+
+    /**
+     * @brief Beginning of catch or finally block.
+     */
+    public abstract class GraphNodeCatchFinallyBlock : GraphNodeBlock {
+        public GraphNodeCatchFinallyBlock (ScriptCollector coll) : base (coll)
+        { }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            // we should always start catch/finally blocks with nothing on stack
+            // ...as CLI wipes stack for various conditions
+            if (coll.stackDepth.Count != 0) {
+                throw new Exception ("stack depth " + coll.stackDepth.Count);
+            }
+        }
+    }
+
+    /**
+     * @brief Beginning of catch block.
+     */
+    public class GraphNodeBeginCatchBlock : GraphNodeCatchFinallyBlock {
+        public Type excType;
+
+        public GraphNodeBeginCatchBlock (ScriptCollector coll, Type excType) : base (coll)
+        {
+            this.excType = excType;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            // catch block always enters with one value on stack
+            if (coll.stackDepth.Count != 0) {
+                throw new Exception ("stack depth " + coll.stackDepth.Count);
+            }
+            coll.stackDepth.Push (excType);
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("  begincatchblock_");
+            sb.Append (excBlock.excBlkSeqNo);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.BeginCatchBlock (excType);
+        }
+
+        /**
+         * @brief The beginning of every catch { } conditinally branches to the beginning 
+         *        of all outer catch { }s up to and including the next outer finally { }.
+         */
+        protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
+        {
+            return new NNEnumerable (this, typeof (NNEnumerator));
+        }
+
+        private class NNEnumerator : NNEnumeratorBase {
+            private GraphNodeBeginCatchBlock gn;
+            private int index;
+            public NNEnumerator (GraphNodeBeginCatchBlock gn)
+            {
+                this.gn = gn;
+            }
+            public override bool MoveNext ()
+            {
+                while (true) {
+                    switch (index) {
+                        case 0: {
+                            // start with the fallthru
+                            nn = gn.nextLin;
+                            index ++;
+                            return true;
+                        }
+
+                        case 1: {
+                            // get the first outer catch { } or finally { }
+                            // pretend we last returned beginning of this catch { }
+                            // then loop back to get next outer catch { } or finally { }
+                            nn = gn;
+                            break;
+                        }
+
+                        case 2: {
+                            // nn points to a catch { } previously returned
+                            // get the corresponding try { }
+                            GraphNodeBeginExceptionBlock nntry = nn.excBlock;
+
+                            // step out to next outer try { }
+                            nntry = nntry.excBlock;
+                            if (nntry == null) break;
+
+                            // return corresponding catch { } or finally { }
+                            nn = nntry.catchFinallyBlock;
+
+                            // if it's a finally { } we don't do anything after that
+                            if (nn is GraphNodeBeginFinallyBlock) index ++;
+                            return true;
+                        }
+
+                        case 3: {
+                            // we've returned the fallthru, catches and one finally
+                            // so there's nothing more to say
+                            nn = null;
+                            return false;
+                        }
+
+                        default: throw new Exception ();
+                    }
+                    index ++;
+                }
+            }
+            public override void Reset ()
+            {
+                index = 0;
+                nn = null;
+            }
+        }
+    }
+
+    /**
+     * @brief Beginning of finally block.
+     */
+    public class GraphNodeBeginFinallyBlock : GraphNodeCatchFinallyBlock {
+
+        // leaveTargets has a list of all the targets of any contained 
+        // leave instructions, ie, where an endfinally can possibly jump.
+        // But only those targets within the next outer finally { }, we 
+        // don't contain any targets outside of that, those targets are 
+        // stored in the actual finally that will jump to the target.
+        // The endfinally enumerator assumes that it is always possible 
+        // for it to jump to the next outer finally (as would happen for
+        // an uncaught exception), so no need to do anything special.
+        public List<GraphNodeBlock> leaveTargets = new List<GraphNodeBlock> ();
+
+        public GraphNodeBeginFinallyBlock (ScriptCollector coll) : base (coll)
+        { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("  beginfinallyblock_");
+            sb.Append (excBlock.excBlkSeqNo);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.BeginFinallyBlock ();
+        }
+    }
+
+    /**
+     * @brief End of try/catch/finally block.
+     */
+    public class GraphNodeEndExceptionBlock : GraphNode {
+        public GraphNodeEndExceptionBlock (ScriptCollector coll) : base (coll)
+        { }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            // we should always end exception blocks with nothing on stack
+            // ...as CLI wipes stack for various conditions
+            if (coll.stackDepth.Count != 0) {
+                throw new Exception ("stack depth " + coll.stackDepth.Count);
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("  endexceptionblock_");
+            sb.Append (excBlock.excBlkSeqNo);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.EndExceptionBlock ();
+        }
+    }
+
+    /**
+     * @brief Actual instruction emits...
+     */
+    public abstract class GraphNodeEmit : GraphNode {
+        public OpCode opcode;
+        public Token errorAt;
+
+        public GraphNodeEmit (ScriptCollector coll, Token errorAt, OpCode opcode) : base (coll)
+        {
+            this.opcode  = opcode;
+            this.errorAt = errorAt;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            // compute resultant stack depth
+            int stack = coll.stackDepth.Count;
+
+            if ((stack != 0) && ((opcode == OpCodes.Endfinally) || (opcode == OpCodes.Leave) || (opcode == OpCodes.Rethrow))) {
+                throw new Exception (opcode + " stack depth " + stack);
+            }
+            if ((stack != 1) && (opcode == OpCodes.Throw)) {
+                throw new Exception (opcode + " stack depth " + stack);
+            }
+        }
+
+        /**
+         * @brief See if it's possible for it to fall through to the next inline (nextLin) instruction.
+         */
+        public override bool CanFallThrough ()
+        {
+            switch (opcode.FlowControl) {
+                case FlowControl.Branch:      return false;  // unconditional branch
+                case FlowControl.Break:       return true;   // break
+                case FlowControl.Call:        return true;   // call
+                case FlowControl.Cond_Branch: return true;   // conditional branch
+                case FlowControl.Next:        return true;   // falls through to next instruction
+                case FlowControl.Return:      return false;  // return
+                case FlowControl.Throw:       return false;  // throw
+                default: {
+                    string op = opcode.ToString ();
+                    if (op == "volatile.") return true;
+                    throw new Exception ("unknown flow control " + opcode.FlowControl + " for " + op);
+                }
+            }
+        }
+
+        // if followed by OpCodes.Pop, it can be discarded
+        public bool isPoppable
+        { get {
+            return
+                ((opcode.StackBehaviourPop  == StackBehaviour.Pop0) &&    // ldarg,ldloc,ldsfld
+                 (opcode.StackBehaviourPush == StackBehaviour.Push1)) || 
+                ((opcode.StackBehaviourPop  == StackBehaviour.Pop0) &&    // ldarga,ldloca,ldc,ldsflda,...
+                 (opcode.StackBehaviourPush == StackBehaviour.Pushi)) || 
+                (opcode == OpCodes.Ldnull) || 
+                (opcode == OpCodes.Ldc_R4) || 
+                (opcode == OpCodes.Ldc_R8) || 
+                (opcode == OpCodes.Ldstr)  || 
+                (opcode == OpCodes.Ldc_I8) || 
+                (opcode == OpCodes.Dup);
+        } }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("".PadRight (OPINDENT));
+            sb.Append (opcode.ToString ().PadRight (OPDEBLEN));
+        }
+
+        /**
+         * @brief If instruction is terminating, we say there is nothing following (eg, return).
+         *        Otherwise, say the one-and-only next instruction is the next instruction inline.
+         */
+        protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
+        {
+            return new NNEnumerable (this, typeof (NNEnumerator));
+        }
+
+        private class NNEnumerator : NNEnumeratorBase {
+            private GraphNodeEmit gn;
+            private int index;
+            public NNEnumerator (GraphNodeEmit gn)
+            {
+                this.gn = gn;
+            }
+            public override bool MoveNext ()
+            {
+                switch (index) {
+                    case 0: {
+                        if (gn.CanFallThrough ()) {
+                            index ++;
+                            nn = gn.nextLin;
+                            return nn != null;
+                        }
+                        return false;
+                    }
+                    case 1: {
+                        nn = null;
+                        return false;
+                    }
+                }
+                throw new Exception ();
+            }
+            public override void Reset ()
+            {
+                index = 0;
+                nn = null;
+            }
+        }
+    }
+
+    public class GraphNodeEmitNull : GraphNodeEmit {
+        public GraphNodeEmitNull (ScriptCollector coll, Token errorAt, OpCode opcode) : base (coll, errorAt, opcode)
+        { }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "nop": break;
+                case "break": break;
+                case "volatile.": break;
+                case "ldarg.0": coll.stackDepth.Push (coll.wrapped.argTypes[0]); break;
+                case "ldarg.1": coll.stackDepth.Push (coll.wrapped.argTypes[1]); break;
+                case "ldarg.2": coll.stackDepth.Push (coll.wrapped.argTypes[2]); break;
+                case "ldarg.3": coll.stackDepth.Push (coll.wrapped.argTypes[3]); break;
+                case "ldnull":  coll.stackDepth.Push (null); break;
+                case "ldc.i4.m1":
+                case "ldc.i4.0":
+                case "ldc.i4.1":
+                case "ldc.i4.2":
+                case "ldc.i4.3":
+                case "ldc.i4.4":
+                case "ldc.i4.5":
+                case "ldc.i4.6":
+                case "ldc.i4.7":
+                case "ldc.i4.8": {
+                    coll.stackDepth.Push (typeof (int));
+                    break;
+                }
+                case "dup": {
+                    Type t = coll.stackDepth.Peek (0);
+                    bool b = coll.stackDepth.PeekBoxed (0);
+                    coll.stackDepth.Push (t, b);
+                    break;
+                }
+                case "pop": {
+                    coll.stackDepth.Pop (1);
+                    break;
+                }
+                case "ret": {
+                    int sd = (coll.wrapped.retType != typeof (void)) ? 1 : 0;
+                    if (coll.stackDepth.Count != sd) throw new Exception ("bad stack depth");
+                    if (sd > 0) {
+                        coll.stackDepth.Pop (coll.wrapped.retType);
+                    }
+                    break;
+                }
+                case "add":
+                case "sub":
+                case "mul":
+                case "div":
+                case "div.un":
+                case "rem":
+                case "rem.un":
+                case "and":
+                case "or":
+                case "xor":
+                case "shl":
+                case "shr":
+                case "shr.un":
+                case "add.ovf":
+                case "add.ovf.un":
+                case "mul.ovf":
+                case "mul.ovf.un":
+                case "sub.ovf":
+                case "sub.ovf.un": {
+                    coll.stackDepth.PopNumVal ();
+                    Type t = coll.stackDepth.PopNumVal ();
+                    coll.stackDepth.Push (t);
+                    break;
+                }
+                case "neg":
+                case "not": {
+                    Type t = coll.stackDepth.PopNumVal ();
+                    coll.stackDepth.Push (t);
+                    break;
+                }
+                case "conv.i1":
+                case "conv.i2":
+                case "conv.i4":
+                case "conv.i8":
+                case "conv.r4":
+                case "conv.r8":
+                case "conv.u4":
+                case "conv.u8":
+                case "conv.r.un":
+                case "conv.ovf.i1.un":
+                case "conv.ovf.i2.un":
+                case "conv.ovf.i4.un":
+                case "conv.ovf.i8.un":
+                case "conv.ovf.u1.un":
+                case "conv.ovf.u2.un":
+                case "conv.ovf.u4.un":
+                case "conv.ovf.u8.un":
+                case "conv.ovf.i.un":
+                case "conv.ovf.u.un":
+                case "conv.ovf.i1":
+                case "conv.ovf.u1":
+                case "conv.ovf.i2":
+                case "conv.ovf.u2":
+                case "conv.ovf.i4":
+                case "conv.ovf.u4":
+                case "conv.ovf.i8":
+                case "conv.ovf.u8":
+                case "conv.u2":
+                case "conv.u1":
+                case "conv.i":
+                case "conv.ovf.i":
+                case "conv.ovf.u":
+                case "conv.u": {
+                    coll.stackDepth.PopNumVal ();
+                    coll.stackDepth.Push (ConvToType (opcode));
+                    break;
+                }
+                case "throw": {
+                    if (coll.stackDepth.Count != 1) throw new Exception ("bad stack depth " + coll.stackDepth.Count);
+                    coll.stackDepth.PopRef ();
+                    break;
+                }
+                case "ldlen": {
+                    coll.stackDepth.Pop (typeof (string));
+                    coll.stackDepth.Push (typeof (int));
+                    break;
+                }
+                case "ldelem.i1":
+                case "ldelem.u1":
+                case "ldelem.i2":
+                case "ldelem.u2":
+                case "ldelem.i4":
+                case "ldelem.u4":
+                case "ldelem.i8":
+                case "ldelem.i":
+                case "ldelem.r4":
+                case "ldelem.r8":
+                case "ldelem.ref": {
+                    Type t = coll.stackDepth.Peek (1).GetElementType ();
+                    coll.stackDepth.Pop (typeof (int));
+                    coll.stackDepth.Pop (t.MakeArrayType ());
+                    coll.stackDepth.Push (t);
+                    break;
+                }
+                case "stelem.i":
+                case "stelem.i1":
+                case "stelem.i2":
+                case "stelem.i4":
+                case "stelem.i8":
+                case "stelem.r4":
+                case "stelem.r8":
+                case "stelem.ref": {
+                    Type t = coll.stackDepth.Peek (2).GetElementType ();
+                    coll.stackDepth.Pop (t);
+                    coll.stackDepth.Pop (typeof (int));
+                    coll.stackDepth.Pop (t.MakeArrayType ());
+                    break;
+                }
+                case "endfinally":
+                case "rethrow": {
+                    if (coll.stackDepth.Count != 0) throw new Exception ("bad stack depth " + coll.stackDepth.Count);
+                    break;
+                }
+                case "ceq": {
+                    Type t = coll.stackDepth.Pop (1);
+                    if (t == null) {
+                        coll.stackDepth.PopRef ();
+                    } else {
+                        coll.stackDepth.Pop (t);
+                    }
+                    coll.stackDepth.Push (typeof (int));
+                    break;
+                }
+                case "cgt":
+                case "cgt.un":
+                case "clt":
+                case "clt.un": {
+                    coll.stackDepth.PopNumVal ();
+                    coll.stackDepth.PopNumVal ();
+                    coll.stackDepth.Push (typeof (int));
+                    break;
+                }
+                case "ldind.i4": {
+                    coll.stackDepth.Pop (typeof (int).MakeByRefType ());
+                    coll.stackDepth.Push (typeof (int));
+                    break;
+                }
+                case "stind.i4": {
+                    coll.stackDepth.Pop (typeof (int));
+                    coll.stackDepth.Pop (typeof (int).MakeByRefType ());
+                    break;
+                }
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        private static Type ConvToType (OpCode opcode)
+        {
+            string s = opcode.ToString ();
+            s = s.Substring (5);  // strip off "conv."
+            if (s.StartsWith ("ovf.")) s = s.Substring (4);
+            if (s.EndsWith (".un")) s = s.Substring (0, s.Length - 3);
+
+            switch (s) {
+                case "i":  return typeof (IntPtr);
+                case "i1": return typeof (sbyte);
+                case "i2": return typeof (short);
+                case "i4": return typeof (int);
+                case "i8": return typeof (long);
+                case "r":
+                case "r4": return typeof (float);
+                case "r8": return typeof (double);
+                case "u1": return typeof (byte);
+                case "u2": return typeof (ushort);
+                case "u4": return typeof (uint);
+                case "u8": return typeof (ulong);
+                case "u":  return typeof (UIntPtr);
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode);
+        }
+    }
+
+    public class GraphNodeEmitNullEndfinally : GraphNodeEmitNull {
+        public GraphNodeEmitNullEndfinally (ScriptCollector coll, Token errorAt) : base (coll, errorAt, OpCodes.Endfinally)
+        { }
+
+        /**
+         * @brief Endfinally can branch to:
+         *          1) the corresponding EndExceptionBlock
+         *          2) any of the corresponding BeginFinallyBlock's leaveTargets
+         *          3) the next outer BeginFinallyBlock
+         */
+        protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
+        {
+            return new NNEnumerable (this, typeof (NNEnumerator));
+        }
+
+        private class NNEnumerator : NNEnumeratorBase {
+            private GraphNodeEmitNullEndfinally gn;
+            private IEnumerator<GraphNodeBlock> leaveTargetEnumerator;
+            private int index;
+            public NNEnumerator (GraphNodeEmitNullEndfinally gn)
+            {
+                this.gn = gn;
+
+                // endfinally instruction must be within some try/catch/finally mess
+                GraphNodeBeginExceptionBlock thistry = gn.excBlock;
+
+                // endfinally instruction must be within some finally { } mess
+                GraphNodeBeginFinallyBlock thisfin = (GraphNodeBeginFinallyBlock)thistry.catchFinallyBlock;
+
+                // get the list of the finally { } leave instruction targets
+                this.leaveTargetEnumerator = thisfin.leaveTargets.GetEnumerator ();
+            }
+            public override bool MoveNext ()
+            {
+                while (true) {
+                    switch (index) {
+
+                        // to start, return end of our finally { }
+                        case 0: {
+                            GraphNodeBeginExceptionBlock thistry = gn.excBlock;
+                            nn = thistry.endExcBlock;
+                            if (nn == null) throw new NullReferenceException ("thistry.endExcBlock");
+                            index ++;
+                            return true;
+                        }
+
+                        // return next one of our finally { }'s leave targets
+                        // ie, where any leave instructions in the try { } want 
+                        // the finally { } to go to when it finishes
+                        case 1: {
+                            if (this.leaveTargetEnumerator.MoveNext ()) {
+                                nn = this.leaveTargetEnumerator.Current;
+                                if (nn == null) throw new NullReferenceException ("this.leaveTargetEnumerator.Current");
+                                return true;
+                            }
+                            break;
+                        }
+
+                        // return beginning of next outer finally { }
+                        case 2: {
+                            GraphNodeBeginExceptionBlock nntry = gn.excBlock;
+                            while ((nntry = nntry.excBlock) != null) {
+                                if (nntry.catchFinallyBlock is GraphNodeBeginFinallyBlock) {
+                                    nn = nntry.catchFinallyBlock;
+                                    if (nn == null) throw new NullReferenceException ("nntry.catchFinallyBlock");
+                                    index ++;
+                                    return true;
+                                }
+                            }
+                            break;
+                        }
+
+                        // got nothing more
+                        case 3: {
+                            return false;
+                        }
+
+                        default: throw new Exception ();
+                    }
+                    index ++;
+                }
+            }
+            public override void Reset ()
+            {
+                leaveTargetEnumerator.Reset ();
+                index = 0;
+                nn = null;
+            }
+        }
+    }
+
+    public class GraphNodeEmitField : GraphNodeEmit {
+        public FieldInfo field;
+
+        public GraphNodeEmitField (ScriptCollector coll, Token errorAt, OpCode opcode, FieldInfo field) : base (coll, errorAt, opcode)
+        {
+            this.field = field;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "ldfld":   PopPointer ();                         coll.stackDepth.Push (field.FieldType);                  break;
+                case "ldflda":  PopPointer ();                         coll.stackDepth.Push (field.FieldType.MakeByRefType ()); break;
+                case "stfld":   coll.stackDepth.Pop (field.FieldType); PopPointer ();                                           break;
+                case "ldsfld":                                         coll.stackDepth.Push (field.FieldType);                  break;
+                case "ldsflda":                                        coll.stackDepth.Push (field.FieldType.MakeByRefType ()); break;
+                case "stsfld":                                         coll.stackDepth.Pop  (field.FieldType);                  break;
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+        private void PopPointer ()
+        {
+            Type t = field.DeclaringType;               // get class/field type
+            if (t.IsValueType) {
+                Type brt = t.MakeByRefType ();      // if value type, eg Vector, it can be pushed by reference or by value
+                int c = coll.stackDepth.Count;
+                if ((c > 0) && (coll.stackDepth[c-1] == brt)) t = brt;
+            }
+            coll.stackDepth.Pop (t);                    // type of what should be on the stack pointing to object or struct
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append (field.Name);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, field);
+        }
+    }
+
+    public class GraphNodeEmitLocal : GraphNodeEmit {
+        public ScriptMyLocal myLocal;
+
+        public GraphNodeEmitLocal (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptMyLocal myLocal) : base (coll, errorAt, opcode)
+        {
+            this.myLocal = myLocal;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "ldloc":  coll.stackDepth.Push (myLocal.type); break;
+                case "ldloca": coll.stackDepth.Push (myLocal.type.MakeByRefType ()); break;
+                case "stloc":  coll.stackDepth.Pop  (myLocal.type); break;
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append (myLocal.name);
+        }
+
+        public override ScriptMyLocal ReadsLocal  ()
+        {
+            if (opcode == OpCodes.Ldloc)  return myLocal;
+            if (opcode == OpCodes.Ldloca) return myLocal;
+            if (opcode == OpCodes.Stloc)  return null;
+            throw new Exception ("unknown opcode " + opcode);
+        }
+        public override ScriptMyLocal WritesLocal ()
+        {
+            if (opcode == OpCodes.Ldloc)  return null;
+            if (opcode == OpCodes.Ldloca) return myLocal;
+            if (opcode == OpCodes.Stloc)  return myLocal;
+            throw new Exception ("unknown opcode " + opcode);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, myLocal);
+        }
+    }
+
+    public class GraphNodeEmitType : GraphNodeEmit {
+        public Type type;
+
+        public GraphNodeEmitType (ScriptCollector coll, Token errorAt, OpCode opcode, Type type) : base (coll, errorAt, opcode)
+        {
+            this.type = type;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "castclass":
+                case "isinst": {
+                    coll.stackDepth.PopRef ();
+                    coll.stackDepth.Push (type, type.IsValueType);
+                    break;
+                }
+                case "box": {
+                    if (!type.IsValueType) throw new Exception ("can't box a non-value type");
+                    coll.stackDepth.Pop (type);
+                    coll.stackDepth.Push (type, true);
+                    break;
+                }
+                case "unbox":
+                case "unbox.any": {
+                    if (!type.IsValueType) throw new Exception ("can't unbox to a non-value type");
+                    coll.stackDepth.PopRef ();
+                    coll.stackDepth.Push (type);
+                    break;
+                }
+                case "newarr": {
+                    coll.stackDepth.Pop (typeof (int));
+                    coll.stackDepth.Push (type.MakeArrayType ());
+                    break;
+                }
+                case "sizeof": {
+                    coll.stackDepth.Pop (1);
+                    coll.stackDepth.Push (typeof (int));
+                    break;
+                }
+                case "ldelem": {
+                    coll.stackDepth.Pop (typeof (int));
+                    coll.stackDepth.Pop (type.MakeArrayType ());
+                    coll.stackDepth.Push (type);
+                    break;
+                }
+                case "ldelema": {
+                    coll.stackDepth.Pop (typeof (int));
+                    coll.stackDepth.Pop (type.MakeArrayType ());
+                    coll.stackDepth.Push (type.MakeByRefType ());
+                    break;
+                }
+                case "stelem": {
+                    coll.stackDepth.Pop (type);
+                    coll.stackDepth.Pop (typeof (int));
+                    coll.stackDepth.Pop (type.MakeArrayType ());
+                    break;
+                }
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append (type.Name);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, type);
+        }
+    }
+
+    public class GraphNodeEmitLabel : GraphNodeEmit {
+        public ScriptMyLabel myLabel;
+
+        public GraphNodeEmitLabel (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptMyLabel myLabel) : base (coll, errorAt, opcode)
+        {
+            this.myLabel = myLabel;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "brfalse.s":
+                case "brtrue.s":
+                case "brfalse":
+                case "brtrue": {
+                    coll.stackDepth.Pop (1);
+                    break;
+                }
+                case "beq.s":
+                case "bge.s":
+                case "bgt.s":
+                case "ble.s":
+                case "blt.s":
+                case "bne.un.s":
+                case "bge.un.s":
+                case "bgt.un.s":
+                case "ble.un.s":
+                case "blt.un.s":
+                case "beq":
+                case "bge":
+                case "bgt":
+                case "ble":
+                case "blt":
+                case "bne.un":
+                case "bge.un":
+                case "bgt.un":
+                case "ble.un":
+                case "blt.un": {
+                    coll.stackDepth.PopNumVal ();
+                    coll.stackDepth.PopNumVal ();
+                    break;
+                }
+                case "br":
+                case "br.s": break;
+                case "leave": {
+                    if (coll.stackDepth.Count != 0) throw new Exception ("bad stack depth " + coll.stackDepth.Count);
+                    break;
+                }
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+
+            // if a target doesn't have a depth yet, set its depth to the depth after instruction executes
+            // otherwise, make sure it matches all other branches to that target and what fell through to it
+            coll.stackDepth.Matches (myLabel);
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append (myLabel.name);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, myLabel);
+        }
+
+        /**
+         * @brief Conditional branches return the next inline followed by the branch target
+         *        Unconditional branches return only the branch target
+         *        But if the target is outside our scope (eg __retlbl), omit it from the list
+         */
+        protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
+        {
+            return new NNEnumerable (this, typeof (NNEnumerator));
+        }
+
+        private class NNEnumerator : NNEnumeratorBase {
+            private GraphNodeEmitLabel gn;
+            private int index;
+            public NNEnumerator (GraphNodeEmitLabel gn)
+            {
+                this.gn = gn;
+            }
+            public override bool MoveNext ()
+            {
+                switch (gn.opcode.FlowControl) {
+                    case FlowControl.Branch: {
+                        // unconditional branch just goes to target and nothing else
+                        switch (index) {
+                            case 0: {
+                                nn = gn.myLabel.whereAmI;
+                                index ++;
+                                return nn != null;
+                            }
+                            case 1: {
+                                return false;
+                            }
+                        }
+                        throw new Exception ();
+                    }
+                    case FlowControl.Cond_Branch: {
+                        // conditional branch goes inline and to target
+                        switch (index) {
+                            case 0: {
+                                nn = gn.nextLin;
+                                index ++;
+                                return true;
+                            }
+                            case 1: {
+                                nn = gn.myLabel.whereAmI;
+                                index ++;
+                                return nn != null;
+                            }
+                            case 2: {
+                                return false;
+                            }
+                        }
+                        throw new Exception ();
+                    }
+                    default: throw new Exception ("unknown flow control " + gn.opcode.FlowControl.ToString () + 
+                                                  " of " + gn.opcode.ToString ());
+                }
+            }
+            public override void Reset ()
+            {
+                index = 0;
+                nn = null;
+            }
+        }
+    }
+
+    public class GraphNodeEmitLabelLeave : GraphNodeEmitLabel {
+        public GraphNodeBlock unwindTo;  // if unwinding, innermost finally block being unwound
+                                         //         else, same as myTarget.whereAmI
+                                         // null if unwinding completely out of scope, eg, __retlbl
+
+        public GraphNodeEmitLabelLeave (ScriptCollector coll, Token errorAt, ScriptMyLabel myLabel) : base (coll, errorAt, OpCodes.Leave, myLabel)
+        { }
+
+        /**
+         * @brief Leave instructions have exactly one unconditional next node.
+         *        Either the given target if within the same try block 
+         *        or the beginning of the intervening finally block.
+         */
+        protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
+        {
+            return new NNEnumerable (this, typeof (NNEnumerator));
+        }
+
+        private class NNEnumerator : NNEnumeratorBase {
+            private GraphNodeEmitLabelLeave gn;
+            private int index;
+            public NNEnumerator (GraphNodeEmitLabelLeave gn)
+            {
+                this.gn = gn;
+            }
+            public override bool MoveNext ()
+            {
+                if (index == 0) {
+                    nn = gn.unwindTo;
+                    index ++;
+                    return nn != null;
+                }
+                nn = null;
+                return false;
+            }
+            public override void Reset ()
+            {
+                index = 0;
+                nn = null;
+            }
+        }
+    }
+
+    public class GraphNodeEmitLabels : GraphNodeEmit {
+        public ScriptMyLabel[] myLabels;
+
+        public GraphNodeEmitLabels (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels) : base (coll, errorAt, opcode)
+        {
+            this.myLabels = myLabels;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "switch": {
+                    coll.stackDepth.Pop (typeof (int));
+                    break;
+                }
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+
+            // if a target doesn't have a depth yet, set its depth to the depth after instruction executes
+            // otherwise, make sure it matches all other branches to that target and what fell through to it
+            foreach (ScriptMyLabel myLabel in myLabels) {
+                coll.stackDepth.Matches (myLabel);
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            bool first = true;
+            foreach (ScriptMyLabel lbl in myLabels) {
+                if (!first) sb.Append (',');
+                sb.Append (lbl.name);
+                first = false;
+            }
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, myLabels);
+        }
+
+        /**
+         * @brief Return list of all labels followed by the next linear instruction
+         *        But if the target is outside our scope (eg __retlbl), omit it from the list
+         */
+        protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
+        {
+            return new NNEnumerable (this, typeof (NNEnumerator));
+        }
+
+        private class NNEnumerator : NNEnumeratorBase {
+            private GraphNodeEmitLabels gn;
+            private int index;
+            public NNEnumerator (GraphNodeEmitLabels gn)
+            {
+                this.gn = gn;
+            }
+            public override bool MoveNext ()
+            {
+                /*
+                 * Return next from list of switch case labels.
+                 */
+                while (index < gn.myLabels.Length) {
+                    nn = gn.myLabels[index++].whereAmI;
+                    if (nn != null) return true;
+                }
+
+                /*
+                 * If all ran out, the switch instruction falls through.
+                 */
+                if (index == gn.myLabels.Length) {
+                    index ++;
+                    nn = gn.nextLin;
+                    return true;
+                }
+
+                /*
+                 * Even ran out of that, say there's nothing more.
+                 */
+                nn = null;
+                return false;
+            }
+            public override void Reset ()
+            {
+                index = 0;
+                nn = null;
+            }
+        }
+    }
+
+    public class GraphNodeEmitIntMeth : GraphNodeEmit {
+        public ScriptObjWriter method;
+
+        public GraphNodeEmitIntMeth (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptObjWriter method) : base (coll, errorAt, opcode)
+        {
+            this.method = method;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "call": {
+
+                    // calls have Varpop so pop the number of arguments
+                    // they are all static so there is no separate 'this' parameter
+                    coll.stackDepth.Pop (this.method.argTypes);
+
+                    // calls are also Varpush so they push a return value iff non-void
+                    if (this.method.retType != typeof (void)) coll.stackDepth.Push (this.method.retType);
+                    break;
+                }
+
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append (method.methName);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, method);
+        }
+    }
+
+    public class GraphNodeEmitExtMeth : GraphNodeEmit {
+        public MethodInfo method;
+
+        public GraphNodeEmitExtMeth (ScriptCollector coll, Token errorAt, OpCode opcode, MethodInfo method) : base (coll, errorAt, opcode)
+        {
+            this.method = method;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "call":
+                case "callvirt": {
+
+                    // calls have Varpop so pop the number of arguments
+                    coll.stackDepth.Pop (this.method.GetParameters ());
+                    if ((this.method.CallingConvention & CallingConventions.HasThis) != 0) {
+                        coll.stackDepth.Pop (method.DeclaringType);
+                    }
+
+                    // calls are also Varpush so they push a return value iff non-void
+                    if (this.method.ReturnType != typeof (void)) coll.stackDepth.Push (this.method.ReturnType);
+                    break;
+                }
+
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append (method.Name);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, method);
+        }
+    }
+
+    public class GraphNodeEmitCtor : GraphNodeEmit {
+        public ConstructorInfo ctor;
+
+        public GraphNodeEmitCtor (ScriptCollector coll, Token errorAt, OpCode opcode, ConstructorInfo ctor) : base (coll, errorAt, opcode)
+        {
+            this.ctor = ctor;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "newobj": {
+                    coll.stackDepth.Pop (ctor.GetParameters ());
+                    coll.stackDepth.Push (ctor.DeclaringType);
+                    break;
+                }
+
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append (ctor.ReflectedType.Name);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, ctor);
+        }
+    }
+
+    public class GraphNodeEmitDouble : GraphNodeEmit {
+        public double value;
+
+        public GraphNodeEmitDouble (ScriptCollector coll, Token errorAt, OpCode opcode, double value) : base (coll, errorAt, opcode)
+        {
+            this.value = value;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "ldc.r8": coll.stackDepth.Push (typeof (double)); break;
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append (value);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, value);
+        }
+    }
+
+    public class GraphNodeEmitFloat : GraphNodeEmit {
+        public float value;
+
+        public GraphNodeEmitFloat (ScriptCollector coll, Token errorAt, OpCode opcode, float value) : base (coll, errorAt, opcode)
+        {
+            this.value = value;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "ldc.r4": coll.stackDepth.Push (typeof (float)); break;
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append (value);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, value);
+        }
+    }
+
+    public class GraphNodeEmitInt : GraphNodeEmit {
+        public int value;
+
+        public GraphNodeEmitInt (ScriptCollector coll, Token errorAt, OpCode opcode, int value) : base (coll, errorAt, opcode)
+        {
+            this.value = value;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "ldarg":
+                case "ldarg.s":  coll.stackDepth.Push (coll.wrapped.argTypes[value]);                  break;
+                case "ldarga":
+                case "ldarga.s": coll.stackDepth.Push (coll.wrapped.argTypes[value].MakeByRefType ()); break;
+                case "starg":
+                case "starg.s":  coll.stackDepth.Pop  (coll.wrapped.argTypes[value]);                  break;
+                case "ldc.i4":
+                case "ldc.i4.s": coll.stackDepth.Push (typeof (int));                                  break;
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append (value);
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, value);
+        }
+    }
+
+    public class GraphNodeEmitString : GraphNodeEmit {
+        public string value;
+
+        public GraphNodeEmitString (ScriptCollector coll, Token errorAt, OpCode opcode, string value) : base (coll, errorAt, opcode)
+        {
+            this.value = value;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            switch (opcode.ToString ()) {
+                case "ldstr": coll.stackDepth.Push (typeof (string)); break;
+                default: throw new Exception ("unknown opcode " + opcode.ToString ());
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            base.DebString (sb);
+            sb.Append ("\"");
+            sb.Append (value);
+            sb.Append ("\"");
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.Emit (errorAt, opcode, value);
+        }
+    }
+
+    public class GraphNodeMarkLabel : GraphNodeBlock {
+        public ScriptMyLabel myLabel;
+
+        public GraphNodeMarkLabel (ScriptCollector coll, ScriptMyLabel myLabel) : base (coll)
+        {
+            this.myLabel = myLabel;
+        }
+
+        public override void ChainLin ()
+        {
+            base.ChainLin ();
+
+            // if previous instruction can fall through to this label,
+            //     if the label doesn't yet have a stack depth, mark it with current stack depth
+            //     else, the label's stack depth from forward branches and current stack depth must match
+            // else,
+            //     label must have had a forward branch to it so we can know stack depth
+            //     set the current stack depth to the label's stack depth as of that forward branch
+            if (myLabel.whereAmI.prevLin.CanFallThrough ()) {
+                coll.stackDepth.Matches (myLabel);
+            } else {
+                if (myLabel.stackDepth == null) {
+                    throw new Exception ("stack depth unknown at " + myLabel.name);
+                }
+                coll.stackDepth.Clear ();
+                int n = myLabel.stackDepth.Length;
+                for (int i = 0; i < n; i ++) {
+                    coll.stackDepth.Push (myLabel.stackDepth[i], myLabel.stackBoxeds[i]);
+                }
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append (myLabel.name);
+            sb.Append (':');
+            if (myLabel.stackDepth != null) {
+                sb.Append ("  [");
+                sb.Append (myLabel.stackDepth.Length);
+                sb.Append (']');
+            }
+        }
+
+        public override void WriteOutOne (ScriptMyILGen ilGen)
+        {
+            ilGen.MarkLabel (myLabel);
+        }
+    }
+
+
+    /**
+     * @brief Generates enumerator that steps through list of nodes that can
+     *        possibly be next in a flow-control sense.
+     */
+    public class NNEnumerable : System.Collections.Generic.IEnumerable<GraphNode> {
+        private object[] cps;
+        private ConstructorInfo ci;
+
+        public NNEnumerable (GraphNode gn, Type nnEnumeratorType)
+        {
+            this.cps = new object[] { gn };
+            this.ci  = nnEnumeratorType.GetConstructor (new Type[] { gn.GetType () });
+        }
+        System.Collections.Generic.IEnumerator<GraphNode> System.Collections.Generic.IEnumerable<GraphNode>.GetEnumerator ()
+        {
+            return (System.Collections.Generic.IEnumerator<GraphNode>) ci.Invoke (cps);
+        }
+        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+        {
+            return (System.Collections.IEnumerator) ci.Invoke (cps);
+        }
+    }
+
+
+    /**
+     * @brief Steps through list of nodes that can possible be next in a flow-control sense.
+     */
+    public abstract class NNEnumeratorBase : System.Collections.Generic.IEnumerator<GraphNode> {
+        protected GraphNode nn;
+
+        public abstract bool MoveNext ();
+        public abstract void Reset ();
+
+        GraphNode System.Collections.Generic.IEnumerator<GraphNode>.Current {
+            get { return this.nn; }
+        }
+        object System.Collections.IEnumerator.Current {
+            get { return this.nn; }
+        }
+        void System.IDisposable.Dispose() { }
+    }
+
+
+    public class ScriptCollector : ScriptMyILGen {
+        public static readonly bool DEBUG = false;
+
+        public ScriptObjWriter wrapped;
+        public GraphNode firstLin, lastLin;
+        private bool resolvedSomething;
+        private int resolveSequence;
+        private int excBlkSeqNos;
+        public StackDepth stackDepth = new StackDepth ();
+
+        public GraphNodeBeginExceptionBlock curTryBlock = null;  // pushed at beginning of try
+                                                                 // popped at BEGINNING of catch/finally
+        public GraphNodeBeginExceptionBlock curExcBlock = null;  // pushed at beginning of try
+                                                                 // popped at END of catch/finally
+
+        private List<ScriptMyLocal> declaredLocals = new List<ScriptMyLocal> ();
+        private List<ScriptMyLabel> definedLabels  = new List<ScriptMyLabel> ();
+
+        public string methName { get { return wrapped.methName; } }
+
+        /**
+         * @brief Wrap the optimizer around the ScriptObjWriter to collect the instruction stream.
+         *        All stream-writing calls get saved to our graph nodes instead of being written to object file.
+         */
+        public ScriptCollector (ScriptObjWriter wrapped)
+        {
+            this.wrapped       = wrapped;
+            GraphNodeBegin gnb = new GraphNodeBegin (this);
+            this.firstLin      = gnb;
+            this.lastLin       = gnb;
+        }
+
+        public ScriptMyLocal DeclareLocal (Type type, string name)
+        {
+            ScriptMyLocal loc = new ScriptMyLocal ();
+            loc.name   = name;
+            loc.type   = type;
+            loc.number = wrapped.localNumber ++;
+            declaredLocals.Add (loc);
+            return loc;
+        }
+
+        public ScriptMyLabel DefineLabel (string name)
+        {
+            ScriptMyLabel lbl = new ScriptMyLabel ();
+            lbl.name   = name;
+            lbl.number = wrapped.labelNumber ++;
+            definedLabels.Add (lbl);
+            return lbl;
+        }
+
+        public void BeginExceptionBlock ()
+        {
+            GraphNodeBeginExceptionBlock tryBlock = new GraphNodeBeginExceptionBlock (this);
+            tryBlock.ChainLin ();
+            tryBlock.excBlkSeqNo = ++ this.excBlkSeqNos;
+            this.curExcBlock = tryBlock;
+            this.curTryBlock = tryBlock;
+        }
+
+        public void BeginCatchBlock (Type excType)
+        {
+            GraphNodeBeginCatchBlock catchBlock = new GraphNodeBeginCatchBlock (this, excType);
+            catchBlock.ChainLin ();
+            if (curExcBlock.catchFinallyBlock != null) throw new Exception ("only one catch/finally allowed per try");
+            curExcBlock.catchFinallyBlock = catchBlock;
+            curTryBlock = curExcBlock.tryBlock;
+        }
+
+        public void BeginFinallyBlock ()
+        {
+            GraphNodeBeginFinallyBlock finallyBlock = new GraphNodeBeginFinallyBlock (this);
+            finallyBlock.ChainLin ();
+            if (curExcBlock.catchFinallyBlock != null) throw new Exception ("only one catch/finally allowed per try");
+            curExcBlock.catchFinallyBlock = finallyBlock;
+            curTryBlock = curExcBlock.tryBlock;
+        }
+
+        public void EndExceptionBlock ()
+        {
+            GraphNodeEndExceptionBlock endExcBlock = new GraphNodeEndExceptionBlock (this);
+            endExcBlock.ChainLin ();
+            curExcBlock.endExcBlock = endExcBlock;
+            curTryBlock = curExcBlock.tryBlock;
+            curExcBlock = curExcBlock.excBlock;
+        }
+
+        public void Emit (Token errorAt, OpCode opcode)
+        {
+            if (opcode == OpCodes.Endfinally) {
+                new GraphNodeEmitNullEndfinally (this, errorAt).ChainLin ();
+            } else {
+                new GraphNodeEmitNull (this, errorAt, opcode).ChainLin ();
+            }
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, FieldInfo field)
+        {
+            if (field == null) throw new ArgumentNullException ("field");
+            new GraphNodeEmitField (this, errorAt, opcode, field).ChainLin ();
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal)
+        {
+            new GraphNodeEmitLocal (this, errorAt, opcode, myLocal).ChainLin ();
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, Type type)
+        {
+            new GraphNodeEmitType (this, errorAt, opcode, type).ChainLin ();
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel)
+        {
+            if (opcode == OpCodes.Leave) {
+                new GraphNodeEmitLabelLeave (this, errorAt, myLabel).ChainLin ();
+            } else {
+                new GraphNodeEmitLabel (this, errorAt, opcode, myLabel).ChainLin ();
+            }
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels)
+        {
+            new GraphNodeEmitLabels (this, errorAt, opcode, myLabels).ChainLin ();
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method)
+        {
+            if (method == null) throw new ArgumentNullException ("method");
+            new GraphNodeEmitIntMeth (this, errorAt, opcode, method).ChainLin ();
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, MethodInfo method)
+        {
+            if (method == null) throw new ArgumentNullException ("method");
+            new GraphNodeEmitExtMeth (this, errorAt, opcode, method).ChainLin ();
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor)
+        {
+            if (ctor == null) throw new ArgumentNullException ("ctor");
+            new GraphNodeEmitCtor (this, errorAt, opcode, ctor).ChainLin ();
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, double value)
+        {
+            new GraphNodeEmitDouble (this, errorAt, opcode, value).ChainLin ();
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, float value)
+        {
+            new GraphNodeEmitFloat (this, errorAt, opcode, value).ChainLin ();
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, int value)
+        {
+            new GraphNodeEmitInt (this, errorAt, opcode, value).ChainLin ();
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, string value)
+        {
+            new GraphNodeEmitString (this, errorAt, opcode, value).ChainLin ();
+        }
+
+        public void MarkLabel (ScriptMyLabel myLabel)
+        {
+            myLabel.whereAmI = new GraphNodeMarkLabel (this, myLabel);
+            myLabel.whereAmI.ChainLin ();
+        }
+
+        /**
+         * @brief Write the whole graph out to the object file.
+         */
+        public ScriptMyILGen WriteOutAll ()
+        {
+            foreach (ScriptMyLocal loc in declaredLocals) {
+                if (loc.isReferenced) wrapped.DeclareLocal (loc);
+            }
+            foreach (ScriptMyLabel lbl in definedLabels) {
+                wrapped.DefineLabel (lbl);
+            }
+            for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                gn.WriteOutOne (wrapped);
+            }
+            return wrapped;
+        }
+
+        /**
+         * @brief Perform optimizations.
+         */
+        public void Optimize ()
+        {
+            if (curExcBlock != null) throw new Exception ("exception block still open");
+
+            /*
+             * If an instruction says it doesn't fall through, remove all instructions to
+             * the end of the block.
+             */
+            for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                if (!gn.CanFallThrough ()) {
+                    GraphNode nn;
+                    while (((nn = gn.nextLin) != null) && !(nn is GraphNodeBlock) && 
+                                              !(nn is GraphNodeEndExceptionBlock)) {
+                        if ((gn.nextLin = nn.nextLin) != null) {
+                            nn.nextLin.prevLin = gn;
+                        }
+                    }
+                }
+            }
+
+            /*
+             * Scan for OpCodes.Leave instructions.
+             * For each found, its target for flow analysis purposes is the beginning of the corresponding
+             * finally block.  And the end of the finally block gets a conditional branch target of the 
+             * leave instruction's target.  A leave instruction can unwind zero or more finally blocks.
+             */
+            for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                if (gn is GraphNodeEmitLabelLeave) {
+                    GraphNodeEmitLabelLeave leaveInstr = (GraphNodeEmitLabelLeave)gn;         // the leave instruction
+                    GraphNodeMarkLabel leaveTarget = leaveInstr.myLabel.whereAmI;             // label being targeted by leave
+                    GraphNodeBeginExceptionBlock leaveTargetsTryBlock =                       // try block directly enclosing leave target
+                        (leaveTarget == null) ? null : leaveTarget.tryBlock;              // ...it must not be unwound
+
+                    /*
+                     * Step through try { }s from the leave instruction towards its target looking for try { }s with finally { }s.
+                     * The leave instruction unconditionally branches to the beginning of the innermost one found.
+                     * The end of the last one found conditionally branches to the leave instruction's target.
+                     * If none found, the leave is a simple unconditional branch to its target.
+                     */
+                    GraphNodeBeginFinallyBlock innerFinallyBlock = null;
+                    for (GraphNodeBeginExceptionBlock tryBlock = leaveInstr.tryBlock;
+                         tryBlock != leaveTargetsTryBlock;
+                         tryBlock  = tryBlock.tryBlock) {
+                        if (tryBlock == null) throw new Exception ("leave target not at or outer to leave instruction");
+                        GraphNodeCatchFinallyBlock cfb = tryBlock.catchFinallyBlock;
+                        if (cfb is GraphNodeBeginFinallyBlock) {
+                            if (innerFinallyBlock == null) {
+                                leaveInstr.unwindTo = cfb;
+                            }
+                            innerFinallyBlock = (GraphNodeBeginFinallyBlock)cfb;
+                        }
+                    }
+
+                    /*
+                     * The end of the outermost finally being unwound can conditionally jump to the target of the leave instruction.
+                     * In the case of no finallies being unwound, the leave is just a simple unconditional branch.
+                     */
+                    if (innerFinallyBlock == null) {
+                        leaveInstr.unwindTo = leaveTarget;
+                    } else if (!innerFinallyBlock.leaveTargets.Contains (leaveTarget)) {
+                        innerFinallyBlock.leaveTargets.Add (leaveTarget);
+                    }
+                }
+            }
+
+            /*
+             * See which variables a particular block reads before writing.
+             * This just considers the block itself and nothing that it branches to or fallsthru to.
+             */
+            GraphNodeBlock currentBlock = null;
+            for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                if (gn is GraphNodeBlock) currentBlock = (GraphNodeBlock)gn;
+                ScriptMyLocal rdlcl = gn.ReadsLocal ();
+                if ((rdlcl != null) &&
+                    !currentBlock.localsWrittenBeforeRead.Contains (rdlcl) && 
+                    !currentBlock.localsReadBeforeWritten.Contains (rdlcl)) {
+                    currentBlock.localsReadBeforeWritten.Add (rdlcl);
+                }
+                ScriptMyLocal wrlcl = gn.WritesLocal ();
+                if ((wrlcl != null) &&
+                    !currentBlock.localsWrittenBeforeRead.Contains (wrlcl) && 
+                    !currentBlock.localsReadBeforeWritten.Contains (wrlcl)) {
+                    currentBlock.localsWrittenBeforeRead.Add (wrlcl);
+                }
+            }
+
+            /*
+             * For every block we branch to, add that blocks readables to our list of readables,
+             * because we need to have those values valid on entry to our block.  But if we write the 
+             * variable before we can possibly branch to that block, then we don't need to have it valid 
+             * on entry to our block.  So basically it looks like the branch instruction is reading 
+             * everything required by any blocks it can branch to.
+             */
+            do {
+                this.resolvedSomething = false;
+                this.resolveSequence ++;
+                this.ResolveBlock ((GraphNodeBlock)firstLin);
+            } while (this.resolvedSomething);
+
+            /*
+             * Repeat the cutting loops as long as we keep finding stuff.
+             */
+            bool didSomething;
+            do {
+                didSomething = false;
+
+                /*
+                 * Strip out ldc.i4.1/xor/ldc.i4.1/xor
+                 */
+                for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                    if (!(gn is GraphNodeEmit)) continue;
+                    GraphNodeEmit xor2 = (GraphNodeEmit)gn;
+                    if (xor2.opcode != OpCodes.Xor) continue;
+                    if (!(xor2.prevLin is GraphNodeEmit)) continue;
+                    GraphNodeEmit ld12 = (GraphNodeEmit)xor2.prevLin;
+                    if (ld12.opcode != OpCodes.Ldc_I4_1) continue;
+                    if (!(ld12.prevLin is GraphNodeEmit)) continue;
+                    GraphNodeEmit xor1 = (GraphNodeEmit)ld12.prevLin;
+                    if (xor1.opcode != OpCodes.Xor) continue;
+                    if (!(xor2.prevLin is GraphNodeEmit)) continue;
+                    GraphNodeEmit ld11 = (GraphNodeEmit)xor1.prevLin;
+                    if (ld11.opcode != OpCodes.Ldc_I4_1) continue;
+                    ld11.prevLin.nextLin = xor2.nextLin;
+                    xor2.nextLin.prevLin = ld11.prevLin;
+                    didSomething = true;
+                }
+
+                /*
+                 * Replace c{cond}/ldc.i4.1/xor/br{false,true} -> c{cond}/br{true,false}
+                 */
+                for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                    if (!(gn is GraphNodeEmit)) continue;
+                    GraphNodeEmit brft = (GraphNodeEmit)gn;
+                    if ((brft.opcode != OpCodes.Brfalse) && (brft.opcode != OpCodes.Brtrue)) continue;
+                    if (!(brft.prevLin is GraphNodeEmit)) continue;
+                    GraphNodeEmit xor = (GraphNodeEmit)brft.prevLin;
+                    if (xor.opcode != OpCodes.Xor) continue;
+                    if (!(xor.prevLin is GraphNodeEmit)) continue;
+                    GraphNodeEmit ldc = (GraphNodeEmit)xor.prevLin;
+                    if (ldc.opcode != OpCodes.Ldc_I4_1) continue;
+                    if (!(ldc.prevLin is GraphNodeEmit)) continue;
+                    GraphNodeEmit cmp = (GraphNodeEmit)ldc.prevLin;
+                    if (cmp.opcode.StackBehaviourPop  != StackBehaviour.Pop1_pop1) continue;
+                    if (cmp.opcode.StackBehaviourPush != StackBehaviour.Pushi) continue;
+                    cmp.nextLin  = brft;
+                    brft.prevLin = cmp;
+                    brft.opcode  = (brft.opcode == OpCodes.Brfalse) ? OpCodes.Brtrue : OpCodes.Brfalse;
+                    didSomething = true;
+                }
+
+                /*
+                 * Replace c{cond}/br{false,true} -> b{!,}{cond}
+                 */
+                for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                    if (!(gn is GraphNodeEmit)) continue;
+                    GraphNodeEmit brft = (GraphNodeEmit)gn;
+                    if ((brft.opcode != OpCodes.Brfalse) && (brft.opcode != OpCodes.Brtrue)) continue;
+                    if (!(brft.prevLin is GraphNodeEmit)) continue;
+                    GraphNodeEmit cmp = (GraphNodeEmit)brft.prevLin;
+                    if (cmp.opcode.StackBehaviourPop  != StackBehaviour.Pop1_pop1) continue;
+                    if (cmp.opcode.StackBehaviourPush != StackBehaviour.Pushi) continue;
+                    cmp.prevLin.nextLin = brft;
+                    brft.prevLin = cmp.prevLin;
+                    bool brtru = (brft.opcode == OpCodes.Brtrue);
+                         if (cmp.opcode == OpCodes.Ceq)    brft.opcode = brtru ? OpCodes.Beq    : OpCodes.Bne_Un;
+                    else if (cmp.opcode == OpCodes.Cgt)    brft.opcode = brtru ? OpCodes.Bgt    : OpCodes.Ble;
+                    else if (cmp.opcode == OpCodes.Cgt_Un) brft.opcode = brtru ? OpCodes.Bgt_Un : OpCodes.Ble_Un;
+                    else if (cmp.opcode == OpCodes.Clt)    brft.opcode = brtru ? OpCodes.Blt    : OpCodes.Bge;
+                    else if (cmp.opcode == OpCodes.Clt_Un) brft.opcode = brtru ? OpCodes.Blt_Un : OpCodes.Bge_Un;
+                    else throw new Exception ();
+                    didSomething = true;
+                }
+
+                /*
+                 * Replace ld{c.i4.0,null}/br{ne.un,eq} -> br{true,false}
+                 */
+                for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                    if (!(gn is GraphNodeEmit)) continue;
+                    GraphNodeEmit brcc = (GraphNodeEmit)gn;
+                    if ((brcc.opcode != OpCodes.Bne_Un) && (brcc.opcode != OpCodes.Beq)) continue;
+                    if (!(brcc.prevLin is GraphNodeEmit)) continue;
+                    GraphNodeEmit ldc0 = (GraphNodeEmit)brcc.prevLin;
+                    if ((ldc0.opcode != OpCodes.Ldc_I4_0) && (ldc0.opcode != OpCodes.Ldnull)) continue;
+                    ldc0.prevLin.nextLin = brcc;
+                    brcc.prevLin = ldc0.prevLin;
+                    brcc.opcode  = (brcc.opcode == OpCodes.Bne_Un) ? OpCodes.Brtrue : OpCodes.Brfalse;
+                    didSomething = true;
+                }
+
+                /*
+                 * Replace:
+                 *    ldloc v1
+                 *    stloc v2
+                 *    ld<anything> except ld<anything> v2
+                 *    ldloc v2
+                 *      ...v2 unreferenced hereafter
+                 * With:
+                 *    ld<anything> except ld<anything> v2
+                 *    ldloc v1
+                 */
+                for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+
+                    // check for 'ldloc v1' instruction
+                    if (!(gn is GraphNodeEmitLocal)) continue;
+                    GraphNodeEmitLocal ldlv1 = (GraphNodeEmitLocal)gn;
+                    if (ldlv1.opcode != OpCodes.Ldloc) continue;
+
+                    // check for 'stloc v2' instruction
+                    if (!(ldlv1.nextLin is GraphNodeEmitLocal)) continue;
+                    GraphNodeEmitLocal stlv2 = (GraphNodeEmitLocal)ldlv1.nextLin;
+                    if (stlv2.opcode != OpCodes.Stloc) continue;
+
+                    // check for 'ld<anything> except ld<anything> v2' instruction
+                    if (!(stlv2.nextLin is GraphNodeEmit)) continue;
+                    GraphNodeEmit ldany = (GraphNodeEmit)stlv2.nextLin;
+                    if (!ldany.opcode.ToString ().StartsWith ("ld")) continue;
+                    if ((ldany is GraphNodeEmitLocal) &&
+                        ((GraphNodeEmitLocal)ldany).myLocal == stlv2.myLocal) continue;
+
+                    // check for 'ldloc v2' instruction
+                    if (!(ldany.nextLin is GraphNodeEmitLocal)) continue;
+                    GraphNodeEmitLocal ldlv2 = (GraphNodeEmitLocal)ldany.nextLin;
+                    if (ldlv2.opcode != OpCodes.Ldloc) continue;
+                    if (ldlv2.myLocal != stlv2.myLocal) continue;
+
+                    // check that v2 is not needed after this at all
+                    if (IsLocalNeededAfterThis (ldlv2, ldlv2.myLocal)) continue;
+
+                    // make 'ld<anything>...' the first instruction
+                    ldany.prevLin = ldlv1.prevLin;
+                    ldany.prevLin.nextLin = ldany;
+
+                    // make 'ldloc v1' the second instruction
+                    ldany.nextLin = ldlv1;
+                    ldlv1.prevLin = ldany;
+
+                    // and make 'ldloc v1' the last instruction
+                    ldlv1.nextLin = ldlv2.nextLin;
+                    ldlv1.nextLin.prevLin = ldlv1;
+
+                    didSomething = true;
+                }
+
+                /*
+                 * Remove all the stloc/ldloc that are back-to-back without the local
+                 * being needed afterwards.  If it is needed afterwards, replace the 
+                 * stloc/ldloc with dup/stloc.
+                 */
+                for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                    if ((gn is GraphNodeEmitLocal) && 
+                        (gn.prevLin is GraphNodeEmitLocal)) {
+                        GraphNodeEmitLocal stloc = (GraphNodeEmitLocal)gn.prevLin;
+                        GraphNodeEmitLocal ldloc = (GraphNodeEmitLocal)gn;
+                        if ((stloc.opcode  == OpCodes.Stloc) && 
+                            (ldloc.opcode  == OpCodes.Ldloc) && 
+                            (stloc.myLocal == ldloc.myLocal)) {
+                            if (IsLocalNeededAfterThis (ldloc, ldloc.myLocal)) {
+                                GraphNodeEmitNull dup = new GraphNodeEmitNull (this, stloc.errorAt, OpCodes.Dup);
+                                dup.nextLin   = stloc;
+                                dup.prevLin   = stloc.prevLin;
+                                stloc.nextLin = ldloc.nextLin;
+                                stloc.prevLin = dup;
+                                dup.prevLin.nextLin   = dup;
+                                stloc.nextLin.prevLin = stloc;
+                                gn = stloc;
+                            } else {
+                                stloc.prevLin.nextLin = ldloc.nextLin;
+                                ldloc.nextLin.prevLin = stloc.prevLin;
+                                gn = stloc.prevLin;
+                            }
+                            didSomething = true;
+                        }
+                    }
+                }
+
+                /*
+                 * Remove all write-only local variables, ie, those with no ldloc[a] references.
+                 * Replace any stloc instructions with pops.
+                 */
+                for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                    ScriptMyLocal rdlcl = gn.ReadsLocal ();
+                    if (rdlcl != null) rdlcl.isReferenced = true;
+                }
+                for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                    ScriptMyLocal wrlcl = gn.WritesLocal ();
+                    if ((wrlcl != null) && !wrlcl.isReferenced) {
+                        if (!(gn is GraphNodeEmitLocal) || (((GraphNodeEmitLocal)gn).opcode != OpCodes.Stloc)) {
+                            throw new Exception ("expecting stloc");
+                        }
+                        GraphNodeEmitNull pop = new GraphNodeEmitNull (this, ((GraphNodeEmit)gn).errorAt, OpCodes.Pop);
+                        pop.nextLin = gn.nextLin;
+                        pop.prevLin = gn.prevLin;
+                        gn.nextLin.prevLin = pop;
+                        gn.prevLin.nextLin = pop;
+                        gn = pop;
+                        didSomething = true;
+                    }
+                }
+
+                /*
+                 * Remove any Ld<const>/Dup,Pop.
+                 */
+                for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                    if ((gn is GraphNodeEmit) && 
+                        (gn.nextLin is GraphNodeEmit)) {
+                        GraphNodeEmit gne = (GraphNodeEmit)gn;
+                        GraphNodeEmit nne = (GraphNodeEmit)gn.nextLin;
+                        if (gne.isPoppable && (nne.opcode == OpCodes.Pop)) {
+                            gne.prevLin.nextLin = nne.nextLin;
+                            nne.nextLin.prevLin = gne.prevLin;
+                            gn = gne.prevLin;
+                            didSomething = true;
+                        }
+                    }
+                }
+            } while (didSomething);
+
+            /*
+             * Dump out the results.
+             */
+            if (DEBUG) {
+                Console.WriteLine ("");
+                Console.WriteLine (methName);
+                Console.WriteLine ("  resolveSequence=" + this.resolveSequence);
+
+                Console.WriteLine ("  Locals:");
+                foreach (ScriptMyLocal loc in declaredLocals) {
+                    Console.WriteLine ("    " + loc.type.Name + "  " + loc.name);
+                }
+
+                Console.WriteLine ("  Labels:");
+                foreach (ScriptMyLabel lbl in definedLabels) {
+                    Console.WriteLine ("    " + lbl.name);
+                }
+
+                Console.WriteLine ("  Code:");
+                DumpCode ();
+            }
+        }
+
+        private void DumpCode ()
+        {
+            int linSeqNos = 0;
+            for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                gn.linSeqNo = ++ linSeqNos;
+            }
+            for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
+                StringBuilder sb = new StringBuilder ();
+                gn.DebStringExt (sb);
+                Console.WriteLine (sb.ToString ());
+                if (gn is GraphNodeBlock) {
+                    GraphNodeBlock gnb = (GraphNodeBlock)gn;
+                    foreach (ScriptMyLocal lcl in gnb.localsReadBeforeWritten) {
+                        Console.WriteLine ("         reads " + lcl.name);
+                    }
+                }
+            }
+        }
+
+        /**
+         * @brief Scan the given block for branches to other blocks.
+         *        For any locals read by those blocks, mark them as being read by this block, 
+         *        provided this block has not written them by that point.  This makes it look 
+         *        as though the branch instruction is reading all the locals needed by any 
+         *        target blocks.
+         */
+        private void ResolveBlock (GraphNodeBlock currentBlock)
+        {
+            if (currentBlock.hasBeenResolved == this.resolveSequence) return;
+
+            /*
+             * So we don't recurse forever on a backward branch.
+             */
+            currentBlock.hasBeenResolved = this.resolveSequence;
+
+            /*
+             * Assume we haven't written any locals yet.
+             */
+            List<ScriptMyLocal> localsWrittenSoFar = new List<ScriptMyLocal> ();
+
+            /*
+             * Scan through the instructions in this block.
+             */
+            for (GraphNode gn = currentBlock; gn != null;) {
+
+                /*
+                 * See if the instruction writes a local we don't know about yet.
+                 */
+                ScriptMyLocal wrlcl = gn.WritesLocal ();
+                if ((wrlcl != null) && !localsWrittenSoFar.Contains (wrlcl)) {
+                    localsWrittenSoFar.Add (wrlcl);
+                }
+
+                /*
+                 * Scan through all the possible next instructions after this.
+                 * Note that if we are in the first part of a try/catch/finally block, 
+                 * every instruction conditionally branches to the beginning of the 
+                 * second part (the catch/finally block).
+                 */
+                GraphNode nextFallthruNode = null;
+                foreach (GraphNode nn in gn.NextNodes) {
+                    if (nn is GraphNodeBlock) {
+
+                        /*
+                         * Start of a block, go through all locals needed by that block on entry.
+                         */
+                        GraphNodeBlock nextBlock = (GraphNodeBlock)nn;
+                        ResolveBlock (nextBlock);
+                        foreach (ScriptMyLocal readByNextBlock in nextBlock.localsReadBeforeWritten) {
+
+                            /*
+                             * If this block hasn't written it by now and this block doesn't already
+                             * require it on entry, say this block requires it on entry.
+                             */
+                            if (!localsWrittenSoFar.Contains (readByNextBlock) && 
+                                !currentBlock.localsReadBeforeWritten.Contains (readByNextBlock)) {
+                                currentBlock.localsReadBeforeWritten.Add (readByNextBlock);
+                                this.resolvedSomething = true;
+                            }
+                        }
+                    } else {
+
+                        /*
+                         * Not start of a block, should be normal fallthru instruction.
+                         */
+                        if (nextFallthruNode != null) throw new Exception ("more than one fallthru from " + gn.ToString ());
+                        nextFallthruNode = nn;
+                    }
+                }
+
+                /*
+                 * Process next instruction if it isn't the start of a block.
+                 */
+                if (nextFallthruNode == gn) throw new Exception ("can't fallthru to self");
+                gn = nextFallthruNode;
+            }
+        }
+
+        /**
+         * @brief Figure out whether the value in a local var is needed after the given instruction.
+         *        True if we reach the end of the program on all branches before reading it
+         *        True if we write the local var on all branches before reading it
+         *        False otherwise
+         */
+        private bool IsLocalNeededAfterThis (GraphNode node, ScriptMyLocal local)
+        {
+            do {
+                GraphNode nextFallthruNode = null;
+                foreach (GraphNode nn in node.NextNodes) {
+                    if (nn is GraphNodeBlock) {
+                        if (((GraphNodeBlock)nn).localsReadBeforeWritten.Contains (local)) {
+                            return true;
+                        }
+                    } else {
+                        nextFallthruNode = nn;
+                    }
+                }
+                node = nextFallthruNode;
+                if (node == null) return false;
+                if (node.ReadsLocal () == local) return true;
+            } while (node.WritesLocal () != local);
+            return false;
+        }
+
+        public static void PadToLength (StringBuilder sb, int len, string str)
+        {
+            int pad = len - sb.Length;
+            if (pad < 0) pad = 0;
+            sb.Append (str.PadLeft (pad));
+        }
+    }
+}

+ 1677 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompValu.cs

@@ -0,0 +1,1677 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+/**
+ * @brief Compute values used during code generation to keep track of where computed values are stored.
+ *
+ *        Conceptually holds the memory address and type of the value
+ *        such as that used for a local variable, global variable, temporary variable.
+ *        Also used for things like constants and function/method entrypoints,
+ *        they are basically treated as read-only variables.
+ *
+ *            cv.type - type of the value
+ *
+ *            cv.PushVal() - pushes the value on the CIL stack
+ *            cv.PushRef() - pushes address of the value on the CIL stack
+ *
+ *            cv.PopPre()  - gets ready to pop from the CIL stack
+ *                           ...by possibly pushing something
+ *                <push value to be popped>
+ *            cv.PushPre() - pops value from the CIL stack
+ *
+ *        If the type is a TokenTypeSDTypeDelegate, the location is callable, 
+ *        so you get these additional functions:
+ *
+ *            cv.GetRetType()  - gets function/method's return value type
+ *                               TokenTypeVoid if void
+ *                               null if not a delegate
+ *            cv.GetArgTypes() - gets array of argument types
+ *                               as seen by script level, ie, 
+ *                               does not include any hidden 'this' type
+ *            cv.GetArgSig()   - gets argument signature eg, "(integer,list)"
+ *                               null if not a delegate
+ *
+ *            cv.CallPre()     - gets ready to call the function/method
+ *                               ...by possibly pushing something
+ *                                  such as a 'this' pointer
+ *                <push call args left-to-right>
+ *            cv.CallPost()    - calls the function/method
+ */
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+
+    /**
+     * @brief Location of a value
+     *        Includes constants, expressions and temp variables.
+     */
+    public abstract class CompValu {
+        protected static readonly MethodInfo gsmdMethodInfo = 
+                typeof (XMRInstAbstract).GetMethod ("GetScriptMethodDelegate", 
+                                                    new Type[] { typeof (string), typeof (string), typeof (object) });
+
+        private static readonly MethodInfo avpmListMethInfo   = typeof (XMRInstArrays).GetMethod ("PopList",   new Type[] { typeof (int), typeof (LSL_List) });
+        private static readonly MethodInfo avpmObjectMethInfo = typeof (XMRInstArrays).GetMethod ("PopObject", new Type[] { typeof (int), typeof (object) });
+        private static readonly MethodInfo avpmStringMethInfo = typeof (XMRInstArrays).GetMethod ("PopString", new Type[] { typeof (int), typeof (string) });
+
+        public TokenType type;        // type of the value and where in the source it was used
+
+        public CompValu (TokenType type)
+        {
+            this.type = type;
+        }
+
+        public Type ToSysType()
+        {
+            return (type.ToLSLWrapType () != null) ? type.ToLSLWrapType () : type.ToSysType ();
+        }
+
+        // if a field of an XMRInstArrays array cannot be directly written,
+        // get the method that can write it
+        private static MethodInfo ArrVarPopMeth (FieldInfo fi)
+        {
+            if (fi.Name == "iarLists")   return avpmListMethInfo;
+            if (fi.Name == "iarObjects") return avpmObjectMethInfo;
+            if (fi.Name == "iarStrings") return avpmStringMethInfo;
+            return null;
+        }
+
+        // emit code to push value onto stack
+        public void PushVal (ScriptCodeGen scg, Token errorAt, TokenType stackType)
+        {
+            this.PushVal (scg, errorAt, stackType, false);
+        }
+        public void PushVal (ScriptCodeGen scg, Token errorAt, TokenType stackType, bool explicitAllowed)
+        {
+            this.PushVal (scg, errorAt);
+            TypeCast.CastTopOfStack (scg, errorAt, this.type, stackType, explicitAllowed);
+        }
+        public abstract void PushVal (ScriptCodeGen scg, Token errorAt);
+        public abstract void PushRef (ScriptCodeGen scg, Token errorAt);
+
+        // emit code to pop value from stack
+        public void PopPost (ScriptCodeGen scg, Token errorAt, TokenType stackType)
+        {
+            TypeCast.CastTopOfStack (scg, errorAt, stackType, this.type, false);
+            this.PopPost (scg, errorAt);
+        }
+        public virtual void PopPre (ScriptCodeGen scg, Token errorAt) { }  // call this before pushing value to be popped
+        public abstract void PopPost (ScriptCodeGen scg, Token errorAt);   // call this after pushing value to be popped
+
+        // return true: doing a PushVal() does not involve CheckRun()
+        //       false: otherwise
+        public virtual bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            return true;
+        }
+
+        /*
+         * These additional functions are available if the type is a delegate
+         */
+        public TokenType GetRetType ()
+        {
+            if (!(type is TokenTypeSDTypeDelegate)) return null;
+            return ((TokenTypeSDTypeDelegate)type).decl.GetRetType ();
+        }
+        public TokenType[] GetArgTypes ()
+        {
+            if (!(type is TokenTypeSDTypeDelegate)) return null;
+            return ((TokenTypeSDTypeDelegate)type).decl.GetArgTypes ();
+        }
+        public string GetArgSig ()
+        {
+            if (!(type is TokenTypeSDTypeDelegate)) return null;
+            return ((TokenTypeSDTypeDelegate)type).decl.GetArgSig ();
+        }
+
+        // These are used only if type is a delegate too
+        // - but it is a real delegate pointer in a global or local variable or a field, etc
+        //   ie, PushVal() pushes a delegate pointer
+        // - so we must have CallPre() push the delegate pointer as a 'this' for this.Invoke(...)
+        // - and CallPost() call the delegate's Invoke() method
+        // - we assume the target function is non-trivial so we always use a call label
+        public virtual void CallPre (ScriptCodeGen scg, Token errorAt)   // call this before pushing arguments
+        {
+            new ScriptCodeGen.CallLabel (scg, errorAt);
+            this.PushVal (scg, errorAt);
+        }
+        public virtual void CallPost (ScriptCodeGen scg, Token errorAt)  // call this after pushing arguments
+        {
+            TokenTypeSDTypeDelegate ttd = (TokenTypeSDTypeDelegate)type;
+            MethodInfo invokeMethodInfo = ttd.decl.GetInvokerInfo ();
+            scg.ilGen.Emit (errorAt, OpCodes.Callvirt, invokeMethodInfo);
+            scg.openCallLabel = null;
+        }
+
+        /*
+         * Utilities used by CompValuGlobalVar and CompValuInstField
+         * where the value is located in a type-dependent array.
+         */
+        protected void EmitFieldPushVal (ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldfld, var.vTableArray);   // which array
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, var.vTableIndex);  // which array element
+            if (type is TokenTypeFloat) {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldelem_R8);
+            } else if (type is TokenTypeInt) {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldelem_I4);
+            } else if (type is TokenTypeSDTypeDelegate) {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (object));
+                scg.ilGen.Emit (errorAt, OpCodes.Castclass, ToSysType ());
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldelem, ToSysType ());
+            }
+        }
+
+        protected void EmitFieldPushRef (ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
+        {
+            if (ArrVarPopMeth (var.vTableArray) != null) {
+                scg.ErrorMsg (errorAt, "can't take address of this variable");
+            }
+            scg.ilGen.Emit (errorAt, OpCodes.Ldfld, var.vTableArray);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, var.vTableIndex);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldelema, ToSysType());
+        }
+
+        protected void EmitFieldPopPre (ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
+        {
+            if (ArrVarPopMeth (var.vTableArray) != null) {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, var.vTableIndex);
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, var.vTableArray);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, var.vTableIndex);
+            }
+        }
+
+        protected void EmitFieldPopPost (ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
+        {
+            if (ArrVarPopMeth (var.vTableArray) != null) {
+                scg.ilGen.Emit (errorAt, OpCodes.Call, ArrVarPopMeth (var.vTableArray));
+            } else if (type is TokenTypeFloat) {
+                scg.ilGen.Emit (errorAt, OpCodes.Stelem_R8);
+            } else if (type is TokenTypeInt) {
+                scg.ilGen.Emit (errorAt, OpCodes.Stelem_I4);
+            } else if (type is TokenTypeSDTypeDelegate) {
+                scg.ilGen.Emit (errorAt, OpCodes.Stelem, typeof (object));
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Stelem, ToSysType ());
+            }
+        }
+
+        /**
+         * @brief With value pushed on stack, emit code to set a property by calling its setter() method.
+         * @param scg = which script is being compiled
+         * @param errorAt = for error messages
+         * @param type = property type
+         * @param setProp = setter() method
+         */
+        protected void EmitPopPostProp (ScriptCodeGen scg, Token errorAt, TokenType type, CompValu setProp)
+        {
+            ScriptMyLocal temp = scg.ilGen.DeclareLocal (type.ToSysType (), "__spr_" + errorAt.Unique);
+            scg.ilGen.Emit   (errorAt, OpCodes.Stloc, temp);
+            setProp.CallPre  (scg, errorAt);
+            scg.ilGen.Emit   (errorAt, OpCodes.Ldloc, temp);
+            setProp.CallPost (scg, errorAt);
+        }
+    }
+
+    // The value is kept in an (XMR_Array) array element
+    public class CompValuArEle : CompValu {
+        public  CompValu arr;
+        private CompValu idx;
+        private TokenTypeObject tto;
+
+        private static readonly MethodInfo getByKeyMethodInfo = typeof (XMR_Array).GetMethod ("GetByKey", 
+                                                                                              new Type[] { typeof (object) });
+        private static readonly MethodInfo setByKeyMethodInfo = typeof (XMR_Array).GetMethod ("SetByKey", 
+                                                                                              new Type[] { typeof (object),
+                                                                                                           typeof (object) });
+
+        // type = TokenTypeObject always, as our array elements are always of type 'object'
+        // arr  = where the array object itself is stored
+        // idx  = where the index value is stored
+        public CompValuArEle (TokenType type, CompValu arr, CompValu idx) : base (type)
+        {
+            this.arr = arr;
+            this.idx = idx;
+            this.tto = new TokenTypeObject (this.type);
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            arr.PushVal (scg, errorAt);   // array
+            idx.PushVal (scg, errorAt, this.tto);  // key
+            scg.ilGen.Emit (errorAt, OpCodes.Call, getByKeyMethodInfo);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "array element not allowed here");
+            scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
+        }
+        public override void PopPre (ScriptCodeGen scg, Token errorAt)
+        {
+            arr.PushVal (scg, errorAt);            // array
+            idx.PushVal (scg, errorAt, this.tto);  // key
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, setByKeyMethodInfo);
+        }
+
+        // non-trivial because it needs to be copied into a temp
+        // in case the idiot does dumb-ass side effects tricks
+        //   eg,  (x = 0) + x + 2
+        //   should read old value of x not 0
+        // but if 'xmroption norighttoleft;' in effect,
+        // we can read it in any order so reading an
+        // XMR_Array element is trivial
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            return readAt.nr2l;
+        }
+    }
+
+    // The value is kept in the current function's argument list
+    public class CompValuArg : CompValu {
+        public int index;
+        public bool readOnly;
+
+        private static OpCode[] ldargs = { OpCodes.Ldarg_0, OpCodes.Ldarg_1, 
+                                           OpCodes.Ldarg_2, OpCodes.Ldarg_3 };
+
+        public CompValuArg (TokenType type, int index) : base (type)
+        {
+            this.index = index;
+        }
+        public CompValuArg (TokenType type, int index, bool ro) : base (type)
+        {
+            this.index = index;
+            this.readOnly = ro;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+                 if (index < ldargs.Length) scg.ilGen.Emit (errorAt, ldargs[index]);
+            else if (index <= 255) scg.ilGen.Emit (errorAt, OpCodes.Ldarg_S, index);
+                                else scg.ilGen.Emit (errorAt, OpCodes.Ldarg, index);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            if (readOnly) {
+                scg.ErrorMsg (errorAt, "location cannot be written to");
+            }
+            if (index <= 255) scg.ilGen.Emit (errorAt, OpCodes.Ldarga_S, index);
+                           else scg.ilGen.Emit (errorAt, OpCodes.Ldarga, index);
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            if (readOnly) {
+                scg.ErrorMsg (errorAt, "location cannot be written to");
+            }
+            scg.ilGen.Emit (errorAt, OpCodes.Starg, index);
+        }
+
+        // non-trivial because it needs to be copied into a temp
+        // in case the idiot does dumb-ass side effects tricks
+        //   eg,  (x = 0) + x + 2
+        //   should read old value of x not 0
+        // but if 'xmroption norighttoleft;' in effect,
+        // we can read it in any order so reading an
+        // argument is trivial
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            return readAt.nr2l;
+        }
+    }
+
+    // The value is a character constant
+    public class CompValuChar : CompValu {
+        public char x;
+
+        public CompValuChar (TokenType type, char x) : base (type)
+        {
+            if (!(this.type is TokenTypeChar)) {
+                this.type = new TokenTypeChar (this.type);
+            }
+            this.x = x;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, (int)x);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get constant's address");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot store into contant");
+        }
+    }
+
+    // The value is kept in a struct/class field of an internal struct/class
+    public class CompValuField : CompValu {
+        CompValu obj;
+        FieldInfo field;
+
+        public CompValuField (TokenType type, CompValu obj, FieldInfo field) : base (type)
+        {
+            this.obj   = obj;
+            this.field = field;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            if (field.ReflectedType.IsValueType) {
+                obj.PushRef (scg, errorAt);
+            } else {
+                obj.PushVal (scg, errorAt);
+            }
+            scg.ilGen.Emit (errorAt, OpCodes.Ldfld, field);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            if (field.ReflectedType.IsValueType) {
+                obj.PushRef (scg, errorAt);
+            } else {
+                obj.PushVal (scg, errorAt);
+            }
+            scg.ilGen.Emit (errorAt, OpCodes.Ldflda, field);
+        }
+        public override void PopPre (ScriptCodeGen scg, Token errorAt)
+        {
+            if (field.ReflectedType.IsValueType) {
+                obj.PushRef (scg, errorAt);
+            } else {
+                obj.PushVal (scg, errorAt);
+            }
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Stfld, field);
+        }
+
+        // non-trivial because it needs to be copied into a temp
+        // in case the idiot does dumb-ass side effects tricks
+        //   eg,  (x = 0) + x + 2
+        //   should read old value of x not 0
+        // but if 'xmroption norighttoleft;' in effect,
+        // we can read it in any order so reading an
+        // field of a class/struct is trivial
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            return readAt.nr2l;
+        }
+    }
+
+    // Accessing an element of a fixed-dimension array
+    public class CompValuFixArEl : CompValu {
+        private CompValu   baseRVal;
+        private CompValu[] subRVals;
+
+        private int nSubs;
+        private TokenDeclVar getFunc;
+        private TokenDeclVar setFunc;
+        private TokenTypeInt tokenTypeInt;
+
+        /**
+         * @brief Set up to access an element of an array.
+         * @param scg = what script we are compiling
+         * @param baseRVal = what array we are accessing
+         * @param subRVals = the subscripts being applied
+         */
+        public CompValuFixArEl (ScriptCodeGen scg, CompValu baseRVal, CompValu[] subRVals) : base (GetElementType (scg, baseRVal, subRVals))
+        {
+            this.baseRVal = baseRVal;  // location of the array itself
+            this.subRVals = subRVals;  // subscript values
+            this.nSubs    = subRVals.Length;
+
+            TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseRVal.type;
+            TokenDeclSDTypeClass sdtDecl = sdtType.decl;
+            tokenTypeInt = new TokenTypeInt (sdtType);
+
+            TokenName name = new TokenName (sdtType, "Get");
+            TokenType[] argsig = new TokenType[nSubs];
+            for (int i = 0; i < nSubs; i ++) {
+                argsig[i] = tokenTypeInt;
+            }
+            getFunc = scg.FindThisMember (sdtDecl, name, argsig);
+
+            name = new TokenName (sdtType, "Set");
+            argsig = new TokenType[nSubs+1];
+            for (int i = 0; i < nSubs; i ++) {
+                argsig[i] = tokenTypeInt;
+            }
+            argsig[nSubs] = getFunc.retType;
+            setFunc = scg.FindThisMember (sdtDecl, name, argsig);
+        }
+
+        /**
+         * @brief Read array element and push value on stack.
+         */
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            // call script-defined class' Get() method to fetch the value
+            baseRVal.PushVal (scg, errorAt);
+            for (int i = 0; i < nSubs; i ++) {
+                subRVals[i].PushVal (scg, errorAt, tokenTypeInt);
+            }
+            scg.ilGen.Emit (errorAt, OpCodes.Call, getFunc.ilGen);
+        }
+
+        /**
+         * @brief Push address of array element on stack.
+         */
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("tu stOOpid to get array element address");
+        }
+
+        /**
+         * @brief Prepare to write array element.
+         */
+        public override void PopPre (ScriptCodeGen scg, Token errorAt)
+        {
+            // set up call to script-defined class' Set() method to write the value
+            baseRVal.PushVal (scg, errorAt);
+            for (int i = 0; i < nSubs; i ++) {
+                subRVals[i].PushVal (scg, errorAt, tokenTypeInt);
+            }
+        }
+
+        /**
+         * @brief Pop value from stack and write array element.
+         */
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            // call script-defined class' Set() method to write the value
+            scg.ilGen.Emit (errorAt, OpCodes.Call, setFunc.ilGen);
+        }
+
+        /**
+         * @brief Get the array element type by getting the Get() functions return type.
+         *        Crude but effective.
+         * @param scg = what script we are compiling
+         * @param baseRVal = what array we are accessing
+         * @param subRVals = the subscripts being applied
+         * @returns array element type
+         */
+        private static TokenType GetElementType (ScriptCodeGen scg, CompValu baseRVal, CompValu[] subRVals)
+        {
+            TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseRVal.type;
+            TokenDeclSDTypeClass sdtDecl = sdtType.decl;
+            TokenName name = new TokenName (sdtType, "Get");
+            int nSubs = subRVals.Length;
+            TokenType[] argsig = new TokenType[nSubs];
+            argsig[0] = new TokenTypeInt (sdtType);
+            for (int i = 0; ++ i < nSubs;) {
+                argsig[i] = argsig[0];
+            }
+            TokenDeclVar getFunc = scg.FindThisMember (sdtDecl, name, argsig);
+            return getFunc.retType;
+        }
+
+        // non-trivial because it needs to be copied into a temp
+        // in case the idiot does dumb-ass side effects tricks
+        //   eg,  (x = 0) + x + 2
+        //   should read old value of x not 0
+        // but if 'xmroption norighttoleft;' in effect,
+        // we can read it in any order so reading an
+        // fixed-dimension array element is trivial
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            return readAt.nr2l;
+        }
+    }
+
+    // The value is a float constant
+    public class CompValuFloat : CompValu {
+        public double x;
+
+        public CompValuFloat (TokenType type, double x) : base (type)
+        {
+            if (!(this.type is TokenTypeFloat)) {
+                this.type = new TokenTypeFloat (this.type);
+            }
+            this.x = x;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, x);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get constant's address");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot store into constant");
+        }
+    }
+
+    // The value is the entrypoint of a script-defined global function.
+    // These are also used for script-defined type static methods as the calling convention is the same,
+    // ie, the XMRInstance pointer is a hidden first argument.
+    // There is just one of these created when the function is being compiled as there is only one value
+    // of the function.
+    public class CompValuGlobalMeth : CompValu {
+        private TokenDeclVar func;
+
+        public CompValuGlobalMeth (TokenDeclVar declFunc) : base (declFunc.GetDelType ())
+        {
+            this.func = declFunc;
+        }
+
+        /**
+         * @brief PushVal for a function/method means push a delegate on the stack.
+         *        We build a call to the DynamicMethod's CreateDelegate() function 
+         *        to create the delegate.  Slip the scriptinstance pointer as the 
+         *        function's arg 0 so it will get passed to the function when called.
+         */
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            string dtn = type.ToString ();
+            if (dtn.StartsWith ("delegate ")) dtn = dtn.Substring (9);
+
+            // delegateinstance = (signature)scriptinstance.GetScriptMethodDelegate (methName, signature, arg0);
+            //   where methName = [<sdtclass>.]<methname>(<argtypes>)
+            //        signature = <rettype>(<argtypes>)
+            //             arg0 = scriptinstance (XMRInstance)
+            scg.PushXMRInst ();                                     // [0] scriptinstance
+            scg.ilGen.Emit (errorAt, OpCodes.Ldstr, func.ilGen.methName);    // [1] method name
+            scg.ilGen.Emit (errorAt, OpCodes.Ldstr, dtn);                    // [2] delegate type name
+            scg.PushXMRInst ();                                     // [3] scriptinstance
+            scg.ilGen.Emit (errorAt, OpCodes.Callvirt, gsmdMethodInfo);      // [0] delegate instance
+            scg.ilGen.Emit (errorAt, OpCodes.Castclass, type.ToSysType ());  // [0] cast to correct delegate class
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get ref to global method");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot store into global method");
+        }
+
+        /**
+         * @brief A direct call is much simpler than pushing a delegate.
+         *        Just push the XMRInstance pointer, push the args and finally call the function.
+         */
+        public override void CallPre (ScriptCodeGen scg, Token errorAt)
+        {
+            if (!this.func.IsFuncTrivial (scg)) new ScriptCodeGen.CallLabel (scg, errorAt);
+
+            // all script-defined global functions are static methods created by DynamicMethod()
+            // and the first argument is always the XMR_Instance pointer
+            scg.PushXMRInst ();
+        }
+        public override void CallPost (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, func.ilGen);
+            if (!this.func.IsFuncTrivial (scg)) scg.openCallLabel = null;
+        }
+    }
+
+    // The value is in a script-global variable = ScriptModule instance variable
+    // It could also be a script-global property
+    public class CompValuGlobalVar : CompValu {
+        private static readonly FieldInfo glblVarsFieldInfo = typeof (XMRInstAbstract).GetField ("glblVars");
+
+        private TokenDeclVar declVar;
+
+        public CompValuGlobalVar (TokenDeclVar declVar, XMRInstArSizes glblSizes) : base (declVar.type)
+        {
+            this.declVar = declVar;
+            if ((declVar.getProp == null) && (declVar.setProp == null)) {
+                declVar.type.AssignVarSlot (declVar, glblSizes);
+            }
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            if ((declVar.getProp == null) && (declVar.setProp == null)) {
+                scg.PushXMRInst ();
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, glblVarsFieldInfo);
+                EmitFieldPushVal (scg, errorAt, declVar);
+            } else if (declVar.getProp != null) {
+                declVar.getProp.location.CallPre  (scg, errorAt);
+                declVar.getProp.location.CallPost (scg, errorAt);
+            } else {
+                scg.ErrorMsg (errorAt, "property not readable");
+                scg.PushDefaultValue (declVar.type);
+            }
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            if ((declVar.getProp == null) && (declVar.setProp == null)) {
+                scg.PushXMRInst ();
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, glblVarsFieldInfo);
+                EmitFieldPushRef (scg, errorAt, declVar);
+            } else {
+                scg.ErrorMsg (errorAt, "cannot get address of property");
+            }
+        }
+        public override void PopPre (ScriptCodeGen scg, Token errorAt)
+        {
+            if ((declVar.getProp == null) && (declVar.setProp == null)) {
+                scg.PushXMRInst ();
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, glblVarsFieldInfo);
+                EmitFieldPopPre (scg, errorAt, declVar);
+            } else if (declVar.setProp == null) {
+                scg.ErrorMsg (errorAt, "property not writable");
+            }
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            if ((declVar.getProp == null) && (declVar.setProp == null)) {
+                EmitFieldPopPost (scg, errorAt, declVar);
+            } else if (declVar.setProp != null) {
+                EmitPopPostProp (scg, errorAt, declVar.type, declVar.setProp.location);
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Pop);
+            }
+        }
+
+        // non-trivial because it needs to be copied into a temp
+        // in case the idiot does dumb-ass side effects tricks
+        //   eg,  (x = 0) + x + 2
+        //   should read old value of x not 0
+        // but if 'xmroption norighttoleft;' in effect,
+        // we can read it in any order so reading an
+        // global variable is trivial provided it is 
+        // not a property or the property function is
+        // trivial.
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            return readAt.nr2l && ((declVar.getProp == null) || declVar.getProp.IsFuncTrivial (scg));
+        }
+    }
+
+    // The value is in an $idxprop property of a script-defined type class or interface instance.
+    // Reading and writing is via a method call.
+    public class CompValuIdxProp : CompValu {
+        private TokenDeclVar idxProp;  // $idxprop property within baseRVal
+        private CompValu baseRVal;     // pointer to class or interface object containing property
+        private TokenType[] argTypes;  // argument types as required by $idxprop declaration
+        private CompValu[] indices;    // actual index values to pass to getter/setter method
+        private CompValu setProp;      // location of setter method
+
+        public CompValuIdxProp (TokenDeclVar idxProp, CompValu baseRVal, TokenType[] argTypes, CompValu[] indices) : base (idxProp.type)
+        {           
+            this.idxProp  = idxProp;
+            this.baseRVal = baseRVal;
+            this.argTypes = argTypes;
+            this.indices  = indices;
+        }
+
+        /**
+         * @brief Pushing the property's value is a matter of calling the getter method
+         *        with the supplied argument list as is.
+         */
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            if (idxProp.getProp != null) {
+                if (!idxProp.getProp.IsFuncTrivial (scg)) {
+                    for (int i = indices.Length; -- i >= 0;) {
+                        indices[i] = scg.Trivialize (indices[i], errorAt);
+                    }
+                }
+                CompValu getProp = GetIdxPropMeth (idxProp.getProp);
+                getProp.CallPre (scg, errorAt);
+                for (int i = 0; i < indices.Length; i ++) {
+                    indices[i].PushVal (scg, errorAt, argTypes[i]);
+                }
+                getProp.CallPost (scg, errorAt);
+            } else {
+                // write-only property
+                scg.ErrorMsg (errorAt, "member not readable");
+                scg.PushDefaultValue (idxProp.type);
+            }
+        }
+
+        /**
+         * @brief A property does not have a memory address.
+         */
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "member has no address");
+            scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
+        }
+
+        /**
+         * @brief Preparing to write a property consists of preparing to call the setter method
+         *        then pushing the index arguments.
+         */
+        public override void PopPre (ScriptCodeGen scg, Token errorAt)
+        {
+            if (idxProp.setProp != null) {
+                if (!idxProp.setProp.IsFuncTrivial (scg)) {
+                    for (int i = indices.Length; -- i >= 0;) {
+                        indices[i] = scg.Trivialize (indices[i], errorAt);
+                    }
+                }
+                this.setProp = GetIdxPropMeth (idxProp.setProp);
+                this.setProp.CallPre (scg, errorAt);
+                for (int i = 0; i < indices.Length; i ++) {
+                    indices[i].PushVal (scg, errorAt, argTypes[i]);
+                }
+            } else {
+                // read-only property
+                scg.ErrorMsg (errorAt, "member not writable");
+            }
+        }
+
+        /**
+         * @brief Finishing writing a property consists of finishing the call to the setter method
+         *        now that the value to be written has been pushed by our caller.
+         */
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            if (idxProp.setProp != null) {
+                this.setProp.CallPost (scg, errorAt);
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Pop);
+            }
+        }
+
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            // if no getter, reading would throw an error, so doesn't really matter what we say
+            if (idxProp.getProp == null) return true;
+
+            // assume interface methods are always non-trivial because we don't know anything about the actual implementation
+            if (baseRVal.type is TokenTypeSDTypeInterface) return false;
+
+            // accessing it in any way can't be trivial if reading the pointer isn't trivial
+            if (!baseRVal.IsReadTrivial (scg, readAt)) return false;
+
+            // likewise with the indices
+            foreach (CompValu idx in indices) {
+                if (!idx.IsReadTrivial (scg, readAt)) return false;
+            }
+
+            // now the only way it can be non-trivial to read is if the getter() method itself is non-trivial.
+            return idxProp.getProp.IsFuncTrivial (scg);
+        }
+
+        /**
+         * @brief Get how to call the getter or setter method.
+         */
+        private CompValu GetIdxPropMeth (TokenDeclVar meth)
+        {
+            if (baseRVal.type is TokenTypeSDTypeClass) {
+                return new CompValuInstMember (meth, baseRVal, false);
+            }
+            return new CompValuIntfMember (meth, baseRVal);
+        }
+    }
+
+    // This represents the type and location of an internally-defined function
+    // that a script can call
+    public class CompValuInline : CompValu {
+        public TokenDeclInline declInline;
+
+        public CompValuInline (TokenDeclInline declInline) : base (declInline.GetDelType ())
+        {
+            this.declInline = declInline;
+        }
+
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "cannot use built-in for delegate, wrap it");
+            scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "cannot use built-in for delegate, wrap it");
+            scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
+        }
+        public override void PopPre (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "cannot use built-in for delegate, wrap it");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "cannot use built-in for delegate, wrap it");
+            scg.ilGen.Emit (errorAt, OpCodes.Pop);
+        }
+    }
+
+    // The value is the entrypoint of a script-defined type's interface method combined with
+    // the pointer used to access the method.  Thus there is one of these per call site.
+    // They also handle accessing interface properties.
+    public class CompValuIntfMember : CompValu {
+        private TokenDeclVar declVar;
+        private CompValu baseRVal;
+
+        public CompValuIntfMember (TokenDeclVar declVar, CompValu baseRVal) : base (declVar.type)
+        {
+            if (this.type == null) throw new Exception ("interface member type is null");
+            this.declVar  = declVar;   // which element of the baseRVal vector to be accessed
+            this.baseRVal = baseRVal;  // the vector of delegates implementing the interface
+        }
+
+        /**
+         * @brief Reading a method's value means getting a delegate to that method.
+         *        Reading a property's value means calling the getter method for that property.
+         */
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            if (declVar.retType != null) {
+                baseRVal.PushVal (scg, errorAt);                        // push pointer to delegate array on stack
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, declVar.vTableIndex);   // select which delegate to access
+                scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (Delegate));     // push delegate on stack
+                scg.ilGen.Emit (errorAt, OpCodes.Castclass, type.ToSysType ());  // cast to correct delegate class
+            } else if (declVar.getProp != null) {
+                CompValu getProp = new CompValuIntfMember (declVar.getProp, baseRVal);
+                getProp.CallPre  (scg, errorAt);                        // reading property, call its getter
+                getProp.CallPost (scg, errorAt);                        // ... with no arguments
+            } else {
+                scg.ErrorMsg (errorAt, "member not readable");
+                scg.PushDefaultValue (declVar.type);
+            }
+        }
+
+        /**
+         * @brief Can't get the address of either a method or a property.
+         */
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "member has no address");
+            scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
+        }
+
+        /**
+         * @brief Can't write a method.
+         *        For property, it means calling the setter method for that property.
+         */
+        public override void PopPre (ScriptCodeGen scg, Token errorAt)
+        {
+            if (declVar.setProp == null) {
+                // read-only property
+                scg.ErrorMsg (errorAt, "member not writable");
+            }
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            if (declVar.setProp != null) {
+                CompValu setProp = new CompValuIntfMember (declVar.setProp, baseRVal);
+                EmitPopPostProp (scg, errorAt, declVar.type, setProp);
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Pop);
+            }
+        }
+
+        /**
+         * @brief Reading a method (ie, it's delegate) is always trivial, it's just retrieving
+         *        an element from the delegate array that make up the interface object.
+         *
+         *        Reading a property is always non-trivial because we don't know which implementation 
+         *        the interface is pointing to, so we don't know if it's trivial or not, so assume 
+         *        the worst, ie, that it is non-trivial and might call CheckRun().
+         *
+         *        But all that assumes that locating the interface object in the first place is 
+         *        trivial, ie, baseRVal.PushVal() must not call CheckRun() either.
+         */
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            return baseRVal.IsReadTrivial (scg, readAt) && (declVar.getProp == null);
+        }
+
+        /**
+         * @brief We just defer to the default CallPre() and CallPost() methods.
+         *        They expect this.PushVal() to push a delegate to the method to be called.
+         *        If this member is a method, our PushVal() will read the correct element 
+         *        of the iTable array and push it on the stack, ready for Invoke() to be
+         *        called.  If this member is a property, the only way it can be called is 
+         *        if the property is a delegate, in which case PushVal() will retrieve the 
+         *        delegate by calling the property's getter method.
+         */
+    }
+
+    // The value is the entrypoint of an internal instance method
+    // such as XMR_Array.index()
+    public class CompValuIntInstMeth : CompValu {
+        private TokenTypeSDTypeDelegate delType;
+        private CompValu baseRVal;
+        private MethodInfo methInfo;
+
+        public CompValuIntInstMeth (TokenTypeSDTypeDelegate delType, CompValu baseRVal, MethodInfo methInfo) : base (delType)
+        {
+            this.delType  = delType;
+            this.baseRVal = baseRVal;
+            this.methInfo = methInfo;
+        }
+
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            // its value, ie, without applying the (arglist), is a delegate...
+            baseRVal.PushVal (scg, errorAt);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldftn, methInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Newobj, delType.decl.GetConstructorInfo ());
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get ref to instance method");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot store into instance method");
+        }
+
+        public override void CallPre (ScriptCodeGen scg, Token errorAt)
+        {
+            // internal instance methods are always trivial so never need a CallLabel.
+            baseRVal.PushVal (scg, errorAt);
+        }
+        public override void CallPost (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
+        }
+    }
+
+    // The value is fetched by calling an internal instance method
+    // such as XMR_Array.count
+    public class CompValuIntInstROProp : CompValu {
+        private CompValu baseRVal;
+        private MethodInfo methInfo;
+
+        public CompValuIntInstROProp (TokenType valType, CompValu baseRVal, MethodInfo methInfo) : base (valType)
+        {
+            this.baseRVal = baseRVal;
+            this.methInfo = methInfo;
+        }
+
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            baseRVal.PushVal (scg, errorAt);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "cannot get ref to read-only property");
+            scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "cannot store into read-only property");
+            scg.ilGen.Emit (errorAt, OpCodes.Pop);
+        }
+    }
+
+    // The value is in a member of a script-defined type class instance.
+    //       field: value is in one of the arrays contained within XMRSDTypeClObj.instVars
+    //      method: value is a delegate; can be called
+    //    property: reading and writing is via a method call
+    public class CompValuInstMember : CompValu {
+        private static readonly FieldInfo instVarsFieldInfo = typeof (XMRSDTypeClObj).GetField ("instVars");
+        private static readonly FieldInfo vTableFieldInfo   = typeof (XMRSDTypeClObj).GetField ("sdtcVTable");
+
+        private TokenDeclVar declVar;  // member being accessed
+        private CompValu baseRVal;     // pointer to particular object instance
+        private bool ignoreVirt;       // ignore virtual attribute; use declVar's non-virtual method/property
+
+        public CompValuInstMember (TokenDeclVar declVar, CompValu baseRVal, bool ignoreVirt) : base (declVar.type)
+        {
+            this.declVar    = declVar;
+            this.baseRVal   = baseRVal;
+            this.ignoreVirt = ignoreVirt;
+        }
+
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            if (declVar.retType != null) {
+                // a method's value, ie, without applying the (arglist), is a delegate...
+                PushValMethod (scg, errorAt);
+            } else if (declVar.vTableArray != null) {
+                // a field's value is its XMRSDTypeClObj.instVars array element
+                baseRVal.PushVal (scg, errorAt);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, instVarsFieldInfo);
+                EmitFieldPushVal (scg, errorAt, declVar);
+            } else if (declVar.getProp != null) {
+                // a property's value is calling its get method with no arguments
+                CompValu getProp = new CompValuInstMember (declVar.getProp, baseRVal, ignoreVirt);
+                getProp.CallPre  (scg, errorAt);
+                getProp.CallPost (scg, errorAt);
+            } else {
+                // write-only property
+                scg.ErrorMsg (errorAt, "member not readable");
+                scg.PushDefaultValue (declVar.type);
+            }
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            if (declVar.vTableArray != null) {
+                // a field's value is its XMRSDTypeClObj.instVars array element
+                baseRVal.PushVal (scg, errorAt);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, instVarsFieldInfo);
+                EmitFieldPushRef (scg, errorAt, declVar);
+            } else {
+                scg.ErrorMsg (errorAt, "member has no address");
+                scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
+            }
+        }
+        public override void PopPre (ScriptCodeGen scg, Token errorAt)
+        {
+            if (declVar.vTableArray != null) {
+                // a field's value is its XMRSDTypeClObj.instVars array element
+                baseRVal.PushVal (scg, errorAt);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, instVarsFieldInfo);
+                EmitFieldPopPre (scg, errorAt, declVar);
+            } else if (declVar.setProp == null) {
+                // read-only property
+                scg.ErrorMsg (errorAt, "member not writable");
+            }
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            if (declVar.vTableArray != null) {
+                EmitFieldPopPost (scg, errorAt, declVar);
+            } else if (declVar.setProp != null) {
+                CompValu setProp = new CompValuInstMember (declVar.setProp, baseRVal, ignoreVirt);
+                EmitPopPostProp (scg, errorAt, declVar.type, setProp);
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Pop);
+            }
+        }
+
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            // accessing it in any way can't be trivial if reading the pointer isn't trivial.
+            // this also handles strict right-to-left mode detection as the side-effect can
+            // only apply to the pointer (it can't change which field or method we access).
+            if (!baseRVal.IsReadTrivial (scg, readAt)) return false;
+
+            // now the only way it can be non-trivial to read is if it is a property and the 
+            // getter() method is non-trivial.  reading a method means getting a delegate 
+            // which is always trivial, and reading a simple field is always trivial, ie, no 
+            // CheckRun() call can possibly be involved.
+            if (declVar.retType != null) {
+                // a method's value, ie, without applying the (arglist), is a delegate...
+                return true;
+            }
+            if (declVar.vTableArray != null) {
+                // a field's value is its XMRSDTypeClObj.instVars array element
+                return true;
+            }
+            if (declVar.getProp != null) {
+                // a property's value is calling its get method with no arguments
+                return declVar.getProp.IsFuncTrivial (scg);
+            }
+
+            // write-only property
+            return true;
+        }
+
+        public override void CallPre (ScriptCodeGen scg, Token errorAt)
+        {
+            if (declVar.retType != null) {
+                CallPreMethod (scg, errorAt);
+            } else {
+                base.CallPre (scg, errorAt);
+            }
+        }
+        public override void CallPost (ScriptCodeGen scg, Token errorAt)
+        {
+            if (declVar.retType != null) {
+                CallPostMethod (scg, errorAt);
+            } else {
+                base.CallPost (scg, errorAt);
+            }
+        }
+
+        /**
+         * @brief A PushVal() for a method means to push a delegate for the method on the stack.
+         */
+        private void PushValMethod (ScriptCodeGen scg, Token errorAt)
+        {
+            if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) throw new Exception ("dont use for statics");
+
+            if (ignoreVirt || (declVar.vTableIndex < 0)) {
+
+                /*
+                 * Non-virtual instance method, create a delegate that references the method.
+                 */
+                string dtn = type.ToString ();
+
+                // delegateinstance = (signature)scriptinstance.GetScriptMethodDelegate (methName, signature, arg0);
+                //   where methName = <sdtclass>.<methname>(<argtypes>)
+                //        signature = <rettype>(<argtypes>)
+                //             arg0 = sdt istance (XMRSDTypeClObj) 'this' value
+                scg.PushXMRInst ();                                     // [0] scriptinstance
+                scg.ilGen.Emit (errorAt, OpCodes.Ldstr, declVar.ilGen.methName); // [1] method name
+                scg.ilGen.Emit (errorAt, OpCodes.Ldstr, dtn);                    // [2] delegate type name
+                baseRVal.PushVal (scg, errorAt);                        // [3] sdtinstance
+                scg.ilGen.Emit (errorAt, OpCodes.Callvirt, gsmdMethodInfo);      // [0] delegate instance
+                scg.ilGen.Emit (errorAt, OpCodes.Castclass, type.ToSysType ());  // [0] cast to correct delegate class
+            } else {
+
+                /*
+                 * Virtual instance method, get the delegate from the vtable.
+                 */
+                baseRVal.PushVal (scg, errorAt);                                 // 'this' selecting the instance
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, vTableFieldInfo);        // get pointer to instance's vtable array
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, declVar.vTableIndex);   // select vtable element
+                scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (Delegate));     // get delegate pointer = 'this' for 'Invoke()'
+                scg.ilGen.Emit (errorAt, OpCodes.Castclass, type.ToSysType ());  // cast to correct delegate class
+            }
+        }
+
+        private void CallPreMethod (ScriptCodeGen scg, Token errorAt)
+        {
+            if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) throw new Exception ("dont use for statics");
+
+            if (!this.declVar.IsFuncTrivial (scg)) new ScriptCodeGen.CallLabel (scg, errorAt);
+
+            if (ignoreVirt || (declVar.vTableIndex < 0)) {
+                baseRVal.PushVal (scg, errorAt);                                 // 'this' being passed directly to method
+            } else {
+                baseRVal.PushVal (scg, errorAt);                                 // 'this' selecting the instance
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, vTableFieldInfo);        // get pointer to instance's vtable array
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, declVar.vTableIndex);   // select vtable element
+                scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (Delegate));     // get delegate pointer = 'this' for 'Invoke()'
+                scg.ilGen.Emit (errorAt, OpCodes.Castclass, type.ToSysType ());  // cast to correct delegate class
+            }
+        }
+        private void CallPostMethod (ScriptCodeGen scg, Token errorAt)
+        {
+            if (ignoreVirt || (declVar.vTableIndex < 0)) {
+                // non-virt instance, just call function directly
+                scg.ilGen.Emit (errorAt, OpCodes.Call, declVar.ilGen);
+            } else {
+                // virtual, call via delegate Invoke(...) method
+                TokenTypeSDTypeDelegate ttd = (TokenTypeSDTypeDelegate)type;
+                MethodInfo invokeMethodInfo = ttd.decl.GetInvokerInfo ();
+                scg.ilGen.Emit (errorAt, OpCodes.Callvirt, invokeMethodInfo);
+            }
+
+            if (!this.declVar.IsFuncTrivial (scg)) scg.openCallLabel = null;
+        }
+    }
+
+    // The value is an integer constant
+    public class CompValuInteger : CompValu {
+        public int x;
+
+        public CompValuInteger (TokenType type, int x) : base (type)
+        {
+            if (!(this.type is TokenTypeInt)) {
+                this.type = new TokenTypeInt (this.type);
+            }
+            this.x = x;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, x);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get constant's address");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot store into constant");
+        }
+    }
+
+    // The value is an element of a list
+    public class CompValuListEl : CompValu {
+        private static readonly MethodInfo getElementFromListMethodInfo = 
+                 typeof (CompValuListEl).GetMethod ("GetElementFromList", new Type[] { typeof (LSL_List), typeof (int) });
+
+        private CompValu theList;
+        private CompValu subscript;
+
+        public CompValuListEl (TokenType type, CompValu theList, CompValu subscript) : base (type)
+        {
+            this.theList   = theList;
+            this.subscript = subscript;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            theList.PushVal (scg, errorAt, new TokenTypeList (type));
+            subscript.PushVal (scg, errorAt, new TokenTypeInt (type));
+            scg.ilGen.Emit (errorAt, OpCodes.Call, getElementFromListMethodInfo);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get list element's address");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "cannot store into list element");
+            scg.ilGen.Emit (errorAt, OpCodes.Pop);
+        }
+
+        public static object GetElementFromList (LSL_List lis, int idx)
+        {
+            object element = lis.Data[idx];
+            if (element is LSL_Float)                return TypeCast.EHArgUnwrapFloat    (element);
+            if (element is LSL_Integer)              return TypeCast.EHArgUnwrapInteger  (element);
+            if (element is LSL_String)               return TypeCast.EHArgUnwrapString   (element);
+            if (element is OpenMetaverse.Quaternion) return TypeCast.EHArgUnwrapRotation (element);
+            if (element is OpenMetaverse.Vector3)    return TypeCast.EHArgUnwrapVector   (element);
+            return element;
+        }
+    }
+
+    // The value is kept in a script-addressable local variable
+    public class CompValuLocalVar : CompValu {
+        private static int htpopseq = 0;
+
+        private ScriptMyLocal localBuilder;
+
+        public CompValuLocalVar (TokenType type, string name, ScriptCodeGen scg) : base (type)
+        {
+            if (type.ToHeapTrackerType () != null) {
+                this.localBuilder = scg.ilGen.DeclareLocal (type.ToHeapTrackerType (), name);
+                scg.PushXMRInst ();
+                scg.ilGen.Emit (type, OpCodes.Newobj, type.GetHeapTrackerCtor ());
+                scg.ilGen.Emit (type, OpCodes.Stloc, localBuilder);
+            } else {
+                this.localBuilder = scg.ilGen.DeclareLocal (ToSysType (), name);
+            }
+        }
+
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldloc, localBuilder);
+            if (type.ToHeapTrackerType () != null) {
+                scg.ilGen.Emit (errorAt, OpCodes.Call, type.GetHeapTrackerPushMeth ());
+            }
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            if (type.ToHeapTrackerType () != null) {
+                scg.ErrorMsg (errorAt, "can't take ref of heap-tracked type " + type.ToString ());
+                scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldloca, localBuilder);
+            }
+        }
+
+        public override void PopPre (ScriptCodeGen scg, Token errorAt)
+        {
+            if (type.ToHeapTrackerType () != null) {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldloc, localBuilder);
+            }
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            if (type.ToHeapTrackerType () != null) {
+                scg.ilGen.Emit (errorAt, OpCodes.Call, type.GetHeapTrackerPopMeth ());
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Stloc, localBuilder);
+            }
+        }
+
+        public void Pop (ScriptCodeGen scg, Token errorAt)
+        {
+            if (type.ToHeapTrackerType () != null) {
+                /*
+                 * Popping into a heap tracker wrapped local variable.
+                 * First pop value into a temp var, then call the heap tracker's pop method.
+                 */
+                ScriptMyLocal htpop = scg.ilGen.DeclareLocal (type.ToSysType (), "htpop$" + (++ htpopseq).ToString ());
+                scg.ilGen.Emit (errorAt, OpCodes.Stloc, htpop);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldloc, localBuilder);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldloc, htpop);
+                scg.ilGen.Emit (errorAt, OpCodes.Call,  type.GetHeapTrackerPopMeth ());
+            } else {
+
+                /*
+                 * Not a heap-tracked local var, just pop directly into it.
+                 */
+                scg.ilGen.Emit (errorAt, OpCodes.Stloc, localBuilder);
+            }
+        }
+
+        // non-trivial because it needs to be copied into a temp
+        // in case the idiot does dumb-ass side effects tricks
+        //   eg,  (x = 0) + x + 2
+        //   should read old value of x not 0
+        // but if 'xmroption norighttoleft;' in effect,
+        // we can read it in any order so reading a
+        // local variable is trivial.
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            return readAt.nr2l;
+        }
+    }
+
+    // The value is a null
+    public class CompValuNull : CompValu {
+        public CompValuNull (TokenType type) : base (type) { }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldnull);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get null's address");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot store into null");
+        }
+    }
+
+    // The value is a rotation
+    public class CompValuRot : CompValu {
+        public CompValu x;
+        public CompValu y;
+        public CompValu z;
+        public CompValu w;
+
+        private static readonly ConstructorInfo lslRotConstructorInfo = 
+                typeof (LSL_Rotation).GetConstructor (new Type[] { typeof (double), 
+                                                                   typeof (double), 
+                                                                   typeof (double), 
+                                                                   typeof (double) });
+
+        public CompValuRot (TokenType type, CompValu x, CompValu y, CompValu z, CompValu w) :
+                base (type)
+        {
+            if (!(type is TokenTypeRot)) {
+                this.type = new TokenTypeRot (type);
+            }
+            this.x = x;
+            this.y = y;
+            this.z = z;
+            this.w = w;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            this.x.PushVal (scg, errorAt, new TokenTypeFloat (this.x.type));
+            this.y.PushVal (scg, errorAt, new TokenTypeFloat (this.y.type));
+            this.z.PushVal (scg, errorAt, new TokenTypeFloat (this.z.type));
+            this.w.PushVal (scg, errorAt, new TokenTypeFloat (this.w.type));
+            scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslRotConstructorInfo);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get constant's address");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot store into constant");
+        }
+
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            // the supplied values must be trivial because when we call their PushVal()s
+            // there will be stuff on the stack for all but the first PushVal() and so
+            // they would have a non-empty stack at their call label.
+            if (!this.w.IsReadTrivial (scg, readAt) ||
+                !this.x.IsReadTrivial (scg, readAt) ||
+                !this.y.IsReadTrivial (scg, readAt) ||
+                !this.z.IsReadTrivial (scg, readAt)) {
+                throw new Exception ("rotation values must be trivial");
+            }
+
+            return true;
+        }
+    }
+
+    // The value is in a static field of an internally defined struct/class
+    public class CompValuSField : CompValu {
+        public FieldInfo field;
+
+        public CompValuSField (TokenType type, FieldInfo field) : base (type)
+        {
+            this.field = field;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            if ((field.Attributes & FieldAttributes.Literal) == 0) {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldsfld, field);
+                return;
+            }
+            if (field.FieldType == typeof (LSL_Rotation)) {
+                LSL_Rotation rot = (LSL_Rotation)field.GetValue (null);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, rot.x);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, rot.y);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, rot.z);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, rot.s);
+                scg.ilGen.Emit (errorAt, OpCodes.Newobj, ScriptCodeGen.lslRotationConstructorInfo);
+                return;
+            }
+            if (field.FieldType == typeof (LSL_Vector)) {
+                LSL_Vector vec = (LSL_Vector)field.GetValue (null);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, vec.x);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, vec.y);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_R8, vec.z);
+                scg.ilGen.Emit (errorAt, OpCodes.Newobj, ScriptCodeGen.lslRotationConstructorInfo);
+                return;
+            }
+            if (field.FieldType == typeof (string)) {
+                string str = (string)field.GetValue (null);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldstr, str);
+                return;
+            }
+            throw new Exception ("unsupported literal type " + field.FieldType.Name);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            if ((field.Attributes & FieldAttributes.Literal) != 0) {
+                throw new Exception ("can't write a constant");
+            }
+            scg.ilGen.Emit (errorAt, OpCodes.Ldflda, field);
+        }
+        public override void PopPre (ScriptCodeGen scg, Token errorAt)
+        {
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            if ((field.Attributes & FieldAttributes.Literal) != 0) {
+                throw new Exception ("can't write a constant");
+            }
+            scg.ilGen.Emit (errorAt, OpCodes.Stsfld, field);
+        }
+
+        // non-trivial because it needs to be copied into a temp
+        // in case the idiot does dumb-ass side effects tricks
+        //   eg,  (x = 0) + x + 2
+        //   should read old value of x not 0
+        // but if 'xmroption norighttoleft;' in effect,
+        // we can read it in any order so reading a
+        // local variable is trivial.
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            return readAt.nr2l;
+        }
+    }
+
+    // The value is a character within a string
+    public class CompValuStrChr : CompValu {
+        private static readonly MethodInfo getCharFromStringMethodInfo = 
+                 typeof (CompValuStrChr).GetMethod ("GetCharFromString", new Type[] { typeof (string), typeof (int) });
+
+        private CompValu theString;
+        private CompValu subscript;
+
+        public CompValuStrChr (TokenType type, CompValu theString, CompValu subscript) : base (type)
+        {
+            this.theString = theString;
+            this.subscript = subscript;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            theString.PushVal (scg, errorAt, new TokenTypeStr (type));
+            subscript.PushVal (scg, errorAt, new TokenTypeInt (type));
+            scg.ilGen.Emit (errorAt, OpCodes.Call, getCharFromStringMethodInfo);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get string character's address");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ErrorMsg (errorAt, "cannot store into string character");
+            scg.ilGen.Emit (errorAt, OpCodes.Pop);
+        }
+
+        public static char GetCharFromString (string s, int i)
+        {
+            return s[i];
+        }
+    }
+
+    // The value is a key or string constant
+    public class CompValuString : CompValu {
+        public string x;
+
+        public CompValuString (TokenType type, string x) : base (type)
+        {
+            if (!(type is TokenTypeKey) && !(this.type is TokenTypeStr)) {
+                throw new Exception ("bad type " + type.ToString ());
+            }
+            this.x = x;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldstr, x);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get constant's address");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot store into constant");
+        }
+    }
+
+    // The value is kept in a temp local variable
+    public class CompValuTemp : CompValu {
+        public ScriptMyLocal localBuilder;
+
+        public CompValuTemp (TokenType type, ScriptCodeGen scg) : base (type)
+        {
+            string name = "tmp$" + (++ scg.tempCompValuNum);
+            this.localBuilder = scg.ilGen.DeclareLocal (ToSysType(), name);
+        }
+        protected CompValuTemp (TokenType type) : base (type) { }  // CompValuVoid uses this
+
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldloc, localBuilder);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldloca, localBuilder);
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Stloc, localBuilder);
+        }
+        public void Pop (ScriptCodeGen scg, Token errorAt, TokenType stackType)
+        {
+            TypeCast.CastTopOfStack (scg, errorAt, stackType, this.type, false);
+            this.PopPost (scg, errorAt);  // in case PopPost() overridden eg by CompValuVoid
+        }
+        public void Pop (ScriptCodeGen scg, Token errorAt)
+        {
+            this.PopPost (scg, errorAt);  // in case PopPost() overridden eg by CompValuVoid
+        }
+    }
+
+    // The value is a vector
+    public class CompValuVec : CompValu {
+        public CompValu x;
+        public CompValu y;
+        public CompValu z;
+
+        private static readonly ConstructorInfo lslVecConstructorInfo = 
+                typeof (LSL_Vector).GetConstructor (new Type[] { typeof (double), 
+                                                                 typeof (double), 
+                                                                 typeof (double) });
+
+        public CompValuVec (TokenType type, CompValu x, CompValu y, CompValu z) : base (type)
+        {
+            if (!(type is TokenTypeVec)) {
+                this.type = new TokenTypeVec (type);
+            }
+            this.x = x;
+            this.y = y;
+            this.z = z;
+        }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt)
+        {
+            this.x.PushVal (scg, errorAt, new TokenTypeFloat (this.x.type));
+            this.y.PushVal (scg, errorAt, new TokenTypeFloat (this.y.type));
+            this.z.PushVal (scg, errorAt, new TokenTypeFloat (this.z.type));
+            scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslVecConstructorInfo);
+        }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get constant's address");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot store into constant");
+        }
+
+        public override bool IsReadTrivial (ScriptCodeGen scg, Token readAt)
+        {
+            // the supplied values must be trivial because when we call their PushVal()s
+            // there will be stuff on the stack for all but the first PushVal() and so
+            // they would have a non-empty stack at their call label.
+            if (!this.x.IsReadTrivial (scg, readAt) ||
+                !this.y.IsReadTrivial (scg, readAt) ||
+                !this.z.IsReadTrivial (scg, readAt)) {
+                throw new Exception ("vector values must be trivial");
+            }
+
+            return true;
+        }
+    }
+
+    // Used to indicate value will be discarded (eg, where to put return value from a call)
+    public class CompValuVoid : CompValuTemp {
+        public CompValuVoid (Token token) : base ((token is TokenTypeVoid) ? (TokenTypeVoid)token : new TokenTypeVoid (token))
+        { }
+        public override void PushVal (ScriptCodeGen scg, Token errorAt) { }
+        public override void PushRef (ScriptCodeGen scg, Token errorAt)
+        {
+            throw new Exception ("cannot get void address");
+        }
+        public override void PopPost (ScriptCodeGen scg, Token errorAt) { }
+    }
+}

+ 216 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompile.cs

@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @brief Compile a script to produce a ScriptObjCode object
+ */
+
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Reflection;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public partial class XMRInstance
+    {
+        /**
+         * @brief Compile a script to produce a ScriptObjCode object
+         * @returns object code pointer or null if compile error
+         *          also can throw compile error exception
+         */
+        public ScriptObjCode Compile ()
+        {
+            bool oldObjFile = false;
+            Stream objFileStream = null;
+            StreamWriter asmFileWriter = null;
+            string envar = null;
+            string sourceHash = null;
+            TextWriter saveSource = null;
+
+            string asmFileName = GetScriptFileName (m_ScriptObjCodeKey + ".xmrasm");
+            string lslFileName = GetScriptFileName (m_ScriptObjCodeKey + ".lsl");
+            string objFileName = GetScriptFileName (m_ScriptObjCodeKey + ".xmrobj");
+            string tmpFileName = GetScriptFileName (m_ScriptObjCodeKey + ".xmrtmp");
+
+            /*
+             * If we already have an object file, don't bother compiling.
+             */
+            if (!m_ForceRecomp && File.Exists (objFileName)) {
+                objFileStream = File.OpenRead (objFileName);
+                oldObjFile = true;
+            } else {
+
+                /*
+                 * If source file empty, try to read from asset server.
+                 */
+                if (EmptySource (m_SourceCode)) {
+                    m_SourceCode = FetchSource (m_CameFrom);
+                }
+
+                /*
+                 * Maybe write script source to a file for debugging.
+                 */
+                envar = Environment.GetEnvironmentVariable ("MMRScriptCompileSaveSource");
+                if ((envar != null) && ((envar[0] & 1) != 0)) {
+                    m_log.Debug ("[XMREngine]: MMRScriptCompileSaveSource: saving to " + lslFileName);
+                    saveSource = File.CreateText (lslFileName);
+                }
+
+                /*
+                 * Parse source string into tokens.
+                 */
+                TokenBegin tokenBegin;
+                try {
+                    tokenBegin = TokenBegin.Construct(m_CameFrom, saveSource, ErrorHandler, m_SourceCode, out sourceHash);
+                } finally {
+                    if (saveSource != null) saveSource.Close ();
+                }
+                if (tokenBegin == null) {
+                    m_log.Debug ("[XMREngine]: parsing errors on " + m_ScriptObjCodeKey);
+                    return null;
+                }
+
+                /*
+                 * Create object file one way or another.
+                 */
+                try {
+                    objFileStream = File.Create (tmpFileName);
+
+                    /*
+                     * Create abstract syntax tree from raw tokens.
+                     */
+                    TokenScript tokenScript = ScriptReduce.Reduce(tokenBegin);
+                    if (tokenScript == null) {
+                        m_log.Warn ("[XMREngine]: reduction errors on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")");
+                        PrintCompilerErrors ();
+                        return null;
+                    }
+
+                    /*
+                     * Compile abstract syntax tree to write object file.
+                     */
+                    BinaryWriter objFileWriter = new BinaryWriter (objFileStream);
+                    bool ok = ScriptCodeGen.CodeGen(tokenScript, objFileWriter, sourceHash);
+                    if (!ok) {
+                        m_log.Warn ("[XMREngine]: compile error on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")");
+                        PrintCompilerErrors ();
+                        objFileStream.Close ();
+                        return null;
+                    }
+                    objFileStream.Close ();
+
+                    /*
+                     * File has been completely written.
+                     * If there is an old one laying around, delete it now.
+                     * Then re-open the new file for reading from the beginning.
+                     */
+                    if (File.Exists (objFileName)) {
+                        File.Replace (tmpFileName, objFileName, null);
+                    } else {
+                        File.Move (tmpFileName, objFileName);
+                    }
+                    objFileStream = File.OpenRead (objFileName);
+                } finally {
+
+                    /*
+                     * In case something went wrong writing temp file, delete it.
+                     */
+                    try {
+                        File.Delete (tmpFileName);
+                    } catch {
+                    }
+                }
+
+                /*
+                 * Since we just wrote the .xmrobj file, maybe save disassembly.
+                 */
+                envar = Environment.GetEnvironmentVariable ("MMRScriptCompileSaveILGen");
+                if ((envar != null) && ((envar[0] & 1) != 0)) {
+                    m_log.Debug ("[XMREngine]: MMRScriptCompileSaveILGen: saving to " + asmFileName);
+                    asmFileWriter = File.CreateText (asmFileName);
+                }
+            }
+
+            /*
+             * Read object file to create ScriptObjCode object.
+             * Maybe also write disassembly to a file for debugging.
+             */
+            BinaryReader objFileReader = new BinaryReader (objFileStream);
+            ScriptObjCode scriptObjCode = null;
+            try {
+                scriptObjCode = new ScriptObjCode (objFileReader, asmFileWriter, null);
+                if (scriptObjCode != null) {
+                    scriptObjCode.fileDateUtc = File.GetLastWriteTimeUtc (objFileName);
+                }
+            } finally {
+                objFileReader.Close ();
+                if (asmFileWriter != null) {
+                    asmFileWriter.Flush ();
+                    asmFileWriter.Close ();
+                }
+            }
+
+            /*
+             * Maybe an old object file has reached its expiration date.
+             */
+            if (oldObjFile && (scriptObjCode != null) && scriptObjCode.IsExpired ()) {
+                m_log.Debug ("[XMREngine]: expiration reached on " + m_ScriptObjCodeKey + ", reloading");
+                m_ForceRecomp = true;
+                scriptObjCode = Compile ();
+            }
+
+            return scriptObjCode;
+        }
+
+        private void PrintCompilerErrors ()
+        {
+            m_log.Info ("[XMREngine]: - " + m_Part.GetWorldPosition () + " " + m_DescName);
+            foreach (string error in m_CompilerErrors) {
+                m_log.Info ("[XMREngine]: - " + error);
+            }
+        }
+
+        /**
+         * @brief Check for empty source, allowing for a first line of //... script engine selector.
+         */
+        public static bool EmptySource (string source)
+        {
+            int len = source.Length;
+            bool skipeol = false;
+            for (int i = 0; i < len; i ++) {
+                char c = source[i];
+                skipeol &= c != '\n';
+                skipeol |= (c == '/') && (i + 1 < len) && (source[i+1] == '/');
+                if ((c > ' ') && !skipeol) return false;
+            }
+            return true;
+        }
+    }
+}

+ 250 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptConsts.cs

@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    public class ScriptConst {
+
+        public static Dictionary<string, ScriptConst> scriptConstants = Init ();
+
+        /**
+         * @brief look up the value of a given built-in constant.
+         * @param name = name of constant
+         * @returns null: no constant by that name defined
+         *          else: pointer to ScriptConst struct
+         */
+        public static ScriptConst Lookup (string name)
+        {
+            ScriptConst sc;
+            if (!scriptConstants.TryGetValue (name, out sc)) sc = null;
+            return sc;
+        }
+
+        private static Dictionary<string, ScriptConst> Init ()
+        {
+            Dictionary<string, ScriptConst> sc = new Dictionary<string, ScriptConst> ();
+
+            /*
+             * For every event code, define XMREVENTCODE_<eventname> and XMREVENTMASKn_<eventname> symbols.
+             */
+            for (int i = 0; i < 64; i ++) {
+                try {
+                    string s = ((ScriptEventCode)i).ToString ();
+                    if ((s.Length > 0) && (s[0] >= 'a') && (s[0] <= 'z')) {
+                        new ScriptConst (sc, 
+                                         "XMREVENTCODE_" + s, 
+                                         new CompValuInteger (new TokenTypeInt (null), i));
+                        int n = i / 32 + 1;
+                        int m = 1 << (i % 32);
+                        new ScriptConst (sc, 
+                                         "XMREVENTMASK" + n + "_" + s, 
+                                         new CompValuInteger (new TokenTypeInt (null), m));
+                    }
+                } catch { }
+            }
+
+            /*
+             * Also get all the constants from XMRInstAbstract and ScriptBaseClass etc as well.
+             */
+            for (Type t = typeof (XMRInstAbstract); t != typeof (object); t = t.BaseType) {
+                AddInterfaceConstants (sc, t.GetFields ());
+            }
+
+            return sc;
+        }
+
+        /**
+         * @brief Add all constants defined by the given interface.
+         */
+        // this one accepts only upper-case named fields
+        public static void AddInterfaceConstants (Dictionary<string, ScriptConst> sc, FieldInfo[] allFields)
+        {
+            List<FieldInfo> ucfs = new List<FieldInfo> (allFields.Length);
+            foreach (FieldInfo f in allFields) {
+                string fieldName = f.Name;
+                int i;
+                for (i = fieldName.Length; -- i >= 0;) {
+                    if ("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".IndexOf (fieldName[i]) < 0) break;
+                }
+                if (i < 0) ucfs.Add (f);
+            }
+            AddInterfaceConstants (sc, ucfs.GetEnumerator ());
+        }
+
+        // this one accepts all fields given to it
+        public static void AddInterfaceConstants (Dictionary<string, ScriptConst> sc, IEnumerator<FieldInfo> fields)
+        {
+            if (sc == null) sc = scriptConstants;
+
+            for (fields.Reset (); fields.MoveNext ();) {
+                FieldInfo constField = fields.Current;
+                Type fieldType = constField.FieldType;
+                CompValu cv;
+
+                /*
+                 * The location of a simple number is the number itself.
+                 * Access to the value gets compiled as an ldc instruction.
+                 */
+                if (fieldType == typeof (double)) {
+                    cv = new CompValuFloat (new TokenTypeFloat (null),
+                                            (double)(double)constField.GetValue (null));
+                } else if (fieldType == typeof (int)) {
+                    cv = new CompValuInteger (new TokenTypeInt (null),
+                                              (int)constField.GetValue (null));
+                } else if (fieldType == typeof (LSL_Integer)) {
+                    cv = new CompValuInteger (new TokenTypeInt (null),
+                                              ((LSL_Integer)constField.GetValue (null)).value);
+                }
+
+                /*
+                 * The location of a string is the string itself.
+                 * Access to the value gets compiled as an ldstr instruction.
+                 */
+                else if (fieldType == typeof (string)) {
+                    cv = new CompValuString (new TokenTypeStr (null), 
+                                             (string)constField.GetValue (null));
+                } else if (fieldType == typeof (LSL_String)) {
+                    cv = new CompValuString (new TokenTypeStr (null), 
+                                             (string)(LSL_String)constField.GetValue (null));
+                }
+
+                /*
+                 * The location of everything else (objects) is the static field in the interface definition.
+                 * Access to the value gets compiled as an ldsfld instruction.
+                 */
+                else {
+                    cv = new CompValuSField (TokenType.FromSysType (null, fieldType), constField);
+                }
+
+                /*
+                 * Add to dictionary.
+                 */
+                new ScriptConst (sc, constField.Name, cv);
+            }
+        }
+
+        /**
+         * @brief Add arbitrary constant available to script compilation.
+         * CAUTION: These values get compiled-in to a script and must not
+         *          change over time as previously compiled scripts will
+         *          still have the old values.
+         */
+        public static ScriptConst AddConstant (string name, object value)
+        {
+            CompValu cv = null;
+
+            if (value is char) {
+                cv = new CompValuChar (new TokenTypeChar (null), (char)value);
+            }
+            if (value is double) {
+                cv = new CompValuFloat (new TokenTypeFloat (null), (double)(double)value);
+            }
+            if (value is float) {
+                cv = new CompValuFloat (new TokenTypeFloat (null), (double)(float)value);
+            }
+            if (value is int) {
+                cv = new CompValuInteger (new TokenTypeInt (null), (int)value);
+            }
+            if (value is string) {
+                cv = new CompValuString (new TokenTypeStr (null), (string)value);
+            }
+
+            if (value is LSL_Float) {
+                cv = new CompValuFloat (new TokenTypeFloat (null), (double)((LSL_Float)value).value);
+            }
+            if (value is LSL_Integer) {
+                cv = new CompValuInteger (new TokenTypeInt (null), ((LSL_Integer)value).value);
+            }
+            if (value is LSL_Rotation) {
+                LSL_Rotation r = (LSL_Rotation)value;
+                CompValu x = new CompValuFloat (new TokenTypeFloat (null), r.x);
+                CompValu y = new CompValuFloat (new TokenTypeFloat (null), r.y);
+                CompValu z = new CompValuFloat (new TokenTypeFloat (null), r.z);
+                CompValu s = new CompValuFloat (new TokenTypeFloat (null), r.s);
+                cv = new CompValuRot (new TokenTypeRot (null), x, y, z, s);
+            }
+            if (value is LSL_String) {
+                cv = new CompValuString (new TokenTypeStr (null), (string)(LSL_String)value);
+            }
+            if (value is LSL_Vector) {
+                LSL_Vector v = (LSL_Vector)value;
+                CompValu x = new CompValuFloat (new TokenTypeFloat (null), v.x);
+                CompValu y = new CompValuFloat (new TokenTypeFloat (null), v.y);
+                CompValu z = new CompValuFloat (new TokenTypeFloat (null), v.z);
+                cv = new CompValuVec (new TokenTypeVec (null), x, y, z);
+            }
+
+            if (value is OpenMetaverse.Quaternion) {
+                OpenMetaverse.Quaternion r = (OpenMetaverse.Quaternion)value;
+                CompValu x = new CompValuFloat (new TokenTypeFloat (null), r.X);
+                CompValu y = new CompValuFloat (new TokenTypeFloat (null), r.Y);
+                CompValu z = new CompValuFloat (new TokenTypeFloat (null), r.Z);
+                CompValu s = new CompValuFloat (new TokenTypeFloat (null), r.W);
+                cv = new CompValuRot (new TokenTypeRot (null), x, y, z, s);
+            }
+            if (value is OpenMetaverse.UUID) {
+                cv = new CompValuString (new TokenTypeKey (null), value.ToString ());
+            }
+            if (value is OpenMetaverse.Vector3) {
+                OpenMetaverse.Vector3 v = (OpenMetaverse.Vector3)value;
+                CompValu x = new CompValuFloat (new TokenTypeFloat (null), v.X);
+                CompValu y = new CompValuFloat (new TokenTypeFloat (null), v.Y);
+                CompValu z = new CompValuFloat (new TokenTypeFloat (null), v.Z);
+                cv = new CompValuVec (new TokenTypeVec (null), x, y, z);
+            }
+
+            if (cv == null) throw new Exception ("bad type " + value.GetType ().Name);
+            return new ScriptConst (scriptConstants, name, cv);
+        }
+
+        /*
+         * Instance variables
+         */
+        public string name;
+        public CompValu rVal;
+
+        private ScriptConst (Dictionary<string, ScriptConst> lc, string name, CompValu rVal)
+        {
+            lc.Add (name, this);
+            this.name = name;
+            this.rVal = rVal;
+        }
+    }
+}

+ 95 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptEventCode.cs

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    /**
+     * @brief List of event codes that can be passed to StartEventHandler().
+     *        Must have same name as corresponding event handler name, so
+     *        the compiler will know what column in the seht to put the
+     *        event handler entrypoint in.
+     *
+     *        Also, ScriptConst.Init() builds symbols of name XMREVENTCODE_<name>
+     *        and XMREVENTMASK<n>_<name> with the values and masks of all symbols
+     *        in range 0..63 that begin with a lower-case letter for scripts to
+     *        reference.
+     */
+    public enum ScriptEventCode : int {
+
+        // used by XMRInstance to indicate no event being processed
+        None                 = -1,
+
+        // must be bit numbers of equivalent values in ...
+        // OpenSim.Region.ScriptEngine.Shared.ScriptBase.scriptEvents
+        // ... so they can be passed to m_Part.SetScriptEvents().
+        attach               =  0,
+        state_exit           =  1,
+        timer                =  2,
+        touch                =  3,
+        collision            =  4,
+        collision_end        =  5,
+        collision_start      =  6,
+        control              =  7,
+        dataserver           =  8,
+        email                =  9,
+        http_response        = 10,
+        land_collision       = 11,
+        land_collision_end   = 12,
+        land_collision_start = 13,
+        at_target            = 14,
+        listen               = 15,
+        money                = 16,
+        moving_end           = 17,
+        moving_start         = 18,
+        not_at_rot_target    = 19,
+        not_at_target        = 20,
+        touch_start          = 21,
+        object_rez           = 22,
+        remote_data          = 23,
+        at_rot_target        = 24,
+        transaction_result   = 25,
+        run_time_permissions = 28,
+        touch_end            = 29,
+        state_entry          = 30,
+
+        // events not passed to m_Part.SetScriptEvents().
+        changed              = 33,
+        link_message         = 34,
+        no_sensor            = 35,
+        on_rez               = 36,
+        sensor               = 37,
+        http_request         = 38,
+
+        path_update          = 40,
+
+        // XMRE specific
+        region_cross         = 63,
+
+        // marks highest numbered event, ie, number of columns in seht.
+        Size                 = 64
+    }
+}

+ 664 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs

@@ -0,0 +1,664 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+/**
+ * @brief Generate code for the backend API calls.
+ */
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public abstract class TokenDeclInline : TokenDeclVar {
+        public static VarDict inlineFunctions = CreateDictionary ();
+
+        public abstract void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args);
+
+        private static string[] noCheckRuns;
+        private static string[] keyReturns;
+
+        protected bool isTaggedCallsCheckRun;
+
+        /**
+         * @brief Create a dictionary of inline backend API functions.
+         */
+        private static VarDict CreateDictionary ()
+        {
+            /*
+             * For those listed in noCheckRun, we just generate the call (simple computations).
+             * For all others, we generate the call then a call to CheckRun().
+             */
+            noCheckRuns = new string[] {
+                "llBase64ToString",
+                "llCSV2List",
+                "llDeleteSubList",
+                "llDeleteSubString",
+                "llDumpList2String",
+                "llEscapeURL",
+                "llEuler2Rot",
+                "llGetListEntryType",
+                "llGetListLength",
+                "llGetSubString",
+                "llGetUnixTime",
+                "llInsertString",
+                "llList2CSV",
+                "llList2Float",
+                "llList2Integer",
+                "llList2Key",
+                "llList2List",
+                "llList2ListStrided",
+                "llList2Rot",
+                "llList2String",
+                "llList2Vector",
+                "llListFindList",
+                "llListInsertList",
+                "llListRandomize",
+                "llListReplaceList",
+                "llListSort",
+                "llListStatistics",
+                "llMD5String",
+                "llParseString2List",
+                "llParseStringKeepNulls",
+                "llRot2Euler",
+                "llStringLength",
+                "llStringToBase64",
+                "llStringTrim",
+                "llSubStringIndex",
+                "llUnescapeURL"
+            };
+
+            /*
+             * These functions really return a 'key' even though we see them as
+             * returning 'string' because OpenSim has key and string as same type.
+             */
+            keyReturns = new string[] {
+                "llAvatarOnLinkSitTarget",
+                "llAvatarOnSitTarget",
+                "llDetectedKey",
+                "llDetectedOwner",
+                "llGenerateKey",
+                "llGetCreator",
+                "llGetInventoryCreator",
+                "llGetInventoryKey",
+                "llGetKey",
+                "llGetLandOwnerAt",
+                "llGetLinkKey",
+                "llGetNotecardLine",
+                "llGetNumberOfNotecardLines",
+                "llGetOwner",
+                "llGetOwnerKey",
+                "llGetPermissionsKey",
+                "llHTTPRequest",
+                "llList2Key",
+                "llRequestAgentData",
+                "llRequestDisplayName",
+                "llRequestInventoryData",
+                "llRequestSecureURL",
+                "llRequestSimulatorData",
+                "llRequestURL",
+                "llRequestUsername",
+                "llSendRemoteData",
+                "llTransferLindenDollars"
+            };
+
+            VarDict ifd = new VarDict (false);
+
+            Type[] oneDoub  = new Type[] { typeof (double) };
+            Type[] twoDoubs = new Type[] { typeof (double), typeof (double) };
+
+            /*
+             * Mono generates an FPU instruction for many math calls.
+             */
+            new TokenDeclInline_LLAbs   (ifd);
+            new TokenDeclInline_Math    (ifd, "llAcos(float)",        "Acos",  oneDoub);
+            new TokenDeclInline_Math    (ifd, "llAsin(float)",        "Asin",  oneDoub);
+            new TokenDeclInline_Math    (ifd, "llAtan2(float,float)", "Atan2", twoDoubs);
+            new TokenDeclInline_Math    (ifd, "llCos(float)",         "Cos",   oneDoub);
+            new TokenDeclInline_Math    (ifd, "llFabs(float)",        "Abs",   oneDoub);
+            new TokenDeclInline_Math    (ifd, "llLog(float)",         "Log",   oneDoub);
+            new TokenDeclInline_Math    (ifd, "llLog10(float)",       "Log10", oneDoub);
+            new TokenDeclInline_Math    (ifd, "llPow(float,float)",   "Pow",   twoDoubs);
+            new TokenDeclInline_LLRound (ifd);
+            new TokenDeclInline_Math    (ifd, "llSin(float)",         "Sin",   oneDoub);
+            new TokenDeclInline_Math    (ifd, "llSqrt(float)",        "Sqrt",  oneDoub);
+            new TokenDeclInline_Math    (ifd, "llTan(float)",         "Tan",   oneDoub);
+
+            /*
+             * Something weird about the code generation for these calls, so they all have their own handwritten code generators.
+             */
+            new TokenDeclInline_GetFreeMemory (ifd);
+            new TokenDeclInline_GetUsedMemory (ifd);
+
+            /*
+             * These are all the xmr...() calls directly in XMRInstAbstract.
+             * Includes the calls from ScriptBaseClass that has all the stubs
+             * which convert XMRInstAbstract to the various <NAME>_Api contexts.
+             */
+            MethodInfo[] absmeths = typeof (XMRInstAbstract).GetMethods ();
+            AddInterfaceMethods (ifd, absmeths, null);
+
+            return ifd;
+        }
+
+        /**
+         * @brief Add API functions from the given interface to list of built-in functions.
+         *        Only functions beginning with a lower-case letter are entered, all others ignored.
+         * @param ifd = internal function dictionary to add them to
+         * @param ifaceMethods = list of API functions
+         * @param acf = which field in XMRInstanceSuperType holds method's 'this' pointer
+         */
+        // this one accepts only names beginning with a lower-case letter
+        public static void AddInterfaceMethods (VarDict ifd, MethodInfo[] ifaceMethods, FieldInfo acf)
+        {
+            List<MethodInfo> lcms = new List<MethodInfo> (ifaceMethods.Length);
+            foreach (MethodInfo meth in ifaceMethods)
+            {
+                string name = meth.Name;
+                if ((name[0] >= 'a') && (name[0] <= 'z')) {
+                    lcms.Add (meth);
+                }
+            }
+            AddInterfaceMethods (ifd, lcms.GetEnumerator (), acf);
+        }
+
+        // this one accepts all methods given to it
+        public static void AddInterfaceMethods (VarDict ifd, IEnumerator<MethodInfo> ifaceMethods, FieldInfo acf)
+        {
+            if (ifd == null) ifd = inlineFunctions;
+
+            for (ifaceMethods.Reset (); ifaceMethods.MoveNext ();) {
+                MethodInfo ifaceMethod = ifaceMethods.Current;
+                string key = ifaceMethod.Name;
+
+                try {
+                    /*
+                     * See if we will generate a call to CheckRun() right 
+                     * after we generate a call to the function.
+                     * If function begins with xmr, assume we will not call CheckRun()
+                     * Otherwise, assume we will call CheckRun()
+                     */
+                    bool dcr = !key.StartsWith ("xmr");
+                    foreach (string ncr in noCheckRuns) {
+                        if (ncr == key) {
+                            dcr = false;
+                            break;
+                        }
+                    }
+
+                    /*
+                     * Add function to dictionary.
+                     */
+                    new TokenDeclInline_BEApi (ifd, dcr, ifaceMethod, acf);
+                } catch {
+                    ///??? IGNORE ANY THAT FAIL - LIKE UNRECOGNIZED TYPE ???///
+                    ///???                          and OVERLOADED NAMES ???///
+                }
+            }
+        }
+
+        /**
+         * @brief Add an inline function definition to the dictionary.
+         * @param ifd        = dictionary to add inline definition to
+         * @param doCheckRun = true iff the generated code or the function itself can possibly call CheckRun()
+         * @param nameArgSig = inline function signature string, in form <name>(<arglsltypes>,...)
+         * @param retType    = return type, use TokenTypeVoid if no return value
+         */
+        protected TokenDeclInline (VarDict ifd, 
+                                   bool doCheckRun, 
+                                   string nameArgSig, 
+                                   TokenType retType)
+                : base (null, null, null)
+        {
+            this.retType    = retType;
+            this.triviality = doCheckRun ? Triviality.complex : Triviality.trivial;
+
+            int j = nameArgSig.IndexOf ('(');
+            this.name = new TokenName (null, nameArgSig.Substring (0, j ++));
+
+            this.argDecl = new TokenArgDecl (null);
+            if (nameArgSig[j] != ')') {
+                int i;
+                TokenName name;
+                TokenType type;
+
+                for (i = j; nameArgSig[i] != ')'; i ++) {
+                    if (nameArgSig[i] == ',') {
+                        type = TokenType.FromLSLType (null, nameArgSig.Substring (j, i - j));
+                        name = new TokenName (null, "arg" + this.argDecl.varDict.Count);
+                        this.argDecl.AddArg (type, name);
+                        j = i + 1;
+                    }
+                }
+
+                type = TokenType.FromLSLType (null, nameArgSig.Substring (j, i - j));
+                name = new TokenName (null, "arg" + this.argDecl.varDict.Count);
+                this.argDecl.AddArg (type, name);
+            }
+
+            this.location = new CompValuInline (this);
+            if (ifd == null) ifd = inlineFunctions;
+            ifd.AddEntry (this);
+        }
+
+        protected TokenDeclInline (VarDict ifd, 
+                                   bool doCheckRun, 
+                                   MethodInfo methInfo)
+                : base (null, null, null)
+        {
+            TokenType retType = TokenType.FromSysType (null, methInfo.ReturnType);
+
+            this.isTaggedCallsCheckRun = IsTaggedCallsCheckRun (methInfo);
+            this.name       = new TokenName (null, methInfo.Name);
+            this.retType    = GetRetType (methInfo, retType);
+            this.argDecl    = GetArgDecl (methInfo.GetParameters ());
+            this.triviality = (doCheckRun || this.isTaggedCallsCheckRun) ? Triviality.complex : Triviality.trivial;
+            this.location   = new CompValuInline (this);
+
+            if (ifd == null) ifd = inlineFunctions;
+            ifd.AddEntry (this);
+        }
+
+        private static TokenArgDecl GetArgDecl (ParameterInfo[] parameters)
+        {
+            TokenArgDecl argDecl = new TokenArgDecl (null);
+            foreach (ParameterInfo pi in parameters) {
+                TokenType type = TokenType.FromSysType (null, pi.ParameterType);
+                TokenName name = new TokenName (null, pi.Name);
+                argDecl.AddArg (type, name);
+            }
+            return argDecl;
+        }
+
+        /**
+         * @brief The above code assumes all methods beginning with 'xmr' are trivial, ie, 
+         *        they do not call CheckRun() and also we do not generate a CheckRun() 
+         *        call after they return.  So if an 'xmr' method does call CheckRun(), it 
+         *        must be tagged with attribute 'xmrMethodCallsCheckRunAttribute' so we know 
+         *        the method is not trivial.  But in neither case do we emit our own call 
+         *        to CheckRun(), the 'xmr' method must do its own.  We do however set up a
+         *        call label before the call to the non-trivial 'xmr' method so when we are
+         *        restoring the call stack, the restore will call directly in to the 'xmr'
+         *        method without re-executing any code before the call to the 'xmr' method.
+         */
+        private static bool IsTaggedCallsCheckRun (MethodInfo methInfo)
+        {
+            return (methInfo != null) &&
+                Attribute.IsDefined (methInfo, typeof (xmrMethodCallsCheckRunAttribute));
+        }
+
+        /**
+         * @brief The dumbass OpenSim has key and string as the same type so non-ll
+         *        methods must be tagged with xmrMethodReturnsKeyAttribute if we
+         *        are to think they return a key type, otherwise we will think they
+         *        return string.
+         */
+        private static TokenType GetRetType (MethodInfo methInfo, TokenType retType)
+        {
+            if ((methInfo != null) && (retType != null) && (retType is TokenTypeStr)) {
+                if (Attribute.IsDefined (methInfo, typeof (xmrMethodReturnsKeyAttribute))) {
+                    return ChangeToKeyType (retType);
+                }
+
+                string mn = methInfo.Name;
+                foreach (string kr in keyReturns) {
+                    if (kr == mn) return ChangeToKeyType (retType);
+                }
+
+            }
+            return retType;
+        }
+        private static TokenType ChangeToKeyType (TokenType retType)
+        {
+            if (retType is TokenTypeLSLString) {
+                retType = new TokenTypeLSLKey (null);
+            } else {
+                retType = new TokenTypeKey (null);
+            }
+            return retType;
+        }
+
+        public virtual MethodInfo GetMethodInfo ()
+        {
+            return null;
+        }
+
+        /**
+         * @brief Print out a list of all the built-in functions and constants.
+         */
+        public delegate void WriteLine (string str);
+        public static void PrintBuiltins (bool inclNoisyTag, WriteLine writeLine)
+        {
+            writeLine ("\nBuilt-in functions:\n");
+            SortedDictionary<string, TokenDeclInline> bifs = new SortedDictionary<string, TokenDeclInline> ();
+            foreach (TokenDeclVar bif in TokenDeclInline.inlineFunctions) {
+                bifs.Add (bif.fullName, (TokenDeclInline)bif);
+            }
+            foreach (TokenDeclInline bif in bifs.Values) {
+                char noisy = (!inclNoisyTag || !IsTaggedNoisy (bif.GetMethodInfo ())) ? ' ' : (bif.retType is TokenTypeVoid) ? 'N' : 'R';
+                writeLine (noisy + "   " + bif.retType.ToString ().PadLeft (8) + " " + bif.fullName);
+            }
+            if (inclNoisyTag) {
+                writeLine ("\nN - stub that writes name and arguments to stdout");
+                writeLine ("R - stub that writes name and arguments to stdout then reads return value from stdin");
+                writeLine ("    format is:  function_name : return_value");
+                writeLine ("      example:  llKey2Name:\"Kunta Kinte\"");
+            }
+
+            writeLine ("\nBuilt-in constants:\n");
+            SortedDictionary<string, ScriptConst> scs = new SortedDictionary<string, ScriptConst> ();
+            int widest = 0;
+            foreach (ScriptConst sc in ScriptConst.scriptConstants.Values) {
+                if (widest < sc.name.Length) widest = sc.name.Length;
+                scs.Add (sc.name, sc);
+            }
+            foreach (ScriptConst sc in scs.Values) {
+                writeLine ("    " + sc.rVal.type.ToString ().PadLeft (8) + " " + sc.name.PadRight (widest) + " = " + BuiltInConstVal (sc.rVal));
+            }
+        }
+
+        public static bool IsTaggedNoisy (MethodInfo methInfo)
+        {
+            return (methInfo != null) && Attribute.IsDefined (methInfo, typeof (xmrMethodIsNoisyAttribute));
+        }
+
+        public static string BuiltInConstVal (CompValu rVal)
+        {
+            if (rVal is CompValuInteger) {
+                int x = ((CompValuInteger)rVal).x;
+                return "0x" + x.ToString ("X8") + " = " + x.ToString ().PadLeft (11);
+            }
+            if (rVal is CompValuFloat) return ((CompValuFloat)rVal).x.ToString ();
+            if (rVal is CompValuString) {
+                StringBuilder sb = new StringBuilder ();
+                PrintParam (sb, ((CompValuString)rVal).x);
+                return sb.ToString ();
+            }
+            if (rVal is CompValuSField) {
+                FieldInfo fi = ((CompValuSField)rVal).field;
+                StringBuilder sb = new StringBuilder ();
+                PrintParam (sb, fi.GetValue (null));
+                return sb.ToString ();
+            }
+            return rVal.ToString ();  // just prints the type
+        }
+
+        public static void PrintParam (StringBuilder sb, object p)
+        {
+            if (p == null) {
+                sb.Append ("null");
+            } else if (p is LSL_List) {
+                sb.Append ('[');
+                object[] d = ((LSL_List)p).Data;
+                for (int i = 0; i < d.Length; i ++) {
+                    if (i > 0) sb.Append (',');
+                    PrintParam (sb, d[i]);
+                }
+                sb.Append (']');
+            } else if (p is LSL_Rotation) {
+                LSL_Rotation r = (LSL_Rotation)p;
+                sb.Append ('<');
+                sb.Append (r.x);
+                sb.Append (',');
+                sb.Append (r.y);
+                sb.Append (',');
+                sb.Append (r.z);
+                sb.Append (',');
+                sb.Append (r.s);
+                sb.Append ('>');
+            } else if (p is LSL_String) {
+                PrintParamString (sb, (string)(LSL_String)p);
+            } else if (p is LSL_Vector) {
+                LSL_Vector v = (LSL_Vector)p;
+                sb.Append ('<');
+                sb.Append (v.x);
+                sb.Append (',');
+                sb.Append (v.y);
+                sb.Append (',');
+                sb.Append (v.z);
+                sb.Append ('>');
+            } else if (p is string) {
+                PrintParamString (sb, (string)p);
+            } else {
+                sb.Append (p.ToString ());
+            }
+        }
+
+        public static void PrintParamString (StringBuilder sb, string p)
+        {
+            sb.Append ('"');
+            foreach (char c in p) {
+                if (c == '\b') {
+                    sb.Append ("\\b");
+                    continue;
+                }
+                if (c == '\n') {
+                    sb.Append ("\\n");
+                    continue;
+                }
+                if (c == '\r') {
+                    sb.Append ("\\r");
+                    continue;
+                }
+                if (c == '\t') {
+                    sb.Append ("\\t");
+                    continue;
+                }
+                if (c == '"') {
+                    sb.Append ("\\\"");
+                    continue;
+                }
+                if (c == '\\') {
+                    sb.Append ("\\\\");
+                    continue;
+                }
+                sb.Append (c);
+            }
+            sb.Append ('"');
+        }
+    }
+
+    /**
+     * @brief Code generators...
+     * @param scg = script we are generating code for
+     * @param result = type/location for result (type matches function definition)
+     * @param args = type/location of arguments (types match function definition)
+     */
+
+    public class TokenDeclInline_LLAbs : TokenDeclInline {
+        public TokenDeclInline_LLAbs (VarDict ifd)
+                : base (ifd, false, "llAbs(integer)", new TokenTypeInt (null)) { }
+
+        public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
+        {
+            ScriptMyLabel itsPosLabel = scg.ilGen.DefineLabel ("llAbstemp");
+
+            args[0].PushVal (scg, errorAt);
+            scg.ilGen.Emit (errorAt, OpCodes.Dup);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
+            scg.ilGen.Emit (errorAt, OpCodes.Bge_S, itsPosLabel);
+            scg.ilGen.Emit (errorAt, OpCodes.Neg);
+            scg.ilGen.MarkLabel (itsPosLabel);
+            result.Pop (scg, errorAt, retType);
+        }
+    }
+
+    public class TokenDeclInline_Math : TokenDeclInline {
+        private MethodInfo methInfo;
+
+        public TokenDeclInline_Math (VarDict ifd, string sig, string name, Type[] args)
+                : base (ifd, false, sig, new TokenTypeFloat (null))
+        {
+            methInfo = ScriptCodeGen.GetStaticMethod (typeof (System.Math), name, args);
+        }
+
+        public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
+        {
+            for (int i = 0; i < args.Length; i ++) {
+                args[i].PushVal (scg, errorAt, argDecl.types[i]);
+            }
+            scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
+            result.Pop (scg, errorAt, retType);
+        }
+    }
+
+    public class TokenDeclInline_LLRound : TokenDeclInline {
+
+        private static MethodInfo roundMethInfo = ScriptCodeGen.GetStaticMethod (typeof (System.Math), "Round", 
+                new Type[] { typeof (double), typeof (MidpointRounding) });
+
+        public TokenDeclInline_LLRound (VarDict ifd)
+                : base (ifd, false, "llRound(float)", new TokenTypeInt (null)) { }
+
+        public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
+        {
+            args[0].PushVal (scg, errorAt, new TokenTypeFloat (null));
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, (int)System.MidpointRounding.AwayFromZero);
+            scg.ilGen.Emit (errorAt, OpCodes.Call, roundMethInfo);
+            result.Pop (scg, errorAt, new TokenTypeFloat (null));
+        }
+    }
+
+    public class TokenDeclInline_GetFreeMemory : TokenDeclInline {
+        private static readonly MethodInfo getFreeMemMethInfo = typeof (XMRInstAbstract).GetMethod ("xmrHeapLeft", new Type[] { });
+
+        public TokenDeclInline_GetFreeMemory (VarDict ifd)
+                : base (ifd, false, "llGetFreeMemory()", new TokenTypeInt (null)) { }
+
+        // appears as llGetFreeMemory() in script source code
+        // but actually calls xmrHeapLeft()
+        public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
+        {
+            scg.PushXMRInst ();
+            scg.ilGen.Emit (errorAt, OpCodes.Call, getFreeMemMethInfo);
+            result.Pop (scg, errorAt, new TokenTypeInt (null));
+        }
+    }
+
+    public class TokenDeclInline_GetUsedMemory : TokenDeclInline {
+        private static readonly MethodInfo getUsedMemMethInfo = typeof (XMRInstAbstract).GetMethod ("xmrHeapUsed", new Type[] { });
+
+        public TokenDeclInline_GetUsedMemory (VarDict ifd)
+                : base (ifd, false, "llGetUsedMemory()", new TokenTypeInt (null)) { }
+
+        // appears as llGetUsedMemory() in script source code
+        // but actually calls xmrHeapUsed()
+        public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
+        {
+            scg.PushXMRInst ();
+            scg.ilGen.Emit (errorAt, OpCodes.Call, getUsedMemMethInfo);
+            result.Pop (scg, errorAt, new TokenTypeInt (null));
+        }
+    }
+
+    /**
+     * @brief Generate code for the usual ll...() functions.
+     */
+    public class TokenDeclInline_BEApi : TokenDeclInline {
+        private static readonly MethodInfo fixLLParcelMediaQuery = ScriptCodeGen.GetStaticMethod 
+                (typeof (XMRInstAbstract), "FixLLParcelMediaQuery", new Type[] { typeof (LSL_List) });
+
+        private static readonly MethodInfo fixLLParcelMediaCommandList = ScriptCodeGen.GetStaticMethod 
+                (typeof (XMRInstAbstract), "FixLLParcelMediaCommandList", new Type[] { typeof (LSL_List) });
+
+        public bool doCheckRun;
+        private FieldInfo apiContextField;
+        private MethodInfo methInfo;
+
+        /**
+         * @brief Constructor
+         * @param ifd = dictionary to add the function to
+         * @param dcr = append a call to CheckRun()
+         * @param methInfo = ll...() method to be called
+         */
+        public TokenDeclInline_BEApi (VarDict ifd, bool dcr, MethodInfo methInfo, FieldInfo acf)
+                : base (ifd, dcr, methInfo)
+        {
+            this.methInfo = methInfo;
+            doCheckRun = dcr;
+            apiContextField = acf;
+        }
+
+        public override MethodInfo GetMethodInfo ()
+        {
+            return methInfo;
+        }
+
+        /**
+         * @brief Generate call to backend API function (eg llSay()) maybe followed by a call to CheckRun().
+         * @param scg    = script being compiled
+         * @param result = where to place result (might be void)
+         * @param args   = script-visible arguments to pass to API function
+         */
+        public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
+        {
+            if (isTaggedCallsCheckRun) {                         // see if 'xmr' method that calls CheckRun() internally
+                new ScriptCodeGen.CallLabel (scg, errorAt);  // if so, put a call label immediately before it
+                                                                 // .. so restoring the frame will jump immediately to the
+                                                                 // .. call without re-executing any code before this
+            }
+            if (!methInfo.IsStatic) {
+                scg.PushXMRInst ();                          // XMRInstanceSuperType pointer
+                if (apiContextField != null) {
+                    scg.ilGen.Emit (errorAt, OpCodes.Ldfld, apiContextField);
+                                                             // 'this' pointer for API function
+                }
+            }
+            for (int i = 0; i < args.Length; i ++) {             // push arguments, boxing/unboxing as needed
+                args[i].PushVal (scg, errorAt, argDecl.types[i]);
+            }
+            if (methInfo.Name == "llParcelMediaQuery") {
+                scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaQuery);
+            }
+            if (methInfo.Name == "llParcelMediaCommandList") {
+                scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaCommandList);
+            }
+            if (methInfo.IsVirtual) {                            // call API function
+                scg.ilGen.Emit (errorAt, OpCodes.Callvirt, methInfo);
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
+            }
+            result.Pop (scg, errorAt, retType);                  // pop result, boxing/unboxing as needed
+            if (isTaggedCallsCheckRun) {
+                scg.openCallLabel = null;
+            }
+            if (doCheckRun) {
+                scg.EmitCallCheckRun (errorAt, false);       // maybe call CheckRun()
+            }
+        }
+    }
+}

+ 81 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptMyILGen.cs

@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public interface ScriptMyILGen
+    {
+        string methName { get; }
+        ScriptMyLocal DeclareLocal (Type type, string name);
+        ScriptMyLabel DefineLabel (string name);
+        void BeginExceptionBlock ();
+        void BeginCatchBlock (Type excType);
+        void BeginFinallyBlock ();
+        void EndExceptionBlock ();
+        void Emit (Token errorAt, OpCode opcode);
+        void Emit (Token errorAt, OpCode opcode, FieldInfo field);
+        void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal);
+        void Emit (Token errorAt, OpCode opcode, Type type);
+        void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel);
+        void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels);
+        void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method);
+        void Emit (Token errorAt, OpCode opcode, MethodInfo method);
+        void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor);
+        void Emit (Token errorAt, OpCode opcode, double value);
+        void Emit (Token errorAt, OpCode opcode, float value);
+        void Emit (Token errorAt, OpCode opcode, int value);
+        void Emit (Token errorAt, OpCode opcode, string value);
+        void MarkLabel (ScriptMyLabel myLabel);
+    }
+
+    /**
+     * @brief One of these per label defined in the function.
+     */
+    public class ScriptMyLabel {
+        public string name;
+        public int number;
+
+        public GraphNodeMarkLabel whereAmI;
+        public Type[] stackDepth;
+        public bool[] stackBoxeds;
+    }
+
+    /**
+     * @brief One of these per local variable defined in the function.
+     */
+    public class ScriptMyLocal {
+        public string name;
+        public Type type;
+        public int number;
+
+        public bool isReferenced;
+    }
+}

+ 256 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjCode.cs

@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenSim.Region.ScriptEngine.XMREngine;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public delegate void ScriptEventHandler (XMRInstAbstract instance);
+
+    /*
+     * This object represents the output of the compilation.
+     * Once the compilation is complete, its contents should be
+     * considered 'read-only', so it can be shared among multiple
+     * instances of the script.
+     *
+     * It gets created by ScriptCodeGen.
+     * It gets used by XMRInstance to create script instances.
+     */
+    public class ScriptObjCode
+    {
+        public string sourceHash;         // source text hash code
+
+        public XMRInstArSizes glblSizes = new XMRInstArSizes ();
+                                          // number of global variables of various types
+
+        public string[] stateNames;       // convert state number to corresponding string
+
+        public ScriptEventHandler[,] scriptEventHandlerTable;
+                                          // entrypoints to all event handler functions
+                                          // 1st subscript = state code number (0=default)
+                                          // 2nd subscript = event code number
+                                          // null entry means no handler defined for that state,event
+
+        public Dictionary<string, TokenDeclSDType> sdObjTypesName;
+                                          // all script-defined types by name
+        public TokenDeclSDType[] sdObjTypesIndx;
+                                          // all script-defined types by sdTypeIndex
+
+        public Dictionary<Type, string> sdDelTypes;
+                                          // all script-defined delegates (including anonymous)
+
+        public Dictionary<string, DynamicMethod> dynamicMethods;
+                                          // all dyanmic methods
+
+        public Dictionary<string, KeyValuePair<int, ScriptSrcLoc>[]> scriptSrcLocss;
+                                          // method,iloffset -> source file,line,posn
+
+        public int refCount;              // used by engine to keep track of number of 
+                                          // instances that are using this object code
+
+        public Dictionary<string,Dictionary<int,string>> globalVarNames = new Dictionary<string,Dictionary<int,string>> ();
+
+        public DateTime fileDateUtc;
+        public int expiryDays = Int32.MaxValue;
+        public bool IsExpired ()
+        {
+            return (DateTime.UtcNow.Ticks - fileDateUtc.Ticks) / 10000000 / 86400 >= expiryDays;
+        }
+
+        /**
+         * @brief Fill in ScriptObjCode from an XMREngine object file.
+         *   'objFileReader' is a serialized form of the CIL code we generated
+         *   'asmFileWriter' is where we write the disassembly to (or null if not wanted)
+         *   'srcFileWriter' is where we write the decompilation to (or null if not wanted)
+         * Throws an exception if there is any error (theoretically).
+         */
+        public ScriptObjCode (BinaryReader objFileReader, TextWriter asmFileWriter, TextWriter srcFileWriter)
+        {
+            /*
+             * Check version number to make sure we know how to process file contents.
+             */
+            char[] ocm = objFileReader.ReadChars (ScriptCodeGen.OBJECT_CODE_MAGIC.Length);
+            if (new String (ocm) != ScriptCodeGen.OBJECT_CODE_MAGIC) {
+                throw new Exception ("not an XMR object file (bad magic)");
+            }
+            int cvv = objFileReader.ReadInt32 ();
+            if (cvv != ScriptCodeGen.COMPILED_VERSION_VALUE) {
+                throw new CVVMismatchException (cvv, ScriptCodeGen.COMPILED_VERSION_VALUE);
+            }
+
+            /*
+             * Fill in simple parts of scriptObjCode object.
+             */
+            sourceHash = objFileReader.ReadString ();
+            expiryDays = objFileReader.ReadInt32 ();
+            glblSizes.ReadFromFile (objFileReader);
+
+            int nStates = objFileReader.ReadInt32 ();
+
+            stateNames = new string[nStates];
+            for (int i = 0; i < nStates; i ++) {
+                stateNames[i] = objFileReader.ReadString ();
+                if (asmFileWriter != null) {
+                    asmFileWriter.WriteLine ("  state[{0}] = {1}", i, stateNames[i]);
+                }
+            }
+
+            if (asmFileWriter != null) {
+                glblSizes.WriteAsmFile (asmFileWriter, "numGbl");
+            }
+
+            string gblName;
+            while ((gblName = objFileReader.ReadString ()) != "") {
+                string gblType = objFileReader.ReadString ();
+                int gblIndex = objFileReader.ReadInt32 ();
+                Dictionary<int,string> names;
+                if (!globalVarNames.TryGetValue (gblType, out names)) {
+                    names = new Dictionary<int,string> ();
+                    globalVarNames.Add (gblType, names);
+                }
+                names.Add (gblIndex, gblName);
+                if (asmFileWriter != null) {
+                    asmFileWriter.WriteLine ("  {0} = {1}[{2}]", gblName, gblType, gblIndex);
+                }
+            }
+
+            /*
+             * Read in script-defined types.
+             */
+            sdObjTypesName = new Dictionary<string, TokenDeclSDType> ();
+            sdDelTypes = new Dictionary<Type, string> ();
+            int maxIndex = -1;
+            while ((gblName = objFileReader.ReadString ()) != "") {
+                TokenDeclSDType sdt = TokenDeclSDType.ReadFromFile (sdObjTypesName, 
+                                                      gblName, objFileReader, asmFileWriter);
+                sdObjTypesName.Add (gblName, sdt);
+                if (maxIndex < sdt.sdTypeIndex) maxIndex = sdt.sdTypeIndex;
+                if (sdt is TokenDeclSDTypeDelegate) {
+                    sdDelTypes.Add (sdt.GetSysType (), gblName);
+                }
+            }
+            sdObjTypesIndx = new TokenDeclSDType[maxIndex+1];
+            foreach (TokenDeclSDType sdt in sdObjTypesName.Values) {
+                sdObjTypesIndx[sdt.sdTypeIndex] = sdt;
+            }
+
+            /*
+             * Now fill in the methods (the hard part).
+             */
+            scriptEventHandlerTable = new ScriptEventHandler[nStates,(int)ScriptEventCode.Size];
+            dynamicMethods          = new Dictionary<string, DynamicMethod> ();
+            scriptSrcLocss          = new Dictionary<string, KeyValuePair<int, ScriptSrcLoc>[]> ();
+
+            ObjectTokens objectTokens = null;
+            if (asmFileWriter != null) {
+                objectTokens = new OTDisassemble (this, asmFileWriter);
+            } else if (srcFileWriter != null) {
+                objectTokens = new OTDecompile (this, srcFileWriter);
+            }
+
+            try {
+                ScriptObjWriter.CreateObjCode (sdObjTypesName, objFileReader, this, objectTokens);
+            } finally {
+                if (objectTokens != null) objectTokens.Close ();
+            }
+
+            /*
+             * We enter all script event handler methods in the ScriptEventHandler table.
+             * They are named:  <statename> <eventname>
+             */
+            foreach (KeyValuePair<string, DynamicMethod> kvp in dynamicMethods) {
+                string methName = kvp.Key;
+                int i = methName.IndexOf (' ');
+                if (i < 0) continue;
+                string stateName = methName.Substring (0, i);
+                string eventName = methName.Substring (++ i);
+                int stateCode;
+                for (stateCode = stateNames.Length; -- stateCode >= 0;) {
+                    if (stateNames[stateCode] == stateName) break;
+                }
+                int eventCode = (int)Enum.Parse (typeof (ScriptEventCode), eventName);
+                scriptEventHandlerTable[stateCode,eventCode] = 
+                            (ScriptEventHandler)kvp.Value.CreateDelegate (typeof (ScriptEventHandler));
+            }
+
+            /*
+             * Fill in all script-defined class vtables.
+             */
+            foreach (TokenDeclSDType sdt in sdObjTypesIndx) {
+                if ((sdt != null) && (sdt is TokenDeclSDTypeClass)) {
+                    TokenDeclSDTypeClass sdtc = (TokenDeclSDTypeClass)sdt;
+                    sdtc.FillVTables (this);
+                }
+            }
+        }
+
+        /**
+         * @brief Called once for every method found in objFileReader file.
+         *        It enters the method in the ScriptObjCode object table so it can be called.
+         */
+        public void EndMethod (DynamicMethod method, Dictionary<int, ScriptSrcLoc> srcLocs)
+        {
+            /*
+             * Save method object code pointer.
+             */
+            dynamicMethods.Add (method.Name, method);
+
+            /*
+             * Build and sort iloffset -> source code location array.
+             */
+            int n = srcLocs.Count;
+            KeyValuePair<int, ScriptSrcLoc>[] srcLocArray = new KeyValuePair<int, ScriptSrcLoc>[n];
+            n = 0;
+            foreach (KeyValuePair<int, ScriptSrcLoc> kvp in srcLocs) srcLocArray[n++] = kvp;
+            Array.Sort (srcLocArray, endMethodWrapper);
+
+            /*
+             * Save sorted array.
+             */
+            scriptSrcLocss.Add (method.Name, srcLocArray);
+        }
+
+        /**
+         * @brief Called once for every method found in objFileReader file.
+         *        It enters the method in the ScriptObjCode object table so it can be called.
+         */
+        private static EndMethodWrapper endMethodWrapper = new EndMethodWrapper ();
+        private class EndMethodWrapper : System.Collections.IComparer {
+            public int Compare (object x, object y)
+            {
+                KeyValuePair<int, ScriptSrcLoc> kvpx = (KeyValuePair<int, ScriptSrcLoc>)x;
+                KeyValuePair<int, ScriptSrcLoc> kvpy = (KeyValuePair<int, ScriptSrcLoc>)y;
+                return kvpx.Key - kvpy.Key;
+            }
+        }
+    }
+}

+ 947 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjWriter.cs

@@ -0,0 +1,947 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+/**
+ * @brief Wrapper class for ILGenerator.
+ *        It writes the object code to a file and can then make real ILGenerator calls
+ *        based on the file's contents.
+ */
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public enum ScriptObjWriterCode : byte {
+        BegMethod, EndMethod, TheEnd,
+        DclLabel, DclLocal, DclMethod, MarkLabel,
+        EmitNull, EmitField, EmitLocal, EmitType, EmitLabel, EmitMethodExt, 
+        EmitMethodInt, EmitCtor, EmitDouble, EmitFloat, EmitInteger, EmitString,
+        EmitLabels,
+        BegExcBlk, BegCatBlk, BegFinBlk, EndExcBlk
+    }
+
+    public class ScriptObjWriter : ScriptMyILGen
+    {
+        private static Dictionary<short, OpCode> opCodes = PopulateOpCodes ();
+        private static Dictionary<string, Type> string2Type = PopulateS2T ();
+        private static Dictionary<Type, string> type2String = PopulateT2S ();
+
+        private static MethodInfo monoGetCurrentOffset = typeof (ILGenerator).GetMethod ("Mono_GetCurrentOffset",
+                        BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, 
+                        new Type[] { typeof (ILGenerator) }, null);
+
+        private static readonly OpCode[] opCodesLdcI4M1P8 = new OpCode[] {
+            OpCodes.Ldc_I4_M1, OpCodes.Ldc_I4_0, OpCodes.Ldc_I4_1, OpCodes.Ldc_I4_2, OpCodes.Ldc_I4_3, 
+            OpCodes.Ldc_I4_4,  OpCodes.Ldc_I4_5, OpCodes.Ldc_I4_6, OpCodes.Ldc_I4_7, OpCodes.Ldc_I4_8
+        };
+
+        private BinaryWriter objFileWriter;
+        private string lastErrorAtFile = "";
+        private int    lastErrorAtLine = 0;
+        private int    lastErrorAtPosn = 0;
+
+        private Dictionary<Type, string> sdTypesRev = new Dictionary<Type, string> ();
+        public int labelNumber = 0;
+        public int localNumber = 0;
+
+        private string _methName;
+        public string methName { get { return _methName; } }
+
+        public Type   retType;
+        public Type[] argTypes;
+
+        /**
+         * @brief Begin function declaration
+         * @param sdTypes    = script-defined types
+         * @param methName   = name of the method being declared, eg, "Verify(array,list,string)"
+         * @param retType    = its return value type
+         * @param argTypes[] = its argument types
+         * @param objFileWriter  = file to write its object code to
+         *
+         * After calling this function, the following functions should be called:
+         *    this.BegMethod ();
+         *      this.<as required> ();
+         *    this.EndMethod ();
+         *
+         * The design of this object is such that many constructors may be called,
+         * but once a BegMethod() is called for one of the objects, no method may
+         * called for any of the other objects until EndMethod() is called (or it 
+         * would break up the object stream for that method).  But we need to have
+         * many constructors possible so we get function headers at the beginning
+         * of the object file in case there are forward references to the functions.
+         */
+        public ScriptObjWriter (TokenScript tokenScript, string methName, Type retType, Type[] argTypes, string[] argNames, BinaryWriter objFileWriter)
+        {
+            this._methName     = methName;
+            this.retType       = retType;
+            this.argTypes      = argTypes;
+            this.objFileWriter = objFileWriter;
+
+            /*
+             * Build list that translates system-defined types to script defined types.
+             */
+            foreach (TokenDeclSDType sdt in tokenScript.sdSrcTypesValues) {
+                Type sys = sdt.GetSysType();
+                if (sys != null) sdTypesRev[sys] = sdt.longName.val;
+            }
+
+            /*
+             * This tells the reader to call 'new DynamicMethod()' to create
+             * the function header.  Then any forward reference calls to this
+             * method will have a MethodInfo struct to call.
+             */
+            objFileWriter.Write ((byte)ScriptObjWriterCode.DclMethod);
+            objFileWriter.Write (methName);
+            objFileWriter.Write (GetStrFromType (retType));
+
+            int nArgs = argTypes.Length;
+            objFileWriter.Write (nArgs);
+            for (int i = 0; i < nArgs; i ++) {
+                objFileWriter.Write (GetStrFromType (argTypes[i]));
+                objFileWriter.Write (argNames[i]);
+            }
+        }
+
+        /**
+         * @brief Begin outputting object code for the function
+         */
+        public void BegMethod ()
+        {
+            /*
+             * This tells the reader to call methodInfo.GetILGenerator()
+             * so it can start writing CIL code for the method.
+             */
+            objFileWriter.Write ((byte)ScriptObjWriterCode.BegMethod);
+            objFileWriter.Write (methName);
+        }
+
+        /**
+         * @brief End of object code for the function
+         */
+        public void EndMethod ()
+        {
+            /*
+             * This tells the reader that all code for the method has
+             * been written and so it will typically call CreateDelegate()
+             * to finalize the method and create an entrypoint.
+             */
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EndMethod);
+
+            objFileWriter = null;
+        }
+
+        /**
+         * @brief Declare a local variable for use by the function
+         */
+        public ScriptMyLocal DeclareLocal (Type type, string name)
+        {
+            ScriptMyLocal myLocal = new ScriptMyLocal ();
+            myLocal.type   = type;
+            myLocal.name   = name;
+            myLocal.number = localNumber ++;
+            myLocal.isReferenced = true;  // so ScriptCollector won't optimize references away
+            return DeclareLocal (myLocal);
+        }
+        public ScriptMyLocal DeclareLocal (ScriptMyLocal myLocal)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.DclLocal);
+            objFileWriter.Write (myLocal.number);
+            objFileWriter.Write (myLocal.name);
+            objFileWriter.Write (GetStrFromType (myLocal.type));
+            return myLocal;
+        }
+
+        /**
+         * @brief Define a label for use by the function
+         */
+        public ScriptMyLabel DefineLabel (string name)
+        {
+            ScriptMyLabel myLabel = new ScriptMyLabel ();
+            myLabel.name   = name;
+            myLabel.number = labelNumber ++;
+            return DefineLabel (myLabel);
+        }
+        public ScriptMyLabel DefineLabel (ScriptMyLabel myLabel)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.DclLabel);
+            objFileWriter.Write (myLabel.number);
+            objFileWriter.Write (myLabel.name);
+            return myLabel;
+        }
+
+        /**
+         * @brief try/catch blocks.
+         */
+        public void BeginExceptionBlock ()
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.BegExcBlk);
+        }
+
+        public void BeginCatchBlock (Type excType)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.BegCatBlk);
+            objFileWriter.Write (GetStrFromType (excType));
+        }
+
+        public void BeginFinallyBlock ()
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.BegFinBlk);
+        }
+
+        public void EndExceptionBlock ()
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EndExcBlk);
+        }
+
+        public void Emit (Token errorAt, OpCode opcode)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitNull);
+            WriteOpCode (errorAt, opcode);
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, FieldInfo field)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitField);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (GetStrFromType (field.ReflectedType));
+            objFileWriter.Write (field.Name);
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitLocal);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (myLocal.number);
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, Type type)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitType);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (GetStrFromType (type));
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitLabel);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (myLabel.number);
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitLabels);
+            WriteOpCode (errorAt, opcode);
+            int nLabels = myLabels.Length;
+            objFileWriter.Write (nLabels);
+            for (int i = 0; i < nLabels; i ++) {
+                objFileWriter.Write (myLabels[i].number);
+            }
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method)
+        {
+            if (method == null) throw new ArgumentNullException ("method");
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitMethodInt);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (method.methName);
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, MethodInfo method)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitMethodExt);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (method.Name);
+            objFileWriter.Write (GetStrFromType (method.ReflectedType));
+            ParameterInfo[] parms = method.GetParameters ();
+            int nArgs = parms.Length;
+            objFileWriter.Write (nArgs);
+            for (int i = 0; i < nArgs; i ++) {
+                objFileWriter.Write (GetStrFromType (parms[i].ParameterType));
+            }
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitCtor);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (GetStrFromType (ctor.ReflectedType));
+            ParameterInfo[] parms = ctor.GetParameters ();
+            int nArgs = parms.Length;
+            objFileWriter.Write (nArgs);
+            for (int i = 0; i < nArgs; i ++) {
+                objFileWriter.Write (GetStrFromType (parms[i].ParameterType));
+            }
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, double value)
+        {
+            if (opcode != OpCodes.Ldc_R8) {
+                throw new Exception ("bad opcode " + opcode.ToString ());
+            }
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitDouble);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (value);
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, float value)
+        {
+            if (opcode != OpCodes.Ldc_R4) {
+                throw new Exception ("bad opcode " + opcode.ToString ());
+            }
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitFloat);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (value);
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, int value)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitInteger);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (value);
+        }
+
+        public void Emit (Token errorAt, OpCode opcode, string value)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.EmitString);
+            WriteOpCode (errorAt, opcode);
+            objFileWriter.Write (value);
+        }
+
+        /**
+         * @brief Declare that the target of a label is the next instruction.
+         */
+        public void MarkLabel (ScriptMyLabel myLabel)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.MarkLabel);
+            objFileWriter.Write (myLabel.number);
+        }
+
+        /**
+         * @brief Write end-of-file marker to binary file.
+         */
+        public static void TheEnd (BinaryWriter objFileWriter)
+        {
+            objFileWriter.Write ((byte)ScriptObjWriterCode.TheEnd);
+        }
+
+        /**
+         * @brief Take an object file created by ScriptObjWriter() and convert it to a series of dynamic methods.
+         * @param sdTypes   = script-defined types
+         * @param objReader = where to read object file from (as written by ScriptObjWriter above).
+         * @param scriptObjCode.EndMethod = called for each method defined at the end of the methods definition
+         * @param objectTokens = write disassemble/decompile data (or null if not wanted)
+         */
+        public static void CreateObjCode (Dictionary<string, TokenDeclSDType> sdTypes, BinaryReader objReader,
+                ScriptObjCode scriptObjCode, ObjectTokens objectTokens)
+        {
+            Dictionary<string, DynamicMethod> methods = new Dictionary<string, DynamicMethod> ();
+            DynamicMethod method = null;
+            ILGenerator ilGen = null;
+            Dictionary<int, Label> labels = new Dictionary<int, Label> ();
+            Dictionary<int, LocalBuilder> locals = new Dictionary<int, LocalBuilder> ();
+            Dictionary<int, string> labelNames = new Dictionary<int, string> ();
+            Dictionary<int, string> localNames = new Dictionary<int, string> ();
+            object[] ilGenArg = new object[1];
+            int offset = 0;
+            Dictionary<int, ScriptSrcLoc> srcLocs = null;
+            string srcFile = "";
+            int    srcLine = 0;
+            int    srcPosn = 0;
+
+            while (true) {
+
+                /*
+                 * Get IL instruction offset at beginning of instruction.
+                 */
+                offset = 0;
+                if ((ilGen != null) && (monoGetCurrentOffset != null)) {
+                    offset = (int)monoGetCurrentOffset.Invoke (null, ilGenArg);
+                }
+
+                /*
+                 * Read and decode next internal format code from input file (.xmrobj file).
+                 */
+                ScriptObjWriterCode code = (ScriptObjWriterCode)objReader.ReadByte ();
+                switch (code) {
+
+                    /*
+                     * Reached end-of-file so we are all done.
+                     */
+                    case ScriptObjWriterCode.TheEnd: {
+                        return;
+                    }
+
+                    /*
+                     * Beginning of method's contents.
+                     * Method must have already been declared via DclMethod
+                     * so all we need is its name to retrieve from methods[].
+                     */
+                    case ScriptObjWriterCode.BegMethod: {
+                        string methName = objReader.ReadString ();
+
+                        method = methods[methName];
+                        ilGen  = method.GetILGenerator ();
+                        ilGenArg[0] = ilGen;
+
+                        labels.Clear ();
+                        locals.Clear ();
+                        labelNames.Clear ();
+                        localNames.Clear ();
+
+                        srcLocs = new Dictionary<int, ScriptSrcLoc> ();
+                        if (objectTokens != null) objectTokens.BegMethod (method);
+                        break;
+                    }
+
+                    /*
+                     * End of method's contents (ie, an OpCodes.Ret was probably just output).
+                     * Call the callback to tell it the method is complete, and it can do whatever
+                     * it wants with the method.
+                     */
+                    case ScriptObjWriterCode.EndMethod: {
+                        ilGen = null;
+                        ilGenArg[0] = null;
+                        scriptObjCode.EndMethod (method, srcLocs);
+                        srcLocs = null;
+                        if (objectTokens != null) objectTokens.EndMethod ();
+                        break;
+                    }
+
+                    /*
+                     * Declare a label for branching to.
+                     */
+                    case ScriptObjWriterCode.DclLabel: {
+                        int number  = objReader.ReadInt32 ();
+                        string name = objReader.ReadString ();
+
+                        labels.Add (number, ilGen.DefineLabel ());
+                        labelNames.Add (number, name + "_" + number.ToString ());
+                        if (objectTokens != null) objectTokens.DefineLabel (number, name);
+                        break;
+                    }
+
+                    /*
+                     * Declare a local variable to store into.
+                     */
+                    case ScriptObjWriterCode.DclLocal: {
+                        int number  = objReader.ReadInt32 ();
+                        string name = objReader.ReadString ();
+                        string type = objReader.ReadString ();
+                        Type syType = GetTypeFromStr (sdTypes, type);
+
+                        locals.Add (number, ilGen.DeclareLocal (syType));
+                        localNames.Add (number, name + "_" + number.ToString ());
+                        if (objectTokens != null) objectTokens.DefineLocal (number, name, type, syType);
+                        break;
+                    }
+
+                    /*
+                     * Declare a method that will subsequently be defined.
+                     * We create the DynamicMethod object at this point in case there
+                     * are forward references from other method bodies.
+                     */
+                    case ScriptObjWriterCode.DclMethod: {
+                        string methName = objReader.ReadString ();
+                        Type retType    = GetTypeFromStr (sdTypes, objReader.ReadString ());
+                        int nArgs       = objReader.ReadInt32 ();
+
+                        Type[] argTypes = new Type[nArgs];
+                        string[] argNames = new string[nArgs];
+                        for (int i = 0; i < nArgs; i ++) {
+                            argTypes[i] = GetTypeFromStr (sdTypes, objReader.ReadString ());
+                            argNames[i] = objReader.ReadString ();
+                        }
+                        methods.Add (methName, new DynamicMethod (methName, retType, argTypes));
+                        if (objectTokens != null) objectTokens.DefineMethod (methName, retType, argTypes, argNames);
+                        break;
+                    }
+
+                    /*
+                     * Mark a previously declared label at this spot.
+                     */
+                    case ScriptObjWriterCode.MarkLabel: {
+                        int number = objReader.ReadInt32 ();
+
+                        ilGen.MarkLabel (labels[number]);
+
+                        if (objectTokens != null) objectTokens.MarkLabel (offset, number);
+                        break;
+                    }
+
+                    /*
+                     * Try/Catch blocks.
+                     */
+                    case ScriptObjWriterCode.BegExcBlk: {
+                        ilGen.BeginExceptionBlock ();
+                        if (objectTokens != null) objectTokens.BegExcBlk (offset);
+                        break;
+                    }
+
+                    case ScriptObjWriterCode.BegCatBlk: {
+                        Type excType = GetTypeFromStr (sdTypes, objReader.ReadString ());
+                        ilGen.BeginCatchBlock (excType);
+                        if (objectTokens != null) objectTokens.BegCatBlk (offset, excType);
+                        break;
+                    }
+
+                    case ScriptObjWriterCode.BegFinBlk: {
+                        ilGen.BeginFinallyBlock ();
+                        if (objectTokens != null) objectTokens.BegFinBlk (offset);
+                        break;
+                    }
+
+                    case ScriptObjWriterCode.EndExcBlk: {
+                        ilGen.EndExceptionBlock ();
+                        if (objectTokens != null) objectTokens.EndExcBlk (offset);
+                        break;
+                    }
+
+                    /*
+                     * Emit an opcode with no operand.
+                     */
+                    case ScriptObjWriterCode.EmitNull: {
+                        OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode);
+
+                        if (objectTokens != null) objectTokens.EmitNull (offset, opCode);
+                        break;
+                    }
+
+                    /*
+                     * Emit an opcode with a FieldInfo operand.
+                     */
+                    case ScriptObjWriterCode.EmitField: {
+                        OpCode opCode      = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        Type reflectedType = GetTypeFromStr (sdTypes, objReader.ReadString ());
+                        string fieldName   = objReader.ReadString ();
+
+                        FieldInfo field    = reflectedType.GetField (fieldName);
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, field);
+
+                        if (objectTokens != null) objectTokens.EmitField (offset, opCode, field);
+                        break;
+                    }
+
+                    /*
+                     * Emit an opcode with a LocalBuilder operand.
+                     */
+                    case ScriptObjWriterCode.EmitLocal: {
+                        OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        int number    = objReader.ReadInt32 ();
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, locals[number]);
+
+                        if (objectTokens != null) objectTokens.EmitLocal (offset, opCode, number);
+                        break;
+                    }
+
+                    /*
+                     * Emit an opcode with a Type operand.
+                     */
+                    case ScriptObjWriterCode.EmitType: {
+                        OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        string name   = objReader.ReadString ();
+                        Type type     = GetTypeFromStr (sdTypes, name);
+
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, type);
+
+                        if (objectTokens != null) objectTokens.EmitType (offset, opCode, type);
+                        break;
+                    }
+
+                    /*
+                     * Emit an opcode with a Label operand.
+                     */
+                    case ScriptObjWriterCode.EmitLabel: {
+                        OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        int number    = objReader.ReadInt32 ();
+
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, labels[number]);
+
+                        if (objectTokens != null) objectTokens.EmitLabel (offset, opCode, number);
+                        break;
+                    }
+
+                    /*
+                     * Emit an opcode with a Label array operand.
+                     */
+                    case ScriptObjWriterCode.EmitLabels: {
+                        OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        int nLabels   = objReader.ReadInt32 ();
+                        Label[] lbls  = new Label[nLabels];
+                        int[] nums    = new int[nLabels];
+                        for (int i = 0; i < nLabels; i ++) {
+                            nums[i] = objReader.ReadInt32 ();
+                            lbls[i] = labels[nums[i]];
+                        }
+
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, lbls);
+
+                        if (objectTokens != null) objectTokens.EmitLabels (offset, opCode, nums);
+                        break;
+                    }
+
+                    /*
+                     * Emit an opcode with a MethodInfo operand (such as a call) of an external function.
+                     */
+                    case ScriptObjWriterCode.EmitMethodExt: {
+                        OpCode opCode   = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        string methName = objReader.ReadString ();
+                        Type methType   = GetTypeFromStr (sdTypes, objReader.ReadString ());
+                        int nArgs       = objReader.ReadInt32 ();
+
+                        Type[] argTypes = new Type[nArgs];
+                        for (int i = 0; i < nArgs; i ++) {
+                            argTypes[i] = GetTypeFromStr (sdTypes, objReader.ReadString ());
+                        }
+                        MethodInfo methInfo = methType.GetMethod (methName, argTypes);
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, methInfo);
+
+                        if (objectTokens != null) objectTokens.EmitMethod (offset, opCode, methInfo);
+                        break;
+                    }
+
+                    /*
+                     * Emit an opcode with a MethodInfo operand of an internal function
+                     * (previously declared via DclMethod).
+                     */
+                    case ScriptObjWriterCode.EmitMethodInt: {
+                        OpCode opCode   = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        string methName = objReader.ReadString ();
+
+                        MethodInfo methInfo = methods[methName];
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, methInfo);
+
+                        if (objectTokens != null) objectTokens.EmitMethod (offset, opCode, methInfo);
+                        break;
+                    }
+
+                    /*
+                     * Emit an opcode with a ConstructorInfo operand.
+                     */
+                    case ScriptObjWriterCode.EmitCtor: {
+                        OpCode opCode   = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        Type ctorType   = GetTypeFromStr (sdTypes, objReader.ReadString ());
+                        int nArgs       = objReader.ReadInt32 ();
+                        Type[] argTypes = new Type[nArgs];
+                        for (int i = 0; i < nArgs; i ++) {
+                            argTypes[i] = GetTypeFromStr (sdTypes, objReader.ReadString ());
+                        }
+
+                        ConstructorInfo ctorInfo = ctorType.GetConstructor (argTypes);
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, ctorInfo);
+
+                        if (objectTokens != null) objectTokens.EmitCtor (offset, opCode, ctorInfo);
+                        break;
+                    }
+
+                    /*
+                     * Emit an opcode with a constant operand of various types.
+                     */
+                    case ScriptObjWriterCode.EmitDouble: {
+                        OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        double value  = objReader.ReadDouble ();
+
+                        if (opCode != OpCodes.Ldc_R8) {
+                            throw new Exception ("bad opcode " + opCode.ToString ());
+                        }
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, value);
+
+                        if (objectTokens != null) objectTokens.EmitDouble (offset, opCode, value);
+                        break;
+                    }
+
+                    case ScriptObjWriterCode.EmitFloat: {
+                        OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        float value   = objReader.ReadSingle ();
+
+                        if (opCode != OpCodes.Ldc_R4) {
+                            throw new Exception ("bad opcode " + opCode.ToString ());
+                        }
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, value);
+
+                        if (objectTokens != null) objectTokens.EmitFloat (offset, opCode, value);
+                        break;
+                    }
+
+                    case ScriptObjWriterCode.EmitInteger: {
+                        OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        int value     = objReader.ReadInt32 ();
+
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+
+                        if (opCode == OpCodes.Ldc_I4) {
+                            if ((value >= -1) && (value <= 8)) {
+                                opCode = opCodesLdcI4M1P8[value+1];
+                                ilGen.Emit (opCode);
+                                if (objectTokens != null) objectTokens.EmitNull (offset, opCode);
+                                break;
+                            }
+                            if ((value >= 0) && (value <= 127)) {
+                                opCode = OpCodes.Ldc_I4_S;
+                                ilGen.Emit (OpCodes.Ldc_I4_S, (sbyte)value);
+                                goto pemitint;
+                            }
+                        }
+
+                        ilGen.Emit (opCode, value);
+                        pemitint:
+                        if (objectTokens != null) objectTokens.EmitInteger (offset, opCode, value);
+                        break;
+                    }
+
+                    case ScriptObjWriterCode.EmitString: {
+                        OpCode opCode = ReadOpCode (objReader, ref srcFile, ref srcLine, ref srcPosn);
+                        string value  = objReader.ReadString ();
+
+                        SaveSrcLoc (srcLocs, offset, srcFile, srcLine, srcPosn);
+                        ilGen.Emit (opCode, value);
+
+                        if (objectTokens != null) objectTokens.EmitString (offset, opCode, value);
+                        break;
+                    }
+
+                    /*
+                     * Who knows what?
+                     */
+                    default: throw new Exception ("bad ScriptObjWriterCode " + ((byte)code).ToString ());
+                }
+            }
+        }
+
+        /**
+         * @brief Generate array to quickly translate OpCode.Value to full OpCode struct.
+         */
+        private static Dictionary<short, OpCode> PopulateOpCodes ()
+        {
+            Dictionary<short, OpCode> opCodeDict = new Dictionary<short, OpCode> ();
+            FieldInfo[] fields = typeof (OpCodes).GetFields ();
+            for (int i = 0; i < fields.Length; i ++) {
+                OpCode opcode = (OpCode)fields[i].GetValue (null);
+                opCodeDict.Add (opcode.Value, opcode);
+            }
+            return opCodeDict;
+        }
+
+        /**
+         * @brief Write opcode out to file.
+         */
+        private void WriteOpCode (Token errorAt, OpCode opcode)
+        {
+            if (errorAt == null) {
+                objFileWriter.Write ("");
+                objFileWriter.Write (lastErrorAtLine);
+                objFileWriter.Write (lastErrorAtPosn);
+            } else {
+                if (errorAt.file != lastErrorAtFile) {
+                    objFileWriter.Write (errorAt.file);
+                    lastErrorAtFile = errorAt.file;
+                } else {
+                    objFileWriter.Write ("");
+                }
+                objFileWriter.Write (errorAt.line);
+                objFileWriter.Write (errorAt.posn);
+                lastErrorAtLine = errorAt.line;
+                lastErrorAtPosn = errorAt.posn;
+            }
+            objFileWriter.Write (opcode.Value);
+        }
+
+        /**
+         * @brief Read opcode in from file.
+         */
+        private static OpCode ReadOpCode (BinaryReader objReader, ref string srcFile, ref int srcLine, ref int srcPosn)
+        {
+            string f = objReader.ReadString ();
+            if (f != "") srcFile = f;
+            srcLine = objReader.ReadInt32 ();
+            srcPosn = objReader.ReadInt32 ();
+
+            short value = objReader.ReadInt16 ();
+            return opCodes[value];
+        }
+
+        /**
+         * @brief Save an IL_offset -> source location translation entry
+         * @param srcLocs = saved entries for the current function
+         * @param offset = offset in IL object code for next instruction
+         * @param src{File,Line,Posn} = location in source file corresponding to opcode
+         * @returns with entry added to srcLocs
+         */
+        private static void SaveSrcLoc (Dictionary<int, ScriptSrcLoc> srcLocs, int offset, string srcFile, int srcLine, int srcPosn)
+        {
+            ScriptSrcLoc srcLoc = new ScriptSrcLoc ();
+            srcLoc.file = srcFile;
+            srcLoc.line = srcLine;
+            srcLoc.posn = srcPosn;
+            srcLocs[offset] = srcLoc;
+        }
+
+        /**
+         * @brief Create type<->string conversions.
+         *        Using Type.AssemblyQualifiedName is horribly inefficient
+         *        and all our types should be known.
+         */
+        private static Dictionary<string, Type> PopulateS2T ()
+        {
+            Dictionary<string, Type> s2t = new Dictionary<string, Type> ();
+
+            s2t.Add ("badcallx", typeof (ScriptBadCallNoException));
+            s2t.Add ("binopstr", typeof (BinOpStr));
+            s2t.Add ("bool",     typeof (bool));
+            s2t.Add ("char",     typeof (char));
+            s2t.Add ("delegate", typeof (Delegate));
+            s2t.Add ("delarr[]", typeof (Delegate[]));
+            s2t.Add ("double",   typeof (double));
+            s2t.Add ("exceptn",  typeof (Exception));
+            s2t.Add ("float",    typeof (float));
+            s2t.Add ("htlist",   typeof (HeapTrackerList));
+            s2t.Add ("htobject", typeof (HeapTrackerObject));
+            s2t.Add ("htstring", typeof (HeapTrackerString));
+            s2t.Add ("inlfunc",  typeof (CompValuInline));
+            s2t.Add ("int",      typeof (int));
+            s2t.Add ("int*",     typeof (int).MakeByRefType ());
+            s2t.Add ("intrlokd", typeof (System.Threading.Interlocked));
+            s2t.Add ("lslfloat", typeof (LSL_Float));
+            s2t.Add ("lslint",   typeof (LSL_Integer));
+            s2t.Add ("lsllist",  typeof (LSL_List));
+            s2t.Add ("lslrot",   typeof (LSL_Rotation));
+            s2t.Add ("lslstr",   typeof (LSL_String));
+            s2t.Add ("lslvec",   typeof (LSL_Vector));
+            s2t.Add ("math",     typeof (Math));
+            s2t.Add ("midround", typeof (MidpointRounding));
+            s2t.Add ("object",   typeof (object));
+            s2t.Add ("object*",  typeof (object).MakeByRefType ());
+            s2t.Add ("object[]", typeof (object[]));
+            s2t.Add ("scrbase",  typeof (ScriptBaseClass));
+            s2t.Add ("scrcode",  typeof (ScriptCodeGen));
+            s2t.Add ("sdtclobj", typeof (XMRSDTypeClObj));
+            s2t.Add ("string",   typeof (string));
+            s2t.Add ("typecast", typeof (TypeCast));
+            s2t.Add ("undstatx", typeof (ScriptUndefinedStateException));
+            s2t.Add ("void",     typeof (void));
+            s2t.Add ("xmrarray", typeof (XMR_Array));
+            s2t.Add ("xmrinst",  typeof (XMRInstAbstract));
+
+            return s2t;
+        }
+
+        private static Dictionary<Type, string> PopulateT2S ()
+        {
+            Dictionary<string, Type> s2t = PopulateS2T ();
+            Dictionary<Type, string> t2s = new Dictionary<Type, string> ();
+            foreach (KeyValuePair<string, Type> kvp in s2t) {
+                t2s.Add (kvp.Value, kvp.Key);
+            }
+            return t2s;
+        }
+
+        /**
+         * @brief Add to list of internally recognized types.
+         */
+        public static void DefineInternalType (string name, Type type)
+        {
+            if (!string2Type.ContainsKey(name))
+            {
+                string2Type.Add (name, type);
+                type2String.Add (type, name);
+            }
+        }
+
+        private string GetStrFromType (Type t)
+        {
+            string s = GetStrFromTypeWork (t);
+            return s;
+        }
+        private string GetStrFromTypeWork (Type t)
+        {
+            string s;
+
+            // internal fixed types like int and xmrarray etc
+            if (type2String.TryGetValue (t, out s)) return s;
+
+            // script-defined types
+            if (sdTypesRev.TryGetValue (t, out s)) return "sdt$" + s;
+
+            // inline function types
+            s = TokenDeclSDTypeDelegate.TryGetInlineName (t);
+            if (s != null) return s;
+
+            // last resort
+            return t.AssemblyQualifiedName;
+        }
+
+        private static Type GetTypeFromStr (Dictionary<string, TokenDeclSDType> sdTypes, string s)
+        {
+            Type t;
+
+            // internal fixed types like int and xmrarray etc
+            if (string2Type.TryGetValue (s, out t)) return t;
+
+            // script-defined types
+            if (s.StartsWith ("sdt$")) return sdTypes[s.Substring(4)].GetSysType ();
+
+            // inline function types
+            t = TokenDeclSDTypeDelegate.TryGetInlineSysType (s);
+            if (t != null) return t;
+
+            // last resort
+            return Type.GetType (s, true);
+        }
+    }
+
+    public class ScriptSrcLoc {
+        public string file;
+        public int line;
+        public int posn;
+    }
+}

+ 7719 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs

@@ -0,0 +1,7719 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @brief Reduce parser tokens to abstract syntax tree tokens.
+ *
+ * Usage:
+ *
+ *  tokenBegin = returned by TokenBegin.Analyze ()
+ *               representing the whole script source
+ *               as a flat list of tokens
+ *
+ *  TokenScript tokenScript = Reduce.Analyze (TokenBegin tokenBegin);
+ *
+ *  tokenScript = represents the whole script source
+ *                as a tree of tokens
+ */
+
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    public class ScriptReduce {
+        public const uint SDT_PRIVATE   =   1;
+        public const uint SDT_PROTECTED =   2;
+        public const uint SDT_PUBLIC    =   4;
+        public const uint SDT_ABSTRACT  =   8;
+        public const uint SDT_FINAL     =  16;
+        public const uint SDT_NEW       =  32;
+        public const uint SDT_OVERRIDE  =  64;
+        public const uint SDT_STATIC    = 128;
+        public const uint SDT_VIRTUAL   = 256;
+
+        private const int ASNPR = 50;
+
+        private static Dictionary<Type, int> precedence = PrecedenceInit ();
+
+        private static readonly Type[] brkCloseOnly     = new Type[] { typeof (TokenKwBrkClose) };
+        private static readonly Type[] cmpGTOnly        = new Type[] { typeof (TokenKwCmpGT)    };
+        private static readonly Type[] colonOnly        = new Type[] { typeof (TokenKwColon)    };
+        private static readonly Type[] commaOrBrcClose  = new Type[] { typeof (TokenKwComma),   typeof (TokenKwBrcClose)  };
+        private static readonly Type[] colonOrDotDotDot = new Type[] { typeof (TokenKwColon),   typeof (TokenKwDotDotDot) };
+        private static readonly Type[] parCloseOnly     = new Type[] { typeof (TokenKwParClose) };
+        private static readonly Type[] semiOnly         = new Type[] { typeof (TokenKwSemi)     };
+
+        /**
+         * @brief Initialize operator precedence table
+         * @returns with precedence table pointer
+         */
+        private static Dictionary<Type, int> PrecedenceInit ()
+        {
+            Dictionary<Type, int> p = new Dictionary<Type, int> ();
+
+            // http://www.lslwiki.net/lslwiki/wakka.php?wakka=operators
+
+            p.Add (typeof (TokenKwComma),     30);
+
+            p.Add (typeof (TokenKwAsnLSh), ASNPR);  // all assignment operators of equal precedence
+            p.Add (typeof (TokenKwAsnRSh), ASNPR);  // ... so they get processed strictly right-to-left
+            p.Add (typeof (TokenKwAsnAdd), ASNPR);
+            p.Add (typeof (TokenKwAsnAnd), ASNPR);
+            p.Add (typeof (TokenKwAsnSub), ASNPR);
+            p.Add (typeof (TokenKwAsnMul), ASNPR);
+            p.Add (typeof (TokenKwAsnDiv), ASNPR);
+            p.Add (typeof (TokenKwAsnMod), ASNPR);
+            p.Add (typeof (TokenKwAsnOr),  ASNPR);
+            p.Add (typeof (TokenKwAsnXor), ASNPR);
+            p.Add (typeof (TokenKwAssign), ASNPR);
+
+            p.Add (typeof (TokenKwQMark),     60);
+
+            p.Add (typeof (TokenKwOrOrOr),    70);
+            p.Add (typeof (TokenKwAndAndAnd), 80);
+
+            p.Add (typeof (TokenKwOrOr),   100);
+
+            p.Add (typeof (TokenKwAndAnd), 120);
+
+            p.Add (typeof (TokenKwOr),     140);
+
+            p.Add (typeof (TokenKwXor),    160);
+
+            p.Add (typeof (TokenKwAnd),    180);
+
+            p.Add (typeof (TokenKwCmpEQ),  200);
+            p.Add (typeof (TokenKwCmpNE),  200);
+
+            p.Add (typeof (TokenKwCmpLT),  240);
+            p.Add (typeof (TokenKwCmpLE),  240);
+            p.Add (typeof (TokenKwCmpGT),  240);
+            p.Add (typeof (TokenKwCmpGE),  240);
+
+            p.Add (typeof (TokenKwRSh),    260);
+            p.Add (typeof (TokenKwLSh),    260);
+
+            p.Add (typeof (TokenKwAdd),    280);
+            p.Add (typeof (TokenKwSub),    280);
+
+            p.Add (typeof (TokenKwMul),    320);
+            p.Add (typeof (TokenKwDiv),    320);
+            p.Add (typeof (TokenKwMod),    320);
+
+            return p;
+        }
+
+        /**
+         * @brief Reduce raw token stream to a single script token.
+         *        Performs a little semantic testing, ie, undefined variables, etc.
+         * @param tokenBegin = points to a TokenBegin
+         *                     followed by raw tokens
+         *                     and last token is a TokenEnd
+         * @returns null: not a valid script, error messages have been output
+         *          else: valid script top token
+         */
+        public static TokenScript Reduce (TokenBegin tokenBegin)
+        {
+            return new ScriptReduce (tokenBegin).tokenScript;
+        }
+
+        /*
+         * Instance variables.
+         */
+        private bool errors = false;
+        private string lastErrorFile = "";
+        private int lastErrorLine = 0;
+        private int numTypedefs = 0;
+        private TokenDeclVar currentDeclFunc = null;
+        private TokenDeclSDType currentDeclSDType = null;
+        private TokenScript tokenScript;
+        private TokenStmtBlock currentStmtBlock = null;
+
+        /**
+         * @brief the constructor does all the processing.
+         * @param token = first token of script after the TokenBegin token
+         * @returns tokenScript = null: there were errors
+         *                        else: successful
+         */
+        private ScriptReduce (TokenBegin tokenBegin)
+        {
+            /*
+             * Create a place to put the top-level script components,
+             * eg, state bodies, functions, global variables.
+             */
+            tokenScript = new TokenScript (tokenBegin.nextToken);
+            tokenScript.expiryDays = tokenBegin.expiryDays;
+
+            /*
+             * 'class', 'delegate', 'instance' all define types.
+             * So we pre-scan the source tokens for those keywords
+             * to build a script-defined type table and substitute
+             * type tokens for those names in the source.  This is
+             * done as a separate scan so they can cross-reference
+             * each other.  Also does likewise for fixed array types.
+             *
+             * Also, all 'typedef's are processed here.  Their definitions 
+             * remain in the source token stream after this, but they can 
+             * be skipped over, because their bodies have been substituted 
+             * in the source for any references.
+             */
+            ParseSDTypePreScanPassOne (tokenBegin);  // catalog definitions
+            ParseSDTypePreScanPassTwo (tokenBegin);  // substitute references
+
+            /*
+            int braces = 0;
+            Token prevTok = null;
+            for (Token token = tokenBegin; token != null; token = token.nextToken) {
+                if (token is TokenKwParClose) braces -= 2;
+                if (token is TokenKwBrcClose) braces -= 4;
+                StringBuilder sb = new StringBuilder ("ScriptReduce*: ");
+                sb.Append (token.GetHashCode ().ToString ("X8"));
+                sb.Append (" ");
+                sb.Append (token.line.ToString ().PadLeft (3));
+                sb.Append (".");
+                sb.Append (token.posn.ToString ().PadLeft (3));
+                sb.Append ("  ");
+                sb.Append (token.GetType ().Name.PadRight (24));
+                sb.Append (" : ");
+                for (int i = 0; i < braces; i ++) sb.Append (' ');
+                token.DebString (sb);
+                Console.WriteLine (sb.ToString ());
+                if (token.prevToken != prevTok) {
+                    Console.WriteLine ("ScriptReduce*:  -- prevToken link bad => " + token.prevToken.GetHashCode ().ToString ("X8"));
+                }
+                if (token is TokenKwBrcOpen) braces += 4;
+                if (token is TokenKwParOpen) braces += 2;
+                prevTok = token;
+            }
+            */
+
+            /*
+             * Create a function $globalvarinit to hold all explicit
+             * global variable initializations.
+             */
+            TokenDeclVar gviFunc   = new TokenDeclVar (tokenBegin, null, tokenScript);
+            gviFunc.name           = new TokenName (gviFunc, "$globalvarinit");
+            gviFunc.retType        = new TokenTypeVoid (gviFunc);
+            gviFunc.argDecl        = new TokenArgDecl (gviFunc);
+            TokenStmtBlock gviBody = new TokenStmtBlock (gviFunc);
+            gviBody.function       = gviFunc;
+            gviFunc.body           = gviBody;
+            tokenScript.globalVarInit = gviFunc;
+            tokenScript.AddVarEntry (gviFunc);
+
+            /*
+             * Scan through the tokens until we reach the end.
+             */
+            for (Token token = tokenBegin.nextToken; !(token is TokenEnd);) {
+                if (token is TokenKwSemi) {
+                    token = token.nextToken;
+                    continue;
+                }
+
+                /*
+                 * Script-defined type declarations.
+                 */
+                if (ParseDeclSDTypes (ref token, null, SDT_PUBLIC)) continue;
+
+                /*
+                 * constant <name> = <rval> ;
+                 */
+                if (token is TokenKwConst) {
+                    ParseDeclVar (ref token, null);
+                    continue;
+                }
+
+                /*
+                 * <type> <name> ;
+                 * <type> <name> = <rval> ;
+                 */
+                if ((token is TokenType) &&
+                    (token.nextToken is TokenName) &&
+                    ((token.nextToken.nextToken is TokenKwSemi) || 
+                     (token.nextToken.nextToken is TokenKwAssign))) {
+                    TokenDeclVar var = ParseDeclVar (ref token, gviFunc);
+                    if (var != null) {
+                        // <name> = <init>;
+                        TokenLValName left = new TokenLValName (var.name, tokenScript.variablesStack);
+                        DoVarInit (gviFunc, left, var.init);
+                    }
+                    continue;
+                }
+
+                /*
+                 * <type> <name> { [ get { <body> } ] [ set { <body> } ] }
+                 */
+                if ((token is TokenType) &&
+                    (token.nextToken is TokenName) &&
+                    (token.nextToken.nextToken is TokenKwBrcOpen)) {
+                    ParseProperty (ref token, false, true);
+                    continue;
+                }
+
+                /*
+                 * <type> <name> <funcargs> <funcbody>
+                 * global function returning specified type
+                 */
+                if (token is TokenType) {
+                    TokenType tokenType = (TokenType)token;
+
+                    token = token.nextToken;
+                    if (!(token is TokenName)) {
+                        ErrorMsg (token, "expecting variable/function name");
+                        token = SkipPastSemi (token);
+                        continue;
+                    }
+                    TokenName tokenName = (TokenName)token;
+                    token = token.nextToken;
+                    if (!(token is TokenKwParOpen)) {
+                        ErrorMsg (token, "<type> <name> must be followed by ; = or (");
+                        token = SkipPastSemi (token);
+                        continue;
+                    }
+                    token = tokenType;
+                    TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, false, false, false);
+                    if (tokenDeclFunc == null) continue;
+                    if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
+                        ErrorMsg (tokenName, "duplicate function " + tokenDeclFunc.funcNameSig.val);
+                    }
+                    continue;
+                }
+
+                /*
+                 * <name> <funcargs> <funcbody>
+                 * global function returning void
+                 */
+                if (token is TokenName) {
+                    TokenName tokenName = (TokenName)token;
+                    token = token.nextToken;
+                    if (!(token is TokenKwParOpen)) {
+                        ErrorMsg (token, "looking for open paren after assuming " + 
+                                         tokenName.val + " is a function name");
+                        token = SkipPastSemi (token);
+                        continue;
+                    }
+                    token = tokenName;
+                    TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, false, false, false);
+                    if (tokenDeclFunc == null) continue;
+                    if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
+                        ErrorMsg (tokenName, "duplicate function " + tokenDeclFunc.funcNameSig.val);
+                    }
+                    continue;
+                }
+
+                /*
+                 * default <statebody>
+                 */
+                if (token is TokenKwDefault) {
+                    TokenDeclState tokenDeclState = new TokenDeclState (token);
+                    token = token.nextToken;
+                    tokenDeclState.body = ParseStateBody (ref token);
+                    if (tokenDeclState.body == null) continue;
+                    if (tokenScript.defaultState != null) {
+                        ErrorMsg (tokenDeclState, "default state already declared");
+                        continue;
+                    }
+                    tokenScript.defaultState = tokenDeclState;
+                    continue;
+                }
+
+                /*
+                 * state <name> <statebody>
+                 */
+                if (token is TokenKwState) {
+                    TokenDeclState tokenDeclState = new TokenDeclState (token);
+                    token = token.nextToken;
+                    if (!(token is TokenName)) {
+                        ErrorMsg (token, "state must be followed by state name");
+                        token = SkipPastSemi (token);
+                        continue;
+                    }
+                    tokenDeclState.name = (TokenName)token;
+                    token = token.nextToken;
+                    tokenDeclState.body = ParseStateBody (ref token);
+                    if (tokenDeclState.body == null) continue;
+                    if (tokenScript.states.ContainsKey (tokenDeclState.name.val)) {
+                        ErrorMsg (tokenDeclState.name, "duplicate state definition");
+                        continue;
+                    }
+                    tokenScript.states.Add (tokenDeclState.name.val, tokenDeclState);
+                    continue;
+                }
+
+                /*
+                 * Doesn't fit any of those forms, output message and skip to next statement.
+                 */
+                ErrorMsg (token, "looking for var name, type, state or default, script-defined type declaration");
+                token = SkipPastSemi (token);
+                continue;
+            }
+
+            /*
+             * Must have a default state to start in.
+             */
+            if (!errors && (tokenScript.defaultState == null)) {
+                ErrorMsg (tokenScript, "no default state defined");
+            }
+
+            /*
+             * If any error messages were written out, set return value to null.
+             */
+            if (errors) tokenScript = null;
+        }
+
+        /**
+         * @brief Pre-scan the source for class, delegate, interface, typedef definition keywords.
+         *        Clump the keywords and name being defined together, but leave the body intact.
+         *        In the case of a delegate with an explicit return type, it reverses the name and return type.
+         *        After this completes there shouldn't be any TokenKw{Class,Delegate,Interface,Typedef} 
+         *        keywords in the source, they are all replaced by TokenDeclSDType{Class,Delegate,Interface,
+         *        Typedef} tokens which also encapsulate the name of the type being defined and any generic 
+         *        parameter names.  The body remains intact in the source token stream following the 
+         *        TokenDeclSDType* token.
+         */
+        private void ParseSDTypePreScanPassOne (Token tokenBegin)
+        {
+            Stack<int> braceLevels = new Stack<int> ();
+            Stack<TokenDeclSDType> outerLevels = new Stack<TokenDeclSDType> ();
+            int openBraceLevel = 0;
+            braceLevels.Push (-1);
+            outerLevels.Push (null);
+
+            for (Token t = tokenBegin; !((t = t.nextToken) is TokenEnd);) {
+
+                /*
+                 * Keep track of nested definitions so we can link them up.
+                 * We also need to detect the end of class and interface definitions.
+                 */
+                if (t is TokenKwBrcOpen) {
+                    openBraceLevel ++;
+                    continue;
+                }
+                if (t is TokenKwBrcClose) {
+                    if (-- openBraceLevel < 0) {
+                        ErrorMsg (t, "{ } mismatch");
+                        return;
+                    }
+                    if (braceLevels.Peek () == openBraceLevel) {
+                        braceLevels.Pop ();
+                        outerLevels.Pop ().endToken = t;
+                    }
+                    continue;
+                }
+
+                /*
+                 * Check for 'class' or 'interface'.
+                 * They always define a new class or interface.
+                 * They can contain nested script-defined type definitions.
+                 */
+                if ((t is TokenKwClass) || (t is TokenKwInterface)) {
+                    Token kw = t;
+                    t = t.nextToken;
+                    if (!(t is TokenName)) {
+                        ErrorMsg (t, "expecting class or interface name");
+                        t = SkipPastSemi (t).prevToken;
+                        continue;
+                    }
+                    TokenName name = (TokenName)t;
+                    t = t.nextToken;
+
+                    /*
+                     * Malloc the script-defined type object.
+                     */
+                    TokenDeclSDType decl;
+                    if (kw is TokenKwClass) decl = new TokenDeclSDTypeClass (name, kw.prevToken is TokenKwPartial);
+                                   else decl = new TokenDeclSDTypeInterface (name);
+                    decl.outerSDType = outerLevels.Peek ();
+
+                    /*
+                     * Check for generic parameter list.
+                     */
+                    if (!ParseGenProtoParamList (ref t, decl)) continue;
+
+                    /*
+                     * Splice in a TokenDeclSDType token that replaces the keyword and the name tokens 
+                     * and any generic parameters including the '<', ','s and '>'.
+                     *   kw = points to 'class' or 'interface' keyword.
+                     *    t = points to just past last part of class name parsed, hopefully a ':' or '{'.
+                     */
+                    decl.prevToken = decl.isPartial ? kw.prevToken.prevToken : kw.prevToken;
+                    decl.nextToken = t;
+                    decl.prevToken.nextToken = decl;
+                    decl.nextToken.prevToken = decl;
+
+                    /*
+                     * Enter it in name lists so it can be seen by others.
+                     */
+                    Token partialNewBody = CatalogSDTypeDecl (decl);
+
+                    /*
+                     * Start inner type definitions.
+                     */
+                    braceLevels.Push (openBraceLevel);
+                    outerLevels.Push (decl);
+
+                    /*
+                     * Scan the body starting on for before the '{'.
+                     *
+                     * If this body had an old partial merged into it,
+                     * resume scanning at the beginning of the new body, 
+                     * ie, what used to be the first token after the '{' 
+                     * before the old body was spliced in.
+                     */
+                    if (partialNewBody != null) {
+
+                        /*
+                         * We have a partial that has had old partial body merged 
+                         * into new partial body.  So resume scanning at the beginning 
+                         * of the new partial body so we don't get any duplicate scanning 
+                         * of the old partial body.
+                         *
+                         *   <decl> ... { <oldbody> <newbody> }
+                         *                          ^- resume scanning here
+                         *                             but inc openBraceLevel because
+                         *                             we skipped scanning the '{'
+                         */
+                        openBraceLevel ++;
+                        t = partialNewBody;
+                    }
+                    t = t.prevToken;
+                    continue;
+                }
+
+                /*
+                 * Check for 'delegate'.
+                 * It always defines a new delegate.
+                 * Delegates never define nested types.
+                 */
+                if (t is TokenKwDelegate) {
+                    Token kw = t;
+                    t = t.nextToken;
+
+                    /*
+                     * Next thing might be an explicit return type or the delegate's name.
+                     * If it's a type token, then it's the return type, simple enough.
+                     * But if it's a name token, it might be the name of some other script-defined type.
+                     * The way to tell is that the delegate name is followed by a '(', whereas an 
+                     * explicit return type is followed by the delegate name.
+                     */
+                    Token retType = t;
+                    TokenName delName = null;
+                    Token u;
+                    int angles = 0;
+                    for (u = t; !(u is TokenKwParOpen); u = u.nextToken) {
+                        if ((u is TokenKwSemi) || (u is TokenEnd)) break;
+                        if (u is TokenKwCmpLT) angles ++;
+                        if (u is TokenKwCmpGT) angles --;
+                        if (u is TokenKwRSh)   angles -= 2;  // idiot >>
+                        if ((angles == 0) && (u is TokenName)) delName = (TokenName)u;
+                    }
+                    if (!(u is TokenKwParOpen)) {
+                        ErrorMsg (u, "expecting ( for delegate parameter list");
+                        t = SkipPastSemi (t).prevToken;
+                        continue;
+                    }
+                    if (delName == null) {
+                        ErrorMsg (u, "expecting delegate name");
+                        t = SkipPastSemi (t).prevToken;
+                        continue;
+                    }
+                    if (retType == delName) retType = null;
+
+                    /*
+                     * Malloc the script-defined type object.
+                     */
+                    TokenDeclSDTypeDelegate decl = new TokenDeclSDTypeDelegate (delName);
+                    decl.outerSDType = outerLevels.Peek ();
+
+                    /*
+                     * Check for generic parameter list.
+                     */
+                    t = delName.nextToken;
+                    if (!ParseGenProtoParamList (ref t, decl)) continue;
+
+                    /*
+                     * Enter it in name lists so it can be seen by others.
+                     */
+                    CatalogSDTypeDecl (decl);
+
+                    /*
+                     * Splice in the token that replaces the 'delegate' keyword and the whole name 
+                     * (including the '<' name ... '>' parts). The return type token(s), if any, 
+                     * follow the splice token and come before the '('.
+                     */
+                    decl.prevToken = kw.prevToken;
+                    kw.prevToken.nextToken = decl;
+
+                    if (retType == null) {
+                        decl.nextToken = t;
+                        t.prevToken = decl;
+                    } else {
+                        decl.nextToken = retType;
+                        retType.prevToken = decl;
+                        retType.nextToken = t;
+                        t.prevToken = retType;
+                    }
+
+                    /*
+                     * Scan for terminating ';'.
+                     * There cannot be an intervening class, delegate, interfate, typedef, { or }.
+                     */
+                    for (t = decl; !(t is TokenKwSemi); t = u) {
+                        u = t.nextToken;
+                        if ((u is TokenEnd) || 
+                            (u is TokenKwClass) ||
+                            (u is TokenKwDelegate) ||
+                            (u is TokenKwInterface) ||
+                            (u is TokenKwTypedef) ||
+                            (u is TokenKwBrcOpen) || 
+                            (u is TokenKwBrcClose)) {
+                            ErrorMsg (t, "delegate missing terminating ;");
+                            break;
+                        }
+                    }
+                    decl.endToken = t;
+                    continue;
+                }
+
+                /*
+                 * Check for 'typedef'.
+                 * It always defines a new macro.
+                 * Typedefs never define nested types.
+                 */
+                if (t is TokenKwTypedef) {
+                    Token kw = t;
+                    t = t.nextToken;
+
+                    if (!(t is TokenName)) {
+                        ErrorMsg (t, "expecting typedef name");
+                        t = SkipPastSemi (t).prevToken;
+                        continue;
+                    }
+                    TokenName tdName = (TokenName)t;
+                    t = t.nextToken;
+
+                    /*
+                     * Malloc the script-defined type object.
+                     */
+                    TokenDeclSDTypeTypedef decl = new TokenDeclSDTypeTypedef (tdName);
+                    decl.outerSDType = outerLevels.Peek ();
+
+                    /*
+                     * Check for generic parameter list.
+                     */
+                    if (!ParseGenProtoParamList (ref t, decl)) continue;
+
+                    /*
+                     * Enter it in name lists so it can be seen by others.
+                     */
+                    CatalogSDTypeDecl (decl);
+                    numTypedefs ++;
+
+                    /*
+                     * Splice in the token that replaces the 'typedef' keyword and the whole name 
+                     * (including the '<' name ... '>' parts).
+                     */
+                    decl.prevToken = kw.prevToken;
+                    kw.prevToken.nextToken = decl;
+                    decl.nextToken = t;
+                    t.prevToken = decl;
+
+                    /*
+                     * Scan for terminating ';'.
+                     * There cannot be an intervening class, delegate, interfate, typedef, { or }.
+                     */
+                    Token u;
+                    for (t = decl; !(t is TokenKwSemi); t = u) {
+                        u = t.nextToken;
+                        if ((u is TokenEnd) || 
+                            (u is TokenKwClass) ||
+                            (u is TokenKwDelegate) ||
+                            (u is TokenKwInterface) ||
+                            (u is TokenKwTypedef) ||
+                            (u is TokenKwBrcOpen) || 
+                            (u is TokenKwBrcClose)) {
+                            ErrorMsg (t, "typedef missing terminating ;");
+                            break;
+                        }
+                    }
+                    decl.endToken = t;
+                    continue;
+                }
+            }
+        }
+
+        /**
+         * @brief Parse a possibly generic type definition's parameter list.
+         * @param t = points to the possible opening '<' on entry
+         *            points just past the closing '>' on return
+         * @param decl = the generic type being declared
+         * @returns false: parse error
+         *           true: decl.genParams = filled in with parameter list
+         *                 decl.innerSDTypes = filled in with parameter list
+         */
+        private bool ParseGenProtoParamList (ref Token t, TokenDeclSDType decl)
+        {
+            /*
+             * Maybe there aren't any generic parameters.
+             * If so, leave decl.genParams = null.
+             */
+            if (!(t is TokenKwCmpLT)) return true;
+
+            /*
+             * Build list of generic parameter names.
+             */
+            Dictionary<string, int> parms = new Dictionary<string, int> ();
+            do {
+                t = t.nextToken;
+                if (!(t is TokenName)) {
+                    ErrorMsg (t, "expecting generic parameter name");
+                    break;
+                }
+                TokenName tn = (TokenName)t;
+                if (parms.ContainsKey (tn.val)) {
+                    ErrorMsg (tn, "duplicate use of generic parameter name");
+                } else {
+                    parms.Add (tn.val, parms.Count);
+                }
+                t = t.nextToken;
+            } while (t is TokenKwComma);
+            if (!(t is TokenKwCmpGT)) {
+                ErrorMsg (t, "expecting , for more params or > to end param list");
+                return false;
+            }
+            t = t.nextToken;
+            decl.genParams = parms;
+
+            return true;
+        }
+
+        /**
+         * @brief Catalog a script-defined type.
+         *        Its short name (eg, 'Node') gets put in the next outer level (eg, 'List')'s inner type definition table.
+         *        Its long name (eg, 'List.Node') gets put in the global type definition table.
+         */
+        public Token CatalogSDTypeDecl (TokenDeclSDType decl)
+        {
+            string longName = decl.longName.val;
+            TokenDeclSDType dupDecl;
+            if (!tokenScript.sdSrcTypesTryGetValue (longName, out dupDecl)) {
+                tokenScript.sdSrcTypesAdd (longName, decl);
+                if (decl.outerSDType != null) {
+                    decl.outerSDType.innerSDTypes.Add (decl.shortName.val, decl);
+                }
+                return null;
+            }
+
+            if (!dupDecl.isPartial || !decl.isPartial) {
+                ErrorMsg (decl, "duplicate definition of type " + longName);
+                ErrorMsg (dupDecl, "previous definition here");
+                return null;
+            }
+
+            if (!GenericParametersMatch (decl, dupDecl)) {
+                ErrorMsg (decl, "all partial class generic parameters must match");
+            }
+
+            /*
+             * Have new declaration be the cataloged one because body is going to get 
+             * snipped out of old declaration and pasted into new declaration.
+             */
+            tokenScript.sdSrcTypesRep (longName, decl);
+            if (decl.outerSDType != null) {
+                 decl.outerSDType.innerSDTypes[decl.shortName.val] = decl;
+            }
+
+            /*
+             * Find old partial definition's opening brace.
+             */
+            Token dupBrcOpen;
+            for (dupBrcOpen = dupDecl; !(dupBrcOpen is TokenKwBrcOpen); dupBrcOpen = dupBrcOpen.nextToken) {
+                if (dupBrcOpen == dupDecl.endToken) {
+                    ErrorMsg (dupDecl, "missing {");
+                    return null;
+                }
+            }
+
+            /*
+             * Find new partial definition's opening brace.
+             */
+            Token brcOpen;
+            for (brcOpen = decl; !(brcOpen is TokenKwBrcOpen); brcOpen = brcOpen.nextToken) {
+                if (brcOpen is TokenEnd) {
+                    ErrorMsg (decl, "missing {");
+                    return null;
+                }
+            }
+            Token body = brcOpen.nextToken;
+
+            /*
+             * Stick old partial definition's extends/implementeds list just 
+             * in front of new partial definition's extends/implementeds list.
+             *
+             *    class    oldextimp    {          oldbody    }                 ...
+             *   dupDecl               dupBrcOpen            dupDecl.endToken
+             *
+             *    class    newextimp    {          newbody    }                 ...
+             *   decl                  brcOpen    body       decl.endToken
+             *
+             * becomes
+             *
+             *    class             ...
+             *   dupDecl
+             *   dupDecl.endToken
+             *
+             *    class    oldextimp   newextimp    {          oldbody   newbody    }              ...
+             *   decl                              brcOpen              body       decl.endToken
+             */
+            if (dupBrcOpen != dupDecl.nextToken) {
+                dupBrcOpen.prevToken.nextToken = decl.nextToken;
+                dupDecl.nextToken.prevToken = decl;
+                decl.nextToken.prevToken = dupBrcOpen.prevToken;
+                decl.nextToken = dupDecl.nextToken;
+            }
+
+            /*
+             * Stick old partial definition's body just 
+             * in front of new partial definition's body.
+             */
+            if (dupBrcOpen.nextToken != dupDecl.endToken) {
+                dupBrcOpen.nextToken.prevToken = brcOpen;
+                dupDecl.endToken.prevToken.nextToken = body;
+                body.prevToken = dupDecl.endToken.prevToken;
+                brcOpen.nextToken = dupBrcOpen.nextToken;
+            }
+
+            /*
+             * Null out old definition's extends/implementeds list and body 
+             * by having the declaration token be the only thing left.
+             */
+            dupDecl.nextToken = dupDecl.endToken.nextToken;
+            dupDecl.nextToken.prevToken = dupDecl;
+            dupDecl.endToken = dupDecl;
+
+            return body;
+        }
+
+        /**
+         * @brief Determine whether or not the generic parameters of two class declarations match exactly.
+         */
+        private static bool GenericParametersMatch (TokenDeclSDType c1, TokenDeclSDType c2)
+        {
+            if ((c1.genParams == null) && (c2.genParams == null)) return true;
+            if ((c1.genParams == null) || (c2.genParams == null)) return false;
+            Dictionary<string, int> gp1 = c1.genParams;
+            Dictionary<string, int> gp2 = c2.genParams;
+            if (gp1.Count != gp2.Count) return false;
+            foreach (KeyValuePair<string, int> kvp1 in gp1) {
+                int v2;
+                if (!gp2.TryGetValue (kvp1.Key, out v2)) return false;
+                if (v2 != kvp1.Value) return false;
+            }
+            return true;
+        }
+
+        /**
+         * @brief Replace all TokenName tokens that refer to the script-defined types with 
+         *        corresponding TokenTypeSDType{Class,Delegate,GenParam,Interface} tokens.
+         *        Also handle generic references, ie, recognize that 'List<integer>' is an
+         *        instantiation of 'List<>' and instantiate the generic.
+         */
+        private const uint REPEAT_NOTYPE = 1;
+        private const uint REPEAT_INSTGEN = 2;
+        private const uint REPEAT_SUBST = 4;
+
+        private void ParseSDTypePreScanPassTwo (Token tokenBegin)
+        {
+            List<Token> noTypes = new List<Token> ();
+            TokenDeclSDType outerSDType;
+            uint repeat;
+
+            do {
+                repeat = 0;
+                outerSDType = null;
+                noTypes.Clear ();
+
+                for (Token t = tokenBegin; !((t = t.nextToken) is TokenEnd);) {
+
+                    /*
+                     * Maybe it's time to pop out of an outer class definition.
+                     */
+                    if ((outerSDType != null) && (outerSDType.endToken == t)) {
+                        outerSDType = outerSDType.outerSDType;
+                        continue;
+                    }
+
+                    /*
+                     * Skip completely over any script-defined generic prototypes.
+                     * We only need to process their instantiations which are non-
+                     * generic versions of the generics.
+                     */
+                    if ((t is TokenDeclSDType) && (((TokenDeclSDType)t).genParams != null)) {
+                        t = ((TokenDeclSDType)t).endToken;
+                        continue;
+                    }
+
+                    /*
+                     * Check for beginning of non-generic script-defined type definitions.
+                     * They can have nested definitions in their innerSDTypes[] that match 
+                     * name tokens, so add them to the stack.
+                     *
+                     * But just ignore any preliminary partial definitions as they have had 
+                     * their entire contents spliced out and spliced into a subsequent partial 
+                     * definition.  So if we originally had:
+                     *    partial class Abc { public intenger one; }
+                     *    partial class Abc { public intenger two; }
+                     * We now have:
+                     *    partial_class_Abc    <== if we are here, just ignore the partial_class_Abc token
+                     *    partial_class_Abc { public intenger one; public intenger two; }
+                     */
+                    if (t is TokenDeclSDType) {
+                        if (((TokenDeclSDType)t).endToken != t) {
+                            outerSDType = (TokenDeclSDType)t;
+                        }
+                        continue;
+                    }
+
+                    /*
+                     * For names not preceded by a '.', scan the script-defined type definition 
+                     * stack for that name.  Splice the name out and replace with equivalent token.
+                     */
+                    if ((t is TokenName) && !(t.prevToken is TokenKwDot)) {
+                        t = TrySpliceTypeRef (t, outerSDType, ref repeat, noTypes);
+                    }
+
+                    /*
+                     * This handles types such as integer[,][], List<string>[], etc.
+                     * They are an instantiation of an internally generated type of the same name, brackets and all.
+                     * Note that to malloc an array, use something like 'new float[,][](3,5)', not 'new float[3,5][]'.
+                     *
+                     * Note that we must not get confused by $idxprop property declarations such as:
+                     *    float [string kee] { get { ... } }
+                     * ... and try to convert 'float' '[' to an array type.
+                     */
+                    if ((t is TokenType) && (t.nextToken is TokenKwBrkOpen)) {
+                        if ((t.nextToken.nextToken is TokenKwBrkClose) || 
+                            (t.nextToken.nextToken is TokenKwComma)) {
+                            t = InstantiateJaggedArray (t, tokenBegin, ref repeat);
+                        }
+                    }
+                }
+
+                /*
+                 * If we instantiated a generic, loop back to process its contents
+                 * just as if the source code had the instantiated code to begin with.
+                 * Also repeat if we found a non-type inside the <> of a generic reference 
+                 * provided we have made at least one name->type substitution.
+                 */
+            } while (((repeat & REPEAT_INSTGEN) != 0) || 
+                     ((repeat & (REPEAT_NOTYPE | REPEAT_SUBST)) == (REPEAT_NOTYPE | REPEAT_SUBST)));
+
+            /*
+             * These are places where we required a type be present, 
+             * eg, a generic type argument or the body of a typedef.
+             */
+            foreach (Token t in noTypes) {
+                ErrorMsg (t, "looking for type");
+            }
+        }
+
+        /**
+         * @brief Try to convert the source token string to a type reference
+         *        and splice the type reference into the source token string
+         *        replacing the original token(s).
+         * @param t = points to the initial TokenName token
+         * @param outerSDType = null: this is a top-level code reference
+         *                      else: this code is within outerSDType
+         * @returns pointer to last token parsed
+         *          possibly with spliced-in type token
+         *          repeat = possibly set true if need to do another pass
+         */
+        private Token TrySpliceTypeRef (Token t, TokenDeclSDType outerSDType, ref uint repeat, List<Token> noTypes)
+        {
+            Token start = t;
+            string tnamestr = ((TokenName)t).val;
+
+            /*
+             * Look for the name as a type declared by outerSDType or anything
+             * even farther out than that.  If not found, simply return 
+             * without updating t, meaning that t isn't the name of a type.
+             */
+            TokenDeclSDType decl = null;
+            while (outerSDType != null) {
+                if (outerSDType.innerSDTypes.TryGetValue (tnamestr, out decl)) break;
+                outerSDType = outerSDType.outerSDType;
+            }
+            if ((outerSDType == null) && !tokenScript.sdSrcTypesTryGetValue (tnamestr, out decl)) return t;
+
+            TokenDeclSDType instdecl;
+            while (true) {
+
+                /*
+                 * If it is a generic type, it must be followed by instantiation arguments.
+                 */
+                instdecl = decl;
+                if (decl.genParams != null) {
+                    t = t.nextToken;
+                    if (!(t is TokenKwCmpLT)) {
+                        ErrorMsg (t, "expecting < for generic argument list");
+                        return t;
+                    }
+                    tnamestr += "<";
+                    int nArgs = decl.genParams.Count;
+                    TokenType[] genArgs = new TokenType[nArgs];
+                    for (int i = 0; i < nArgs;) {
+                        t = t.nextToken;
+                        if (!(t is TokenType)) {
+                            repeat |= REPEAT_NOTYPE;
+                            noTypes.Add (t);
+                            return t.prevToken;  // make sure name gets processed
+                                                 // so substitution can occur on it
+                        }
+                        TokenType ga = (TokenType)t;
+                        genArgs[i] = ga;
+                        tnamestr += ga.ToString ();
+                        t = t.nextToken;
+                        if (++ i < nArgs) {
+                            if (!(t is TokenKwComma)) {
+                                ErrorMsg (t, "expecting , for more generic arguments");
+                                return t;
+                            }
+                            tnamestr += ",";
+                        }
+                    }
+                    if (t is TokenKwRSh) {  // idiot >>
+                        Token u = new TokenKwCmpGT (t);
+                        Token v = new TokenKwCmpGT (t);
+                        v.posn ++;
+                        u.prevToken = t.prevToken;
+                        u.nextToken = v;
+                        v.nextToken = t.nextToken;
+                        v.prevToken = u;
+                        u.prevToken.nextToken = u;
+                        v.nextToken.prevToken = v;
+                        t = u;
+                    }
+                    if (!(t is TokenKwCmpGT)) {
+                        ErrorMsg (t, "expecting > at end of generic argument list");
+                        return t;
+                    }
+                    tnamestr += ">";
+                    if (outerSDType != null) {
+                        outerSDType.innerSDTypes.TryGetValue (tnamestr, out instdecl);
+                    } else {
+                        tokenScript.sdSrcTypesTryGetValue (tnamestr, out instdecl);
+                    }
+
+                    /*
+                     * Couldn't find 'List<string>' but found 'List' and we have genArgs = 'string'.
+                     * Instantiate the generic to create 'List<string>'.  This splices the definition 
+                     * of 'List<string>' into the source token stream just as if it had been there all 
+                     * along.  We have to then repeat the scan to process the instance's contents.
+                     */
+                    if (instdecl == null) {
+                        instdecl = decl.InstantiateGeneric (tnamestr, genArgs, this);
+                        CatalogSDTypeDecl (instdecl);
+                        repeat |= REPEAT_INSTGEN;
+                    }
+                }
+
+                /*
+                 * Maybe caller wants a subtype by putting a '.' following all that.
+                 */
+                if (!(t.nextToken is TokenKwDot)) break;
+                if (!(t.nextToken.nextToken is TokenName)) break;
+                tnamestr = ((TokenName)t.nextToken.nextToken).val;
+                if (!instdecl.innerSDTypes.TryGetValue (tnamestr, out decl)) break;
+                t = t.nextToken.nextToken;
+                outerSDType = instdecl;
+            }
+
+            /*
+             * Create a reference in the source to the definition
+             * that encapsulates the long dotted type name given in
+             * the source, and replace the long dotted type name in
+             * the source with the reference token, eg, replace 
+             * 'Dictionary' '<' 'string' ',' 'integer' '>' '.' 'ValueList'
+             * with 'Dictionary<string,integer>.ValueList'.
+             */
+            TokenType refer = instdecl.MakeRefToken (start);
+            if (refer == null) {
+                // typedef body is not yet a type
+                noTypes.Add (start);
+                repeat |= REPEAT_NOTYPE;
+                return start;
+            }
+            refer.prevToken = start.prevToken;  // start points right at the first TokenName
+            refer.nextToken = t.nextToken;      // t points at the last TokenName or TokenKwCmpGT
+            refer.prevToken.nextToken = refer;
+            refer.nextToken.prevToken = refer;
+            repeat |= REPEAT_SUBST;
+
+            return refer;
+        }
+
+        /**
+         * @brief We are known to have <type>'[' so make an equivalent array type.
+         * @param t = points to the TokenType
+         * @param tokenBegin = where we can safely splice in new array class definitions
+         * @param repeat = set REPEAT_INSTGEN if new type created
+         * @returns pointer to last token parsed
+         *          possibly with spliced-in type token
+         *          repeat = possibly set true if need to do another pass
+         */
+        private Token InstantiateJaggedArray (Token t, Token tokenBegin, ref uint repeat)
+        {
+            Token start = t;
+            TokenType ofType = (TokenType)t;
+
+            Stack<int> ranks = new Stack<int> ();
+
+            /*
+             * When script specifies 'float[,][]' it means a two-dimensional matrix
+             * that points to one-dimensional vectors of floats.  So we would push 
+             * a 2 then a 1 in this parsing code...
+             */
+            do {
+                t = t.nextToken;                // point at '['
+                int rank = 0;
+                do {
+                    rank ++;                // count '[' and ','s
+                    t = t.nextToken;        // point at ',' or ']'
+                } while (t is TokenKwComma);
+                if (!(t is TokenKwBrkClose)) {
+                    ErrorMsg (t, "expecting only [ , or ] for array type specification");
+                    return t;
+                }
+                ranks.Push (rank);
+            } while (t.nextToken is TokenKwBrkOpen);
+
+            /*
+             * Now we build the types in reverse order.  For the example above we will:
+             *   first, create a type that is a one-dimensional vector of floats, float[]
+             *   second, create a type that is a two-dimensional matrix of that.
+             * This keeps declaration and referencing similar, eg, 
+             *   float[,][] jag = new float[,][] (3,4);
+             *     jag[i,j][k] ... is used to access the elements
+             */
+            do {
+                int rank = ranks.Pop ();
+                TokenDeclSDType decl = InstantiateFixedArray (rank, ofType, tokenBegin, ref repeat);
+                ofType = decl.MakeRefToken (ofType);
+            } while (ranks.Count > 0);
+
+            /*
+             * Finally splice in the resultant array type to replace the original tokens.
+             */
+            ofType.prevToken = start.prevToken;
+            ofType.nextToken = t.nextToken;
+            ofType.prevToken.nextToken = ofType;
+            ofType.nextToken.prevToken = ofType;
+
+            /*
+             * Resume parsing just after the spliced-in array type token.
+             */
+            return ofType;
+        }
+
+        /**
+         * @brief Instantiate a script-defined class type to handle fixed-dimension arrays.
+         * @param rank = number of dimensions for the array
+         * @param ofType = type of each element of the array
+         * @returns script-defined class declaration created to handle the array
+         */
+        private TokenDeclSDType InstantiateFixedArray (int rank, TokenType ofType, Token tokenBegin, ref uint repeat)
+        {
+            /*
+             * Create the array type's name.
+             * If starting with a non-array type, just append the rank to it, eg, float + rank=1 -> float[]
+             * If starting with an array type, slip this rank in front of existing array, eg, float[] + rank=2 -> float[,][].
+             * This makes it consistent with what the script-writer sees for both a type specification and when 
+             * referencing elements in a jagged array.
+             */
+            string name = ofType.ToString ();
+            StringBuilder sb = new StringBuilder (name);
+            int ix = name.IndexOf ('[');
+            if (ix < 0) ix = name.Length;
+            sb.Insert (ix ++, '[');
+            for (int i = 0; ++ i < rank;) {
+                sb.Insert (ix ++, ',');
+            }
+            sb.Insert (ix, ']');
+            name = sb.ToString ();
+
+            TokenDeclSDType fa;
+            if (!tokenScript.sdSrcTypesTryGetValue (name, out fa)) {
+                char suffix = 'O';
+                if (ofType is TokenTypeChar)  suffix = 'C';
+                if (ofType is TokenTypeFloat) suffix = 'F';
+                if (ofType is TokenTypeInt)   suffix = 'I';
+
+                /*
+                 * Don't already have one, create a new skeleton struct.
+                 * Splice in a definition for the class at beginning of source file.
+                 *
+                 *    class <arraytypename> {
+                 */
+                fa = new TokenDeclSDTypeClass (new TokenName (tokenScript, name), false);
+                CatalogSDTypeDecl (fa);
+                repeat |= REPEAT_INSTGEN;
+                ((TokenDeclSDTypeClass)fa).arrayOfType = ofType;
+                ((TokenDeclSDTypeClass)fa).arrayOfRank = rank;
+
+                Token t = SpliceAfter (tokenBegin, fa);
+                t = SpliceAfter (t, new TokenKwBrcOpen (t));
+
+                /*
+                 *        public integer len0;
+                 *        public integer len1;
+                 *        ...
+                 *        public object obj;
+                 */
+                for (int i = 0; i < rank; i ++) {
+                    t = SpliceAfter (t, new TokenKwPublic (t));
+                    t = SpliceAfter (t, new TokenTypeInt (t));
+                    t = SpliceAfter (t, new TokenName (t, "len" + i));
+                    t = SpliceAfter (t, new TokenKwSemi (t));
+                }
+
+                t = SpliceAfter (t, new TokenKwPublic (t));
+                t = SpliceAfter (t, new TokenTypeObject (t));
+                t = SpliceAfter (t, new TokenName (t, "obj"));
+                t = SpliceAfter (t, new TokenKwSemi (t));
+
+                /*
+                 *        public constructor (integer len0, integer len1, ...) {
+                 *            this.len0 = len0;
+                 *            this.len1 = len1;
+                 *            ...
+                 *            this.obj = xmrFixedArrayAlloc<suffix> (len0 * len1 * ...);
+                 *        }
+                 */
+                t = SpliceAfter (t, new TokenKwPublic (t));
+                t = SpliceAfter (t, new TokenKwConstructor (t));
+                t = SpliceAfter (t, new TokenKwParOpen (t));
+                for (int i = 0; i < rank; i ++) {
+                    if (i > 0) t = SpliceAfter (t, new TokenKwComma (t));
+                    t = SpliceAfter (t, new TokenTypeInt (t));
+                    t = SpliceAfter (t, new TokenName (t, "len" + i));
+                }
+                t = SpliceAfter (t, new TokenKwParClose (t));
+                t = SpliceAfter (t, new TokenKwBrcOpen (t));
+
+                for (int i = 0; i < rank; i ++) {
+                    t = SpliceAfter (t, new TokenKwThis (t));
+                    t = SpliceAfter (t, new TokenKwDot (t));
+                    t = SpliceAfter (t, new TokenName (t, "len" + i));
+                    t = SpliceAfter (t, new TokenKwAssign (t));
+                    t = SpliceAfter (t, new TokenName (t, "len" + i));
+                    t = SpliceAfter (t, new TokenKwSemi (t));
+                }
+
+                t = SpliceAfter (t, new TokenKwThis (t));
+                t = SpliceAfter (t, new TokenKwDot (t));
+                t = SpliceAfter (t, new TokenName (t, "obj"));
+                t = SpliceAfter (t, new TokenKwAssign (t));
+                t = SpliceAfter (t, new TokenName (t, "xmrFixedArrayAlloc" + suffix));
+                t = SpliceAfter (t, new TokenKwParOpen (t));
+                for (int i = 0; i < rank; i ++) {
+                    if (i > 0) t = SpliceAfter (t, new TokenKwMul (t));
+                    t = SpliceAfter (t, new TokenName (t, "len" + i));
+                }
+                t = SpliceAfter (t, new TokenKwParClose (t));
+                t = SpliceAfter (t, new TokenKwSemi (t));
+                t = SpliceAfter (t, new TokenKwBrcClose (t));
+
+                /*
+                 *        public integer Length { get {
+                 *            return this.len0 * this.len1 * ... ;
+                 *        } }
+                 */
+                t = SpliceAfter (t, new TokenKwPublic (t));
+                t = SpliceAfter (t, new TokenTypeInt (t));
+                t = SpliceAfter (t, new TokenName (t, "Length"));
+                t = SpliceAfter (t, new TokenKwBrcOpen (t));
+                t = SpliceAfter (t, new TokenKwGet (t));
+                t = SpliceAfter (t, new TokenKwBrcOpen (t));
+
+                t = SpliceAfter (t, new TokenKwRet (t));
+                for (int i = 0; i < rank; i ++) {
+                    if (i > 0) t = SpliceAfter (t, new TokenKwMul (t));
+                    t = SpliceAfter (t, new TokenKwThis (t));
+                    t = SpliceAfter (t, new TokenKwDot (t));
+                    t = SpliceAfter (t, new TokenName (t, "len" + i));
+                }
+                t = SpliceAfter (t, new TokenKwSemi (t));
+
+                t = SpliceAfter (t, new TokenKwBrcClose (t));
+                t = SpliceAfter (t, new TokenKwBrcClose (t));
+
+                /*
+                 *        public integer Length (integer dim) {
+                 *            switch (dim) {
+                 *                case 0: return this.len0;
+                 *                case 1: return this.len1;
+                 *                ...
+                 *            }
+                 *            return 0;
+                 *        }
+                 */
+                t = SpliceAfter (t, new TokenKwPublic (t));
+                t = SpliceAfter (t, new TokenTypeInt (t));
+                t = SpliceAfter (t, new TokenName (t, "Length"));
+                t = SpliceAfter (t, new TokenKwParOpen (t));
+                t = SpliceAfter (t, new TokenTypeInt (t));
+                t = SpliceAfter (t, new TokenName (t, "dim"));
+                t = SpliceAfter (t, new TokenKwParClose (t));
+                t = SpliceAfter (t, new TokenKwBrcOpen (t));
+
+                t = SpliceAfter (t, new TokenKwSwitch (t));
+                t = SpliceAfter (t, new TokenKwParOpen (t));
+                t = SpliceAfter (t, new TokenName (t, "dim"));
+                t = SpliceAfter (t, new TokenKwParClose (t));
+                t = SpliceAfter (t, new TokenKwBrcOpen (t));
+
+                for (int i = 0; i < rank; i ++) {
+                    t = SpliceAfter (t, new TokenKwCase (t));
+                    t = SpliceAfter (t, new TokenInt (t, i));
+                    t = SpliceAfter (t, new TokenKwColon (t));
+                    t = SpliceAfter (t, new TokenKwRet (t));
+                    t = SpliceAfter (t, new TokenKwThis (t));
+                    t = SpliceAfter (t, new TokenKwDot (t));
+                    t = SpliceAfter (t, new TokenName (t, "len" + i));
+                    t = SpliceAfter (t, new TokenKwSemi (t));
+                }
+                t = SpliceAfter (t, new TokenKwBrcClose (t));
+
+                t = SpliceAfter (t, new TokenKwRet (t));
+                t = SpliceAfter (t, new TokenInt (t, 0));
+                t = SpliceAfter (t, new TokenKwSemi (t));
+                t = SpliceAfter (t, new TokenKwBrcClose (t));
+
+                /*
+                 *        public integer Index (integer idx0, integet idx1, ...) {
+                 *            integer idx = idx0;
+                 *            idx *= this.len1; idx += idx1;
+                 *            idx *= this.len2; idx += idx2;
+                 *            ...
+                 *            return idx;
+                 *        }
+                 */
+                t = SpliceAfter (t, new TokenKwPublic (t));
+                t = SpliceAfter (t, new TokenTypeInt (t));
+                t = SpliceAfter (t, new TokenName (t, "Index"));
+                t = SpliceAfter (t, new TokenKwParOpen (t));
+                for (int i = 0; i < rank; i ++) {
+                    if (i > 0) t = SpliceAfter (t, new TokenKwComma (t));
+                    t = SpliceAfter (t, new TokenTypeInt (t));
+                    t = SpliceAfter (t, new TokenName (t, "idx" + i));
+                }
+                t = SpliceAfter (t, new TokenKwParClose (t));
+                t = SpliceAfter (t, new TokenKwBrcOpen (t));
+
+                t = SpliceAfter (t, new TokenTypeInt (t));
+                t = SpliceAfter (t, new TokenName (t, "idx"));
+                t = SpliceAfter (t, new TokenKwAssign (t));
+                t = SpliceAfter (t, new TokenName (t, "idx0"));
+                t = SpliceAfter (t, new TokenKwSemi (t));
+
+                for (int i = 1; i < rank; i ++) {
+                    t = SpliceAfter (t, new TokenName (t, "idx"));
+                    t = SpliceAfter (t, new TokenKwAsnMul (t));
+                    t = SpliceAfter (t, new TokenKwThis (t));
+                    t = SpliceAfter (t, new TokenKwDot (t));
+                    t = SpliceAfter (t, new TokenName (t, "len" + i));
+                    t = SpliceAfter (t, new TokenKwSemi (t));
+                    t = SpliceAfter (t, new TokenName (t, "idx"));
+                    t = SpliceAfter (t, new TokenKwAsnAdd (t));
+                    t = SpliceAfter (t, new TokenName (t, "idx" + i));
+                    t = SpliceAfter (t, new TokenKwSemi (t));
+                }
+
+                t = SpliceAfter (t, new TokenKwRet (t));
+                t = SpliceAfter (t, new TokenName (t, "idx"));
+                t = SpliceAfter (t, new TokenKwSemi (t));
+                t = SpliceAfter (t, new TokenKwBrcClose (t));
+
+                /*
+                 *        public <oftype> Get (integer idx0, integet idx1, ...) {
+                 *            integer idx = idx0;
+                 *            idx *= this.len1; idx += idx1;
+                 *            idx *= this.len2; idx += idx2;
+                 *            ...
+                 *            return (<oftype>) xmrFixedArrayGet<suffix> (this.obj, idx);
+                 *        }
+                 */
+                t = SpliceAfter (t, new TokenKwPublic (t));
+                t = SpliceAfter (t, ofType.CopyToken (t));
+                t = SpliceAfter (t, new TokenName (t, "Get"));
+                t = SpliceAfter (t, new TokenKwParOpen (t));
+                for (int i = 0; i < rank; i ++) {
+                    if (i > 0) t = SpliceAfter (t, new TokenKwComma (t));
+                    t = SpliceAfter (t, new TokenTypeInt (t));
+                    t = SpliceAfter (t, new TokenName (t, "idx" + i));
+                }
+                t = SpliceAfter (t, new TokenKwParClose (t));
+                t = SpliceAfter (t, new TokenKwBrcOpen (t));
+
+                t = SpliceAfter (t, new TokenTypeInt (t));
+                t = SpliceAfter (t, new TokenName (t, "idx"));
+                t = SpliceAfter (t, new TokenKwAssign (t));
+                t = SpliceAfter (t, new TokenName (t, "idx0"));
+                t = SpliceAfter (t, new TokenKwSemi (t));
+
+                for (int i = 1; i < rank; i ++) {
+                    t = SpliceAfter (t, new TokenName (t, "idx"));
+                    t = SpliceAfter (t, new TokenKwAsnMul (t));
+                    t = SpliceAfter (t, new TokenKwThis (t));
+                    t = SpliceAfter (t, new TokenKwDot (t));
+                    t = SpliceAfter (t, new TokenName (t, "len" + i));
+                    t = SpliceAfter (t, new TokenKwSemi (t));
+                    t = SpliceAfter (t, new TokenName (t, "idx"));
+                    t = SpliceAfter (t, new TokenKwAsnAdd (t));
+                    t = SpliceAfter (t, new TokenName (t, "idx" + i));
+                    t = SpliceAfter (t, new TokenKwSemi (t));
+                }
+
+                t = SpliceAfter (t, new TokenKwRet (t));
+                if (suffix == 'O') {
+                    t = SpliceAfter (t, new TokenKwParOpen (t));
+                    t = SpliceAfter (t, ofType.CopyToken (t));
+                    t = SpliceAfter (t, new TokenKwParClose (t));
+                }
+                t = SpliceAfter (t, new TokenName (t, "xmrFixedArrayGet" + suffix));
+                t = SpliceAfter (t, new TokenKwParOpen (t));
+                t = SpliceAfter (t, new TokenKwThis (t));
+                t = SpliceAfter (t, new TokenKwDot (t));
+                t = SpliceAfter (t, new TokenName (t, "obj"));
+                t = SpliceAfter (t, new TokenKwComma (t));
+                t = SpliceAfter (t, new TokenName (t, "idx"));
+                t = SpliceAfter (t, new TokenKwParClose (t));
+                t = SpliceAfter (t, new TokenKwSemi (t));
+                t = SpliceAfter (t, new TokenKwBrcClose (t));
+
+                /*
+                 *        public void Set (integer idx0, integer idx1, ..., <oftype> val) {
+                 *            integer idx = idx0;
+                 *            idx *= this.len1; idx += idx1;
+                 *            idx *= this.len2; idx += idx2;
+                 *            ...
+                 *            xmrFixedArraySet<suffix> (this.obj, idx, val);
+                 *        }
+                 */
+                t = SpliceAfter (t, new TokenKwPublic (t));
+                t = SpliceAfter (t, new TokenTypeVoid (t));
+                t = SpliceAfter (t, new TokenName (t, "Set"));
+                t = SpliceAfter (t, new TokenKwParOpen (t));
+                for (int i = 0; i < rank; i ++) {
+                    t = SpliceAfter (t, new TokenTypeInt (t));
+                    t = SpliceAfter (t, new TokenName (t, "idx" + i));
+                    t = SpliceAfter (t, new TokenKwComma (t));
+                }
+                t = SpliceAfter (t, ofType.CopyToken (t));
+                t = SpliceAfter (t, new TokenName (t, "val"));
+                t = SpliceAfter (t, new TokenKwParClose (t));
+                t = SpliceAfter (t, new TokenKwBrcOpen (t));
+
+                t = SpliceAfter (t, new TokenTypeInt (t));
+                t = SpliceAfter (t, new TokenName (t, "idx"));
+                t = SpliceAfter (t, new TokenKwAssign (t));
+                t = SpliceAfter (t, new TokenName (t, "idx0"));
+                t = SpliceAfter (t, new TokenKwSemi (t));
+                for (int i = 1; i < rank; i ++) {
+                    t = SpliceAfter (t, new TokenName (t, "idx"));
+                    t = SpliceAfter (t, new TokenKwAsnMul (t));
+                    t = SpliceAfter (t, new TokenKwThis (t));
+                    t = SpliceAfter (t, new TokenKwDot (t));
+                    t = SpliceAfter (t, new TokenName (t, "len" + i));
+                    t = SpliceAfter (t, new TokenKwSemi (t));
+                    t = SpliceAfter (t, new TokenName (t, "idx"));
+                    t = SpliceAfter (t, new TokenKwAsnAdd (t));
+                    t = SpliceAfter (t, new TokenName (t, "idx" + i));
+                    t = SpliceAfter (t, new TokenKwSemi (t));
+                }
+
+                t = SpliceAfter (t, new TokenName (t, "xmrFixedArraySet" + suffix));
+                t = SpliceAfter (t, new TokenKwParOpen (t));
+                t = SpliceAfter (t, new TokenKwThis (t));
+                t = SpliceAfter (t, new TokenKwDot (t));
+                t = SpliceAfter (t, new TokenName (t, "obj"));
+                t = SpliceAfter (t, new TokenKwComma (t));
+                t = SpliceAfter (t, new TokenName (t, "idx"));
+                t = SpliceAfter (t, new TokenKwComma (t));
+                t = SpliceAfter (t, new TokenName (t, "val"));
+                t = SpliceAfter (t, new TokenKwParClose (t));
+                t = SpliceAfter (t, new TokenKwSemi (t));
+
+                t = SpliceAfter (t, new TokenKwBrcClose (t));
+                t = SpliceAfter (t, new TokenKwBrcClose (t));
+            }
+            return fa;
+        }
+        private Token SpliceAfter (Token before, Token after)
+        {
+            after.nextToken = before.nextToken;
+            after.prevToken = before;
+            before.nextToken = after;
+            after.nextToken.prevToken = after;
+            return after;
+        }
+
+        /**
+         * @brief Parse script-defined type declarations.
+         * @param token = points to possible script-defined type keyword
+         * @param outerSDType = null: top-level type
+         *                      else: sub-type of this type
+         * @param flags = access level (SDT_{PRIVATE,PROTECTED,PUBLIC})
+         * @returns true: something defined; else: not a sd type def
+         */
+        private bool ParseDeclSDTypes (ref Token token, TokenDeclSDType outerSDType, uint flags)
+        {
+            if (!(token is TokenDeclSDType)) return false;
+
+            TokenDeclSDType decl = (TokenDeclSDType)token;
+
+            /*
+             * If declaration of generic type, skip it.
+             * The instantiations get parsed (ie, when we know the concrete types)
+             * below because they appear as non-generic types.
+             */
+            if (decl.genParams != null) {
+                token = decl.endToken.nextToken;
+                return true;
+            }
+
+            /*
+             * Also skip over any typedefs.  They were all processed in 
+             * ParseSDTypePreScanPassTwo().
+             */
+            if (decl is TokenDeclSDTypeTypedef) {
+                token = decl.endToken.nextToken;
+                return true;
+            }
+
+            /*
+             * Non-generic types get parsed inline because we know all their types.
+             */
+            if (decl is TokenDeclSDTypeClass) {
+                ParseDeclClass (ref token, outerSDType, flags);
+                return true;
+            }
+            if (decl is TokenDeclSDTypeDelegate) {
+                ParseDeclDelegate (ref token, outerSDType, flags);
+                return true;
+            }
+            if (decl is TokenDeclSDTypeInterface) {
+                ParseDeclInterface (ref token, outerSDType, flags);
+                return true;
+            }
+
+            throw new Exception ("unhandled token " + token.GetType ().ToString ());
+        }
+
+        /**
+         * @brief Parse a class declaration.
+         * @param token = points to TokenDeclSDTypeClass token
+         *                points just past closing '}' on return
+         * @param outerSDType = null: this is a top-level class
+         *                      else: this class is being defined inside this type
+         * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
+         */
+        private void ParseDeclClass (ref Token token, TokenDeclSDType outerSDType, uint flags)
+        {
+            bool haveExplicitConstructor = false;
+            Token u = token;
+            TokenDeclSDTypeClass tokdeclcl;
+
+            tokdeclcl = (TokenDeclSDTypeClass)u;
+            tokdeclcl.outerSDType = outerSDType;
+            tokdeclcl.accessLevel = flags;
+            u = u.nextToken;
+
+            // maybe it is a partial class that had its body snipped out
+            // by a later partial class declaration of the same class
+            if (tokdeclcl.endToken == tokdeclcl) {
+                token = u;
+                return;
+            }
+
+            // make this class the currently compiled class
+            // used for retrieving stuff like 'this' possibly
+            // in field initialization code
+            TokenDeclSDType saveCurSDType = currentDeclSDType;
+            currentDeclSDType = tokdeclcl;
+
+            // next can be ':' followed by list of implemented
+            // interfaces and one extended class
+            if (u is TokenKwColon) {
+                u = u.nextToken;
+                while (true) {
+                    if (u is TokenTypeSDTypeClass) {
+                        TokenDeclSDTypeClass c = ((TokenTypeSDTypeClass)u).decl;
+                        if (tokdeclcl.extends == null) {
+                            tokdeclcl.extends = c;
+                        } else if (tokdeclcl.extends != c) {
+                            ErrorMsg (u, "can extend from only one class");
+                        }
+                    } else if (u is TokenTypeSDTypeInterface) {
+                        TokenDeclSDTypeInterface i = ((TokenTypeSDTypeInterface)u).decl;
+                        i.AddToClassDecl (tokdeclcl);
+                    } else {
+                        ErrorMsg (u, "expecting class or interface name");
+                        if (u is TokenKwBrcOpen) break;
+                    }
+                    u = u.nextToken;
+
+                    // allow : in case it is spliced from multiple partial class definitions
+                    if (!(u is TokenKwComma) && !(u is TokenKwColon)) break;
+                    u = u.nextToken;
+                }
+            }
+
+            // next must be '{' to open class declaration body
+            if (!(u is TokenKwBrcOpen)) {
+                ErrorMsg (u, "expecting { to open class declaration body");
+                token = SkipPastSemi (token);
+                goto ret;
+            }
+            token = u.nextToken;
+
+            // push a var frame to put all the class members in
+            tokdeclcl.members.thisClass = tokdeclcl;
+            tokenScript.PushVarFrame (tokdeclcl.members);
+
+            /*
+             * Create a function $instfieldnit to hold all explicit
+             * instance field initializations.
+             */
+            TokenDeclVar ifiFunc    = new TokenDeclVar (tokdeclcl, null, tokenScript);
+            ifiFunc.name            = new TokenName (ifiFunc, "$instfieldinit");
+            ifiFunc.retType         = new TokenTypeVoid (ifiFunc);
+            ifiFunc.argDecl         = new TokenArgDecl (ifiFunc);
+            ifiFunc.sdtClass        = tokdeclcl;
+            ifiFunc.sdtFlags        = SDT_PUBLIC | SDT_NEW;
+            TokenStmtBlock ifiBody  = new TokenStmtBlock (ifiFunc);
+            ifiBody.function        = ifiFunc;
+            ifiFunc.body            = ifiBody;
+            tokdeclcl.instFieldInit = ifiFunc;
+            tokenScript.AddVarEntry (ifiFunc);
+
+            /*
+             * Create a function $staticfieldnit to hold all explicit
+             * static field initializations.
+             */
+            TokenDeclVar sfiFunc   = new TokenDeclVar (tokdeclcl, null, tokenScript);
+            sfiFunc.name           = new TokenName (sfiFunc, "$staticfieldinit");
+            sfiFunc.retType        = new TokenTypeVoid (sfiFunc);
+            sfiFunc.argDecl        = new TokenArgDecl (sfiFunc);
+            sfiFunc.sdtClass       = tokdeclcl;
+            sfiFunc.sdtFlags       = SDT_PUBLIC | SDT_STATIC | SDT_NEW;
+            TokenStmtBlock sfiBody = new TokenStmtBlock (sfiFunc);
+            sfiBody.function       = sfiFunc;
+            sfiFunc.body           = sfiBody;
+            tokdeclcl.staticFieldInit = sfiFunc;
+            tokenScript.AddVarEntry (sfiFunc);
+
+            // process declaration statements until '}'
+            while (!(token is TokenKwBrcClose)) {
+                if (token is TokenKwSemi) {
+                    token = token.nextToken;
+                    continue;
+                }
+
+                /*
+                 * Check for all qualifiers.
+                 * typedef has an implied 'public' qualifier.
+                 */
+                flags = SDT_PUBLIC;
+                if (!(token is TokenDeclSDTypeTypedef)) {
+                    flags = ParseQualifierFlags (ref token);
+                }
+
+                /*
+                 * Parse nested script-defined type definitions.
+                 */
+                if (ParseDeclSDTypes (ref token, tokdeclcl, flags)) continue;
+
+                /*
+                 * constant <name> = <rval> ;
+                 */
+                if (token is TokenKwConst) {
+                    if ((flags & (SDT_ABSTRACT | SDT_NEW | SDT_OVERRIDE | SDT_VIRTUAL)) != 0) {
+                        ErrorMsg (token, "cannot have abstract, new, override or virtual field");
+                    }
+                    TokenDeclVar var = ParseDeclVar (ref token, null);
+                    if (var != null) {
+                        var.sdtClass = tokdeclcl;
+                        var.sdtFlags = flags | SDT_STATIC;
+                    }
+                    continue;
+                }
+
+                /*
+                 * <type> <name> ;
+                 * <type> <name> = <rval> ;
+                 */
+                if ((token is TokenType) &&
+                    (token.nextToken is TokenName) &&
+                    ((token.nextToken.nextToken is TokenKwSemi) || 
+                     (token.nextToken.nextToken is TokenKwAssign))) {
+                    if ((flags & (SDT_ABSTRACT | SDT_FINAL | SDT_NEW | SDT_OVERRIDE | SDT_VIRTUAL)) != 0) {
+                        ErrorMsg (token, "cannot have abstract, final, new, override or virtual field");
+                    }
+                    TokenDeclVar var = ParseDeclVar (ref token, ifiFunc);
+                    if (var != null) {
+                        var.sdtClass = tokdeclcl;
+                        var.sdtFlags = flags;
+                        if ((flags & SDT_STATIC) != 0) {
+                            // <type>.<name> = <init>;
+                            TokenLValSField left = new TokenLValSField (var.init);
+                            left.baseType        = tokdeclcl.MakeRefToken (var);
+                            left.fieldName       = var.name;
+                            DoVarInit (sfiFunc, left, var.init);
+                        } else if (var.init != null) {
+                            // this.<name> = <init>;
+                            TokenLValIField left = new TokenLValIField (var.init);
+                            left.baseRVal        = new TokenRValThis (var.init, tokdeclcl);
+                            left.fieldName       = var.name;
+                            DoVarInit (ifiFunc, left, var.init);
+                        }
+                    }
+                    continue;
+                }
+
+                /*
+                 * <type> <name> [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
+                 * <type> '[' ... ']' [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
+                 */
+                bool prop = (token is TokenType) &&
+                    (token.nextToken is TokenName) &&
+                    (token.nextToken.nextToken is TokenKwBrcOpen || 
+                     token.nextToken.nextToken is TokenKwColon);
+                prop |= (token is TokenType) && (token.nextToken is TokenKwBrkOpen);
+                if (prop) {
+                    TokenDeclVar var = ParseProperty (ref token, (flags & SDT_ABSTRACT) != 0, true);
+                    if (var != null) {
+                        var.sdtClass = tokdeclcl;
+                        var.sdtFlags = flags;
+                        if (var.getProp != null) {
+                            var.getProp.sdtClass = tokdeclcl;
+                            var.getProp.sdtFlags = flags;
+                        }
+                        if (var.setProp != null) {
+                            var.setProp.sdtClass = tokdeclcl;
+                            var.setProp.sdtFlags = flags;
+                        }
+                    }
+                    continue;
+                }
+
+                /*
+                 * 'constructor' '(' arglist ')' [ ':' [ 'base' ] '(' baseconstructorcall ')' ] '{' body '}'
+                 */
+                if (token is TokenKwConstructor) {
+                    ParseSDTClassCtorDecl (ref token, flags, tokdeclcl);
+                    haveExplicitConstructor = true;
+                    continue;
+                }
+
+                /*
+                 * <type> <name> <funcargs> <funcbody>
+                 * method with explicit return type
+                 */
+                if (token is TokenType) {
+                    ParseSDTClassMethodDecl (ref token, flags, tokdeclcl);
+                    continue;
+                }
+
+                /*
+                 * <name> <funcargs> <funcbody>
+                 * method returning void
+                 */
+                if ((token is TokenName) || ((token is TokenKw) && ((TokenKw)token).sdtClassOp)) {
+                    ParseSDTClassMethodDecl (ref token, flags, tokdeclcl);
+                    continue;
+                }
+
+                /*
+                 * That's all we support in a class declaration.
+                 */
+                ErrorMsg (token, "expecting field or method declaration");
+                token = SkipPastSemi (token);
+            }
+
+            /*
+             * If script didn't specify any constructor, create a default no-argument one.
+             */
+            if (!haveExplicitConstructor) {
+                TokenDeclVar tokenDeclFunc  = new TokenDeclVar (token, null, tokenScript);
+                tokenDeclFunc.name          = new TokenName (token, "$ctor");
+                tokenDeclFunc.retType       = new TokenTypeVoid (token);
+                tokenDeclFunc.argDecl       = new TokenArgDecl (token);
+                tokenDeclFunc.sdtClass      = tokdeclcl;
+                tokenDeclFunc.sdtFlags      = SDT_PUBLIC | SDT_NEW;
+                tokenDeclFunc.body          = new TokenStmtBlock (token);
+                tokenDeclFunc.body.function = tokenDeclFunc;
+
+                if (tokdeclcl.extends != null) {
+                    SetUpDefaultBaseCtorCall (tokenDeclFunc);
+                } else {
+                    // default constructor that doesn't do anything is trivial
+                    tokenDeclFunc.triviality = Triviality.trivial;
+                }
+
+                tokenScript.AddVarEntry (tokenDeclFunc);
+            }
+
+            /*
+             * Skip over the closing brace and pop corresponding var frame.
+             */
+            token = token.nextToken;
+            tokenScript.PopVarFrame ();
+        ret:
+            currentDeclSDType = saveCurSDType;
+        }
+
+        /**
+         * @brief Parse out abstract/override/private/protected/public/static/virtual keywords.
+         * @param token = first token to evaluate
+         * @returns flags found; token = unprocessed token
+         */
+        private Dictionary<uint, Token> foundFlags = new Dictionary<uint, Token> ();
+        private uint ParseQualifierFlags (ref Token token)
+        {
+            foundFlags.Clear ();
+            while (true) {
+                if (token is TokenKwPrivate) {
+                    token = AddQualifierFlag (token, SDT_PRIVATE, SDT_PROTECTED | SDT_PUBLIC);
+                    continue;
+                }
+                if (token is TokenKwProtected) {
+                    token = AddQualifierFlag (token, SDT_PROTECTED, SDT_PRIVATE | SDT_PUBLIC);
+                    continue;
+                }
+                if (token is TokenKwPublic) {
+                    token = AddQualifierFlag (token, SDT_PUBLIC, SDT_PRIVATE | SDT_PROTECTED);
+                    continue;
+                }
+
+                if (token is TokenKwAbstract) {
+                    token = AddQualifierFlag (token, SDT_ABSTRACT, SDT_FINAL | SDT_STATIC | SDT_VIRTUAL);
+                    continue;
+                }
+                if (token is TokenKwFinal) {
+                    token = AddQualifierFlag (token, SDT_FINAL, SDT_ABSTRACT | SDT_VIRTUAL);
+                    continue;
+                }
+                if (token is TokenKwNew) {
+                    token = AddQualifierFlag (token, SDT_NEW, SDT_OVERRIDE);
+                    continue;
+                }
+                if (token is TokenKwOverride) {
+                    token = AddQualifierFlag (token, SDT_OVERRIDE, SDT_NEW | SDT_STATIC);
+                    continue;
+                }
+                if (token is TokenKwStatic) {
+                    token = AddQualifierFlag (token, SDT_STATIC, SDT_ABSTRACT | SDT_OVERRIDE | SDT_VIRTUAL);
+                    continue;
+                }
+                if (token is TokenKwVirtual) {
+                    token = AddQualifierFlag (token, SDT_VIRTUAL, SDT_ABSTRACT | SDT_STATIC);
+                    continue;
+                }
+                break;
+            }
+
+            uint flags = 0;
+            foreach (uint flag in foundFlags.Keys) flags |= flag;
+            if ((flags & (SDT_PRIVATE | SDT_PROTECTED | SDT_PUBLIC)) == 0) {
+                ErrorMsg (token, "must specify exactly one of private, protected or public");
+            }
+            return flags;
+        }
+        private Token AddQualifierFlag (Token token, uint add, uint confs)
+        {
+            while (confs != 0) {
+                uint conf = (uint)(confs & -confs);
+                Token confToken;
+                if (foundFlags.TryGetValue (conf, out confToken)) {
+                    ErrorMsg (token, "conflicts with " + confToken.ToString ());
+                }
+                confs -= conf;
+            }
+            foundFlags[add] = token;
+            return token.nextToken;
+        }
+
+        /**
+         * @brief Parse a property declaration.
+         * @param token = points to the property type token on entry
+         *                points just past the closing brace on return
+         * @param abs = true: property is abstract
+         *             false: property is concrete
+         * @param imp = allow implemented interface specs
+         * @returns null: parse failure
+         *          else: property
+         *
+         * <type> <name> [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
+         * <type> '[' ... ']' [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
+         */
+        private TokenDeclVar ParseProperty (ref Token token, bool abs, bool imp)
+        {
+            /*
+             * Parse out the property's type and name.
+             *   <type> <name>
+             */
+            TokenType type = (TokenType)token;
+            TokenName name;
+            TokenArgDecl args;
+            Token argTokens = null;
+            token = token.nextToken;
+            if (token is TokenKwBrkOpen) {
+                argTokens = token;
+                name  = new TokenName (token, "$idxprop");
+                args  = ParseFuncArgs (ref token, typeof (TokenKwBrkClose));
+            } else {
+                name  = (TokenName)token;
+                token = token.nextToken;
+                args  = new TokenArgDecl (token);
+            }
+
+            /*
+             * Maybe it claims to implement some interface properties.
+             *   [ ':' <ifacetype>[.<propname>] ',' ... ]
+             */
+            TokenIntfImpl implements = null;
+            if (token is TokenKwColon) {
+                implements = ParseImplements (ref token, name);
+                if (implements == null) return null;
+                if (!imp) {
+                    ErrorMsg (token, "cannot implement interface property");
+                }
+            }
+
+            /*
+             * Should have an opening brace.
+             */
+            if (!(token is TokenKwBrcOpen)) {
+                ErrorMsg (token, "expect { to open property definition");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            token = token.nextToken;
+
+            /*
+             * Parse out the getter and/or setter.
+             *   'get' { <body> | ';' }
+             *   'set' { <body> | ';' }
+             */
+            TokenDeclVar getFunc = null;
+            TokenDeclVar setFunc = null;
+            while (!(token is TokenKwBrcClose)) {
+
+                /*
+                 * Maybe create a getter function.
+                 */
+                if (token is TokenKwGet) {
+                    getFunc = new TokenDeclVar (token, null, tokenScript);
+                    getFunc.name       = new TokenName (token, name.val + "$get");
+                    getFunc.retType    = type;
+                    getFunc.argDecl    = args;
+                    getFunc.implements = MakePropertyImplements (implements, "$get");
+
+                    token = token.nextToken;
+                    if (!ParseFunctionBody (ref token, getFunc, abs)) {
+                        getFunc = null;
+                    } else if (!tokenScript.AddVarEntry (getFunc)) {
+                        ErrorMsg (getFunc, "duplicate getter");
+                    }
+                    continue;
+                }
+
+                /*
+                 * Maybe create a setter function.
+                 */
+                if (token is TokenKwSet) {
+                    TokenArgDecl argDecl = args;
+                    if (getFunc != null) {
+                        argDecl = (argTokens == null) ? new TokenArgDecl (token) :
+                            ParseFuncArgs (ref argTokens, typeof (TokenKwBrkClose));
+                    }
+                    argDecl.AddArg (type, new TokenName (token, "value"));
+
+                    setFunc = new TokenDeclVar (token, null, tokenScript);
+                    setFunc.name       = new TokenName (token, name.val + "$set");
+                    setFunc.retType    = new TokenTypeVoid (token);
+                    setFunc.argDecl    = argDecl;
+                    setFunc.implements = MakePropertyImplements (implements, "$set");
+
+                    token = token.nextToken;
+                    if (!ParseFunctionBody (ref token, setFunc, abs)) {
+                        setFunc = null;
+                    } else if (!tokenScript.AddVarEntry (setFunc)) {
+                        ErrorMsg (setFunc, "duplicate setter");
+                    }
+                    continue;
+                }
+
+                ErrorMsg (token, "expecting get or set");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            token = token.nextToken;
+
+            if ((getFunc == null) && (setFunc == null)) {
+                ErrorMsg (name, "must specify at least one of get, set");
+                return null;
+            }
+
+            /*
+             * Set up a variable for the property.
+             */
+            TokenDeclVar tokenDeclVar = new TokenDeclVar (name, null, tokenScript);
+            tokenDeclVar.type    = type;
+            tokenDeclVar.name    = name;
+            tokenDeclVar.getProp = getFunc;
+            tokenDeclVar.setProp = setFunc;
+
+            /*
+             * Can't be same name already in block.
+             */
+            if (!tokenScript.AddVarEntry (tokenDeclVar)) {
+                ErrorMsg (tokenDeclVar, "duplicate member " + name.val);
+                return null;
+            }
+            return tokenDeclVar;
+        }
+
+        /**
+         * @brief Given a list of implemented interface methods, create a similar list with suffix added to all the names
+         * @param implements = original list of implemented interface methods
+         * @param suffix = string to be added to end of implemented interface method names
+         * @returns list similar to implements with suffix added to end of implemented interface method names
+         */
+        private TokenIntfImpl MakePropertyImplements (TokenIntfImpl implements, string suffix)
+        {
+            TokenIntfImpl gsimpls = null;
+            for (TokenIntfImpl impl = implements; impl != null; impl = (TokenIntfImpl)impl.nextToken) {
+                TokenIntfImpl gsimpl = new TokenIntfImpl (impl.intfType, 
+                                                          new TokenName (impl.methName, impl.methName.val + suffix));
+                gsimpl.nextToken = gsimpls;
+                gsimpls = gsimpl;
+            }
+            return gsimpls;
+        }
+
+        /**
+         * @brief Parse a constructor definition for a script-defined type class.
+         * @param token = points to 'constructor' keyword
+         * @param flags = abstract/override/static/virtual flags
+         * @param tokdeclcl = which script-defined type class this method is in
+         * @returns with method parsed and cataloged (or error message(s) printed)
+         */
+        private void ParseSDTClassCtorDecl (ref Token token, uint flags, TokenDeclSDTypeClass tokdeclcl)
+        {
+            if ((flags & (SDT_ABSTRACT | SDT_OVERRIDE | SDT_STATIC | SDT_VIRTUAL)) != 0) {
+                ErrorMsg (token, "cannot have abstract, override, static or virtual constructor");
+            }
+
+            TokenDeclVar tokenDeclFunc = new TokenDeclVar (token, null, tokenScript);
+            tokenDeclFunc.name         = new TokenName (tokenDeclFunc, "$ctor");
+            tokenDeclFunc.retType      = new TokenTypeVoid (token);
+            tokenDeclFunc.sdtClass     = tokdeclcl;
+            tokenDeclFunc.sdtFlags     = flags | SDT_NEW;
+
+            token = token.nextToken;
+            if (!(token is TokenKwParOpen)) {
+                ErrorMsg (token, "expecting ( for constructor argument list");
+                token = SkipPastSemi (token);
+                return;
+            }
+            tokenDeclFunc.argDecl      = ParseFuncArgs (ref token, typeof (TokenKwParClose));
+            if (tokenDeclFunc.argDecl == null) return;
+
+            TokenDeclVar saveDeclFunc = currentDeclFunc;
+            currentDeclFunc = tokenDeclFunc;
+            tokenScript.PushVarFrame (tokenDeclFunc.argDecl.varDict);
+            try {
+                /*
+                 * Set up reference to base constructor.
+                 */
+                TokenLValBaseField baseCtor = new TokenLValBaseField (token, 
+                                              new TokenName (token, "$ctor"), 
+                                              tokdeclcl);
+
+                /*
+                 * Parse any base constructor call as if it were the first statement of the
+                 * constructor itself.
+                 */
+                if (token is TokenKwColon) {
+                    token = token.nextToken;
+                    if (token is TokenKwBase) {
+                        token = token.nextToken;
+                    }
+                    if (!(token is TokenKwParOpen)) {
+                        ErrorMsg (token, "expecting ( for base constructor call arguments");
+                        token = SkipPastSemi (token);
+                        return;
+                    }
+                    TokenRValCall rvc = ParseRValCall (ref token, baseCtor);
+                    if (rvc == null) return;
+                    if (tokdeclcl.extends != null) {
+                        tokenDeclFunc.baseCtorCall = rvc;
+                        tokenDeclFunc.unknownTrivialityCalls.AddLast (rvc);
+                    } else {
+                        ErrorMsg (rvc, "base constructor call cannot be specified if not extending anything");
+                    }
+                } else if (tokdeclcl.extends != null) {
+
+                    /*
+                     * Caller didn't specify a constructor but we are extending, so we will 
+                     * call the extended class's default constructor.
+                     */
+                    SetUpDefaultBaseCtorCall (tokenDeclFunc);
+                }
+
+                /*
+                 * Parse the constructor body.
+                 */
+                tokenDeclFunc.body = ParseStmtBlock (ref token);
+                if (tokenDeclFunc.body == null) return;
+                if (tokenDeclFunc.argDecl == null) return;
+            } finally {
+                tokenScript.PopVarFrame ();
+                currentDeclFunc = saveDeclFunc;
+            }
+
+            /*
+             * Add to list of methods defined by this class.
+             * It has the name "$ctor(argsig)".
+             */
+            if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
+                ErrorMsg (tokenDeclFunc, "duplicate constructor definition");
+            }
+        }
+
+        /**
+         * @brief Set up a call from a constructor to its default base constructor.
+         */
+        private void SetUpDefaultBaseCtorCall (TokenDeclVar thisCtor)
+        {
+            TokenLValBaseField baseCtor = new TokenLValBaseField (thisCtor, 
+                                          new TokenName (thisCtor, "$ctor"), 
+                                          (TokenDeclSDTypeClass)thisCtor.sdtClass);
+            TokenRValCall rvc = new TokenRValCall (thisCtor);
+            rvc.meth          = baseCtor;
+            thisCtor.baseCtorCall = rvc;
+            thisCtor.unknownTrivialityCalls.AddLast (rvc);
+        }
+
+        /**
+         * @brief Parse a method definition for a script-defined type class.
+         * @param token = points to return type (or method name for implicit return type of void)
+         * @param flags = abstract/override/static/virtual flags
+         * @param tokdeclcl = which script-defined type class this method is in
+         * @returns with method parsed and cataloged (or error message(s) printed)
+         */
+        private void ParseSDTClassMethodDecl (ref Token token, uint flags, TokenDeclSDTypeClass tokdeclcl)
+        {
+            TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, 
+                                                        (flags & SDT_ABSTRACT) != 0, 
+                                                        (flags & SDT_STATIC) == 0, 
+                                                        (flags & SDT_STATIC) == 0);
+            if (tokenDeclFunc != null) {
+                tokenDeclFunc.sdtClass = tokdeclcl;
+                tokenDeclFunc.sdtFlags = flags;
+                if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
+                    string funcNameSig = tokenDeclFunc.funcNameSig.val;
+                    ErrorMsg (tokenDeclFunc.funcNameSig, "duplicate method name " + funcNameSig);
+                }
+            }
+        }
+
+        /**
+         * @brief Parse a delegate declaration statement.
+         * @param token = points to TokenDeclSDTypeDelegate token on entry
+         *                points just past ';' on return
+         * @param outerSDType = null: this is a top-level delegate
+         *                      else: this delegate is being defined inside this type
+         * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
+         */
+        private void ParseDeclDelegate (ref Token token, TokenDeclSDType outerSDType, uint flags)
+        {
+            Token u = token;
+            TokenDeclSDTypeDelegate tokdecldel;
+            TokenType retType;
+
+            tokdecldel = (TokenDeclSDTypeDelegate)u;
+            tokdecldel.outerSDType = outerSDType;
+            tokdecldel.accessLevel = flags;
+
+            // first thing following that should be return type
+            // but we will fill in 'void' if it is missing
+            u = u.nextToken;
+            if (u is TokenType) {
+                retType = (TokenType)u;
+                u = u.nextToken;
+            } else {
+                retType = new TokenTypeVoid (u);
+            }
+
+            // get list of argument types until we see a ')'
+            List<TokenType> args = new List<TokenType> ();
+            bool first = true;
+            do {
+                if (first) {
+
+                    // first time should have '(' ')' or '(' <type>
+                    if (!(u is TokenKwParOpen)) {
+                        ErrorMsg (u, "expecting ( after delegate name");
+                        token = SkipPastSemi (token);
+                        return;
+                    }
+                    first = false;
+                    u = u.nextToken;
+                    if (u is TokenKwParClose) break;
+                } else {
+
+                    // other times should have ',' <type>
+                    if (!(u is TokenKwComma)) {
+                        ErrorMsg (u, "expecting , separating arg types");
+                        token = SkipPastSemi (token);
+                        return;
+                    }
+                    u = u.nextToken;
+                }
+                if (!(u is TokenType)) {
+                    ErrorMsg (u, "expecting argument type");
+                    token = SkipPastSemi (token);
+                    return;
+                }
+                args.Add ((TokenType)u);
+                u = u.nextToken;
+
+                // they can put in a dummy name that we toss out
+                if (u is TokenName) u = u.nextToken;
+
+                // scanning ends on a ')'
+            } while (!(u is TokenKwParClose));
+
+            // fill in the return type and argment type array
+            tokdecldel.SetRetArgTypes (retType, args.ToArray ());
+
+            // and finally must have ';' to finish the delegate declaration statement
+            u = u.nextToken;
+            if (!(u is TokenKwSemi)) {
+                ErrorMsg (u, "expecting ; after ) in delegate");
+                token = SkipPastSemi (token);
+                return;
+            }
+            token = u.nextToken;
+        }
+
+        /**
+         * @brief Parse an interface declaration.
+         * @param token = points to TokenDeclSDTypeInterface token on entry
+         *                points just past closing '}' on return
+         * @param outerSDType = null: this is a top-level interface
+         *                      else: this interface is being defined inside this type
+         * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
+         */
+        private void ParseDeclInterface (ref Token token, TokenDeclSDType outerSDType, uint flags)
+        {
+            Token u = token;
+            TokenDeclSDTypeInterface tokdeclin;
+
+            tokdeclin = (TokenDeclSDTypeInterface)u;
+            tokdeclin.outerSDType = outerSDType;
+            tokdeclin.accessLevel = flags;
+            u = u.nextToken;
+
+            // next can be ':' followed by list of implemented interfaces
+            if (u is TokenKwColon) {
+                u = u.nextToken;
+                while (true) {
+                    if (u is TokenTypeSDTypeInterface) {
+                        TokenDeclSDTypeInterface i = ((TokenTypeSDTypeInterface)u).decl;
+                        if (!tokdeclin.implements.Contains (i)) {
+                            tokdeclin.implements.Add (i);
+                        }
+                    } else {
+                        ErrorMsg (u, "expecting interface name");
+                        if (u is TokenKwBrcOpen) break;
+                    }
+                    u = u.nextToken;
+                    if (!(u is TokenKwComma)) break;
+                    u = u.nextToken;
+                }
+            }
+
+            // next must be '{' to open interface declaration body
+            if (!(u is TokenKwBrcOpen)) {
+                ErrorMsg (u, "expecting { to open interface declaration body");
+                token = SkipPastSemi (token);
+                return;
+            }
+            token = u.nextToken;
+
+            // start a var definition frame to collect the interface members
+            tokenScript.PushVarFrame (false);
+            tokdeclin.methsNProps = tokenScript.variablesStack;
+
+            // process declaration statements until '}'
+            while (!(token is TokenKwBrcClose)) {
+                if (token is TokenKwSemi) {
+                    token = token.nextToken;
+                    continue;
+                }
+
+                /*
+                 * Parse nested script-defined type definitions.
+                 */
+                if (ParseDeclSDTypes (ref token, tokdeclin, SDT_PUBLIC)) continue;
+
+                /*
+                 * <type> <name> <funcargs> ;
+                 * abstract method with explicit return type
+                 */
+                if ((token is TokenType) && 
+                    (token.nextToken is TokenName) && 
+                    (token.nextToken.nextToken is TokenKwParOpen)) {
+                    Token name = token.nextToken;
+                    TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, true, false, false);
+                    if (tokenDeclFunc == null) continue;
+                    if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
+                        ErrorMsg (name, "duplicate method name");
+                        continue;
+                    }
+                    continue;
+                }
+
+                /*
+                 * <name> <funcargs> ;
+                 * abstract method returning void
+                 */
+                if ((token is TokenName) && 
+                    (token.nextToken is TokenKwParOpen)) {
+                    Token name = token;
+                    TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, true, false, false);
+                    if (tokenDeclFunc == null) continue;
+                    if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
+                        ErrorMsg (name, "duplicate method name");
+                    }
+                    continue;
+                }
+
+                /*
+                 * <type> <name> { [ get ; ] [ set ; ] }
+                 * <type> '[' ... ']' { [ get ; ] [ set ; ] }
+                 * abstract property
+                 */
+                bool prop = (token is TokenType) &&
+                    (token.nextToken is TokenName) &&
+                    (token.nextToken.nextToken is TokenKwBrcOpen || 
+                     token.nextToken.nextToken is TokenKwColon);
+                prop |= (token is TokenType) && (token.nextToken is TokenKwBrkOpen);
+                if (prop) {
+                    ParseProperty (ref token, true, false);
+                    continue;
+                }
+
+                /*
+                 * That's all we support in an interface declaration.
+                 */
+                ErrorMsg (token, "expecting method or property prototype");
+                token = SkipPastSemi (token);
+            }
+
+            /*
+             * Skip over the closing brace and pop the corresponding var frame.
+             */
+            token = token.nextToken;
+            tokenScript.PopVarFrame ();
+        }
+
+        /**
+         * @brief parse state body (including all its event handlers)
+         * @param token = points to TokenKwBrcOpen
+         * @returns null: state body parse error
+         *          else: token representing state
+         *          token = points past close brace
+         */
+        private TokenStateBody ParseStateBody (ref Token token)
+        {
+            TokenStateBody tokenStateBody = new TokenStateBody (token);
+
+            if (!(token is TokenKwBrcOpen)) {
+                ErrorMsg (token, "expecting { at beg of state");
+                token = SkipPastSemi (token);
+                return null;
+            }
+
+            token = token.nextToken;
+            while (!(token is TokenKwBrcClose)) {
+                if (token is TokenEnd) {
+                    ErrorMsg (tokenStateBody, "eof parsing state body");
+                    return null;
+                }
+                TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, false, false, false);
+                if (tokenDeclFunc == null) return null;
+                if (!(tokenDeclFunc.retType is TokenTypeVoid)) {
+                    ErrorMsg (tokenDeclFunc.retType, "event handlers don't have return types");
+                    return null;
+                }
+                tokenDeclFunc.nextToken = tokenStateBody.eventFuncs;
+                tokenStateBody.eventFuncs = tokenDeclFunc;
+            }
+            token = token.nextToken;
+            return tokenStateBody;
+        }
+
+        /**
+         * @brief Parse a function declaration, including its arg list and body
+         * @param token = points to function return type token (or function name token if return type void)
+         * @param abs = false: concrete function; true: abstract declaration
+         * @param imp = allow implemented interface specs
+         * @param ops = accept operators (==, +, etc) for function name
+         * @returns null: error parsing function definition
+         *          else: function declaration
+         *          token = advanced just past function, ie, just past the closing brace
+         */
+        private TokenDeclVar ParseDeclFunc (ref Token token, bool abs, bool imp, bool ops)
+        {
+            TokenType retType;
+            if (token is TokenType) {
+                retType = (TokenType)token;
+                token = token.nextToken;
+            } else {
+                retType = new TokenTypeVoid (token);
+            }
+
+            TokenName simpleName;
+            if ((token is TokenKw) && ((TokenKw)token).sdtClassOp) {
+                if (!ops) ErrorMsg (token, "operator functions disallowed in static contexts");
+                simpleName = new TokenName (token, "$op" + token.ToString ());
+            } else if (!(token is TokenName)) {
+                ErrorMsg (token, "expecting function name");
+                token = SkipPastSemi (token);
+                return null;
+            } else {
+                simpleName = (TokenName)token;
+            }
+            token = token.nextToken;
+
+            return ParseDeclFunc (ref token, abs, imp, retType, simpleName);
+        }
+
+        /**
+         * @brief Parse a function declaration, including its arg list and body
+         *        This version enters with token pointing to the '(' at beginning of arg list
+         * @param token = points to the '(' of the arg list
+         * @param abs = false: concrete function; true: abstract declaration
+         * @param imp = allow implemented interface specs
+         * @param retType = return type (TokenTypeVoid if void, never null)
+         * @param simpleName = function name without any signature
+         * @returns null: error parsing remainder of function definition
+         *          else: function declaration
+         *          token = advanced just past function, ie, just past the closing brace
+         */
+        private TokenDeclVar ParseDeclFunc (ref Token token, bool abs, bool imp, TokenType retType, TokenName simpleName)
+        {
+            TokenDeclVar tokenDeclFunc = new TokenDeclVar (simpleName, null, tokenScript);
+            tokenDeclFunc.name         = simpleName;
+            tokenDeclFunc.retType      = retType;
+            tokenDeclFunc.argDecl      = ParseFuncArgs (ref token, typeof (TokenKwParClose));
+            if (tokenDeclFunc.argDecl == null) return null;
+
+            if (token is TokenKwColon) {
+                tokenDeclFunc.implements = ParseImplements (ref token, simpleName);
+                if (tokenDeclFunc.implements == null) return null;
+                if (!imp) {
+                    ErrorMsg (tokenDeclFunc.implements, "cannot implement interface method");
+                    tokenDeclFunc.implements = null;
+                }
+            }
+
+            if (!ParseFunctionBody (ref token, tokenDeclFunc, abs)) return null;
+            if (tokenDeclFunc.argDecl == null) return null;
+            return tokenDeclFunc;
+        }
+
+        /**
+         * @brief Parse interface implementation list.
+         * @param token = points to ':' on entry
+         *                points just past list on return
+         * @param simpleName = simple name (no arg signature) of method/property that 
+         *                     is implementing the interface method/property
+         * @returns list of implemented interface methods/properties
+         */
+        private TokenIntfImpl ParseImplements (ref Token token, TokenName simpleName)
+        {
+            TokenIntfImpl implements = null;
+            do {
+                token = token.nextToken;
+                if (!(token is TokenTypeSDTypeInterface)) {
+                    ErrorMsg (token, "expecting interface type");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+                TokenTypeSDTypeInterface intfType = (TokenTypeSDTypeInterface)token;
+                token = token.nextToken;
+                TokenName methName = simpleName;
+                if ((token is TokenKwDot) && (token.nextToken is TokenName)) {
+                    methName = (TokenName)token.nextToken;
+                    token = token.nextToken.nextToken;
+                }
+                TokenIntfImpl intfImpl = new TokenIntfImpl (intfType, methName);
+                intfImpl.nextToken = implements;
+                implements = intfImpl;
+            } while (token is TokenKwComma);
+            return implements;
+        }
+
+
+        /**
+         * @brief Parse function declaration's body
+         * @param token = points to body, ie, ';' or '{'
+         * @param tokenDeclFunc = function being declared
+         * @param abs = false: concrete function; true: abstract declaration
+         * @returns whether or not the function definition parsed correctly
+         */
+        private bool ParseFunctionBody (ref Token token, TokenDeclVar tokenDeclFunc, bool abs)
+        {
+            if (token is TokenKwSemi) {
+                if (!abs) {
+                    ErrorMsg (token, "concrete function must have body");
+                    token = SkipPastSemi (token);
+                    return false;
+                }
+                token = token.nextToken;
+                return true;
+            }
+
+            /*
+             * Declare this function as being the one currently being processed
+             * for anything that cares.  We also start a variable frame that 
+             * includes all the declared parameters.
+             */
+            TokenDeclVar saveDeclFunc = currentDeclFunc;
+            currentDeclFunc = tokenDeclFunc;
+            tokenScript.PushVarFrame (tokenDeclFunc.argDecl.varDict);
+
+            /*
+             * Now parse the function statement block.
+             */
+            tokenDeclFunc.body = ParseStmtBlock (ref token);
+
+            /*
+             * Pop the var frame that contains the arguments.
+             */
+            tokenScript.PopVarFrame ();
+            currentDeclFunc = saveDeclFunc;
+
+            /*
+             * Check final errors.
+             */
+            if (tokenDeclFunc.body == null) return false;
+            if (abs) {
+                ErrorMsg (tokenDeclFunc.body, "abstract function must not have body");
+                tokenDeclFunc.body = null;
+                return false;
+            }
+            return true;
+        }
+
+
+        /**
+         * @brief Parse statement
+         * @param token = first token of statement
+         * @returns null: parse error
+         *          else: token representing whole statement
+         *          token = points past statement
+         */
+        private TokenStmt ParseStmt (ref Token token)
+        {
+            /*
+             * Statements that begin with a specific keyword.
+             */
+            if (token is TokenKwAt)      return ParseStmtLabel   (ref token);
+            if (token is TokenKwBrcOpen) return ParseStmtBlock   (ref token);
+            if (token is TokenKwBreak)   return ParseStmtBreak   (ref token);
+            if (token is TokenKwCont)    return ParseStmtCont    (ref token);
+            if (token is TokenKwDo)      return ParseStmtDo      (ref token);
+            if (token is TokenKwFor)     return ParseStmtFor     (ref token);
+            if (token is TokenKwForEach) return ParseStmtForEach (ref token);
+            if (token is TokenKwIf)      return ParseStmtIf      (ref token);
+            if (token is TokenKwJump)    return ParseStmtJump    (ref token);
+            if (token is TokenKwRet)     return ParseStmtRet     (ref token);
+            if (token is TokenKwSemi)    return ParseStmtNull    (ref token);
+            if (token is TokenKwState)   return ParseStmtState   (ref token);
+            if (token is TokenKwSwitch)  return ParseStmtSwitch  (ref token);
+            if (token is TokenKwThrow)   return ParseStmtThrow   (ref token);
+            if (token is TokenKwTry)     return ParseStmtTry     (ref token);
+            if (token is TokenKwWhile)   return ParseStmtWhile   (ref token);
+
+            /*
+             * Try to parse anything else as an expression, possibly calling
+             * something and/or writing to a variable.
+             */
+            TokenRVal tokenRVal = ParseRVal (ref token, semiOnly);
+            if (tokenRVal != null) {
+                TokenStmtRVal tokenStmtRVal = new TokenStmtRVal (tokenRVal);
+                tokenStmtRVal.rVal = tokenRVal;
+                return tokenStmtRVal;
+            }
+
+            /*
+             * Who knows what it is...
+             */
+            ErrorMsg (token, "unknown statement");
+            token = SkipPastSemi (token);
+            return null;
+        }
+
+        /**
+         * @brief parse a statement block, ie, group of statements between braces
+         * @param token = points to { token
+         * @returns null: error parsing
+         *          else: statements bundled in this token
+         *          token = advanced just past the } token
+         */
+        private TokenStmtBlock ParseStmtBlock (ref Token token)
+        {
+            if (!(token is TokenKwBrcOpen)) {
+                ErrorMsg (token, "statement block body must begin with a {");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            TokenStmtBlock tokenStmtBlock = new TokenStmtBlock (token);
+            tokenStmtBlock.function = currentDeclFunc;
+            tokenStmtBlock.outerStmtBlock = currentStmtBlock;
+            currentStmtBlock = tokenStmtBlock;
+            VarDict outerVariablesStack = tokenScript.variablesStack;
+            try {
+                Token prevStmt = null;
+                token = token.nextToken;
+                while (!(token is TokenKwBrcClose)) {
+                    if (token is TokenEnd) {
+                        ErrorMsg (tokenStmtBlock, "missing }");
+                        return null;
+                    }
+                    Token thisStmt;
+                    if (((token is TokenType) && (token.nextToken is TokenName)) || 
+                        (token is TokenKwConst)) {
+                        thisStmt = ParseDeclVar (ref token, null);
+                    } else {
+                        thisStmt = ParseStmt (ref token);
+                    }
+                    if (thisStmt == null) return null;
+                    if (prevStmt == null) tokenStmtBlock.statements = thisStmt;
+                                     else prevStmt.nextToken = thisStmt;
+                    prevStmt = thisStmt;
+                }
+                token = token.nextToken;
+            } finally {
+                tokenScript.variablesStack = outerVariablesStack;
+                currentStmtBlock = tokenStmtBlock.outerStmtBlock;
+            }
+            return tokenStmtBlock;
+        }
+
+        /**
+         * @brief parse a 'break' statement
+         * @param token = points to break keyword token
+         * @returns null: error parsing
+         *          else: statements bundled in this token
+         *          token = advanced just past the ; token
+         */
+        private TokenStmtBreak ParseStmtBreak (ref Token token)
+        {
+            TokenStmtBreak tokenStmtBreak = new TokenStmtBreak (token);
+            token = token.nextToken;
+            if (!(token is TokenKwSemi)) {
+                ErrorMsg (token, "expecting ;");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            token = token.nextToken;
+            return tokenStmtBreak;
+        }
+
+        /**
+         * @brief parse a 'continue' statement
+         * @param token = points to continue keyword token
+         * @returns null: error parsing
+         *          else: statements bundled in this token
+         *          token = advanced just past the ; token
+         */
+        private TokenStmtCont ParseStmtCont (ref Token token)
+        {
+            TokenStmtCont tokenStmtCont = new TokenStmtCont (token);
+            token = token.nextToken;
+            if (!(token is TokenKwSemi)) {
+                ErrorMsg (token, "expecting ;");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            token = token.nextToken;
+            return tokenStmtCont;
+        }
+
+        /**
+         * @brief parse a 'do' statement
+         * @params token = points to 'do' keyword token
+         * @returns null: parse error
+         *          else: pointer to token encapsulating the do statement, including body
+         *          token = advanced just past the body statement
+         */
+        private TokenStmtDo ParseStmtDo (ref Token token)
+        {
+            currentDeclFunc.triviality = Triviality.complex;
+            TokenStmtDo tokenStmtDo = new TokenStmtDo (token);
+            token = token.nextToken;
+            tokenStmtDo.bodyStmt = ParseStmt (ref token);
+            if (tokenStmtDo.bodyStmt == null) return null;
+            if (!(token is TokenKwWhile)) {
+                ErrorMsg (token, "expecting while clause");
+                return null;
+            }
+            token = token.nextToken;
+            tokenStmtDo.testRVal = ParseRValParen (ref token);
+            if (tokenStmtDo.testRVal == null) return null;
+            if (!(token is TokenKwSemi)) {
+                ErrorMsg (token, "while clause must terminate on semicolon");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            token = token.nextToken;
+            return tokenStmtDo;
+        }
+
+        /**
+         * @brief parse a for statement
+         * @param token = points to 'for' keyword token
+         * @returns null: parse error
+         *          else: pointer to encapsulated for statement token
+         *          token = advanced just past for body statement
+         */
+        private TokenStmt ParseStmtFor (ref Token token)
+        {
+            currentDeclFunc.triviality = Triviality.complex;
+
+            /*
+             * Create encapsulating token and skip past 'for ('
+             */
+            TokenStmtFor tokenStmtFor = new TokenStmtFor (token);
+            token = token.nextToken;
+            if (!(token is TokenKwParOpen)) {
+                ErrorMsg (token, "for must be followed by (");
+                return null;
+            }
+            token = token.nextToken;
+
+            /*
+             * If a plain for, ie, not declaring a variable, it's straightforward.
+             */
+            if (!(token is TokenType)) {
+                tokenStmtFor.initStmt = ParseStmt (ref token);
+                if (tokenStmtFor.initStmt == null) return null;
+                return ParseStmtFor2 (tokenStmtFor, ref token) ? tokenStmtFor : null;
+            }
+
+            /*
+             * Initialization declares a variable, so encapsulate it in a block so
+             * variable has scope only in the for statement, including its body.
+             */
+            TokenStmtBlock forStmtBlock = new TokenStmtBlock (tokenStmtFor);
+            forStmtBlock.outerStmtBlock = currentStmtBlock;
+            forStmtBlock.function       = currentDeclFunc;
+            currentStmtBlock            = forStmtBlock;
+            tokenScript.PushVarFrame (true);
+
+            TokenDeclVar tokenDeclVar   = ParseDeclVar (ref token, null);
+            if (tokenDeclVar == null) {
+                tokenScript.PopVarFrame ();
+                currentStmtBlock    = forStmtBlock.outerStmtBlock;
+                return null;
+            }
+
+            forStmtBlock.statements     = tokenDeclVar;
+            tokenDeclVar.nextToken      = tokenStmtFor;
+
+            bool ok                     = ParseStmtFor2 (tokenStmtFor, ref token);
+            tokenScript.PopVarFrame ();
+            currentStmtBlock            = forStmtBlock.outerStmtBlock;
+            return ok ? forStmtBlock : null;
+        }
+
+        /**
+         * @brief parse rest of 'for' statement starting with the test expression.
+         * @param tokenStmtFor = token encapsulating the for statement
+         * @param token = points to test expression
+         * @returns false: parse error
+         *           true: successful
+         *          token = points just past body statement
+         */
+        private bool ParseStmtFor2 (TokenStmtFor tokenStmtFor, ref Token token)
+        {
+            if (token is TokenKwSemi) {
+                token = token.nextToken;
+            } else {
+                tokenStmtFor.testRVal = ParseRVal (ref token, semiOnly);
+                if (tokenStmtFor.testRVal == null) return false;
+            }
+            if (token is TokenKwParClose) {
+                token = token.nextToken;
+            } else {
+                tokenStmtFor.incrRVal = ParseRVal (ref token, parCloseOnly);
+                if (tokenStmtFor.incrRVal == null) return false;
+            }
+            tokenStmtFor.bodyStmt = ParseStmt (ref token);
+            return tokenStmtFor.bodyStmt != null;
+        }
+
+        /**
+         * @brief parse a foreach statement
+         * @param token = points to 'foreach' keyword token
+         * @returns null: parse error
+         *          else: pointer to encapsulated foreach statement token
+         *          token = advanced just past for body statement
+         */
+        private TokenStmt ParseStmtForEach (ref Token token)
+        {
+            currentDeclFunc.triviality = Triviality.complex;
+
+            /*
+             * Create encapsulating token and skip past 'foreach ('
+             */
+            TokenStmtForEach tokenStmtForEach = new TokenStmtForEach (token);
+            token = token.nextToken;
+            if (!(token is TokenKwParOpen)) {
+                ErrorMsg (token, "foreach must be followed by (");
+                return null;
+            }
+            token = token.nextToken;
+
+            if (token is TokenName) {
+                tokenStmtForEach.keyLVal = new TokenLValName ((TokenName)token, tokenScript.variablesStack);
+                token = token.nextToken;
+            }
+            if (!(token is TokenKwComma)) {
+                ErrorMsg (token, "expecting comma");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            token = token.nextToken;
+            if (token is TokenName) {
+                tokenStmtForEach.valLVal = new TokenLValName ((TokenName)token, tokenScript.variablesStack);
+                token = token.nextToken;
+            }
+            if (!(token is TokenKwIn)) {
+                ErrorMsg (token, "expecting 'in'");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            token = token.nextToken;
+            tokenStmtForEach.arrayRVal = GetOperand (ref token);
+            if (tokenStmtForEach.arrayRVal == null) return null;
+            if (!(token is TokenKwParClose)) {
+                ErrorMsg (token, "expecting )");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            token = token.nextToken;
+            tokenStmtForEach.bodyStmt = ParseStmt (ref token);
+            if (tokenStmtForEach.bodyStmt == null) return null;
+            return tokenStmtForEach;
+        }
+
+        private TokenStmtIf ParseStmtIf (ref Token token)
+        {
+            TokenStmtIf tokenStmtIf = new TokenStmtIf (token);
+            token = token.nextToken;
+            tokenStmtIf.testRVal = ParseRValParen (ref token);
+            if (tokenStmtIf.testRVal == null) return null;
+            tokenStmtIf.trueStmt = ParseStmt (ref token);
+            if (tokenStmtIf.trueStmt == null) return null;
+            if (token is TokenKwElse) {
+                token = token.nextToken;
+                tokenStmtIf.elseStmt = ParseStmt (ref token);
+                if (tokenStmtIf.elseStmt == null) return null;
+            }
+            return tokenStmtIf;
+        }
+
+        private TokenStmtJump ParseStmtJump (ref Token token)
+        {
+            /*
+             * Create jump statement token to encapsulate the whole statement.
+             */
+            TokenStmtJump tokenStmtJump = new TokenStmtJump (token);
+            token = token.nextToken;
+            if (!(token is TokenName) || !(token.nextToken is TokenKwSemi)) {
+                ErrorMsg (token, "expecting label;");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            tokenStmtJump.label = (TokenName)token;
+            token = token.nextToken.nextToken;
+
+            /*
+             * If label is already defined, it means this is a backward (looping)
+             * jump, so remember the label has backward jump references.
+             * We also then assume the function is complex, ie, it has a loop.
+             */
+            if (currentDeclFunc.labels.ContainsKey (tokenStmtJump.label.val)) {
+                currentDeclFunc.labels[tokenStmtJump.label.val].hasBkwdRefs = true;
+                currentDeclFunc.triviality = Triviality.complex;
+            }
+
+            return tokenStmtJump;
+        }
+
+        /**
+         * @brief parse a jump target label statement
+         * @param token = points to the '@' token
+         * @returns null: error parsing
+         *          else: the label
+         *          token = advanced just past the ;
+         */
+        private TokenStmtLabel ParseStmtLabel (ref Token token)
+        {
+            if (!(token.nextToken is TokenName) ||
+                !(token.nextToken.nextToken is TokenKwSemi)) {
+                ErrorMsg (token, "invalid label");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            TokenStmtLabel stmtLabel = new TokenStmtLabel (token);
+            stmtLabel.name  = (TokenName)token.nextToken;
+            stmtLabel.block = currentStmtBlock;
+            if (currentDeclFunc.labels.ContainsKey (stmtLabel.name.val)) {
+                ErrorMsg (token.nextToken, "duplicate label");
+                ErrorMsg (currentDeclFunc.labels[stmtLabel.name.val], "previously defined here");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            currentDeclFunc.labels.Add (stmtLabel.name.val, stmtLabel);
+            token = token.nextToken.nextToken.nextToken;
+            return stmtLabel;
+        }
+
+        private TokenStmtNull ParseStmtNull (ref Token token)
+        {
+            TokenStmtNull tokenStmtNull = new TokenStmtNull (token);
+            token = token.nextToken;
+            return tokenStmtNull;
+        }
+
+        private TokenStmtRet ParseStmtRet (ref Token token)
+        {
+            TokenStmtRet tokenStmtRet = new TokenStmtRet (token);
+            token = token.nextToken;
+            if (token is TokenKwSemi) {
+                token = token.nextToken;
+            } else {
+                tokenStmtRet.rVal = ParseRVal (ref token, semiOnly);
+                if (tokenStmtRet.rVal == null) return null;
+            }
+            return tokenStmtRet;
+        }
+
+        private TokenStmtSwitch ParseStmtSwitch (ref Token token)
+        {
+            TokenStmtSwitch tokenStmtSwitch = new TokenStmtSwitch (token);
+            token = token.nextToken;
+            tokenStmtSwitch.testRVal = ParseRValParen (ref token);
+            if (tokenStmtSwitch.testRVal == null) return null;
+            if (!(token is TokenKwBrcOpen)) {
+                ErrorMsg (token, "expecting open brace");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            token = token.nextToken;
+            TokenSwitchCase tokenSwitchCase = null;
+            bool haveComplained = false;
+            while (!(token is TokenKwBrcClose)) {
+                if (token is TokenKwCase) {
+                    tokenSwitchCase = new TokenSwitchCase (token);
+                    if (tokenStmtSwitch.lastCase == null) {
+                        tokenStmtSwitch.cases = tokenSwitchCase;
+                    } else {
+                        tokenStmtSwitch.lastCase.nextCase = tokenSwitchCase;
+                    }
+                    tokenStmtSwitch.lastCase =  tokenSwitchCase;
+
+                    token = token.nextToken;
+                    tokenSwitchCase.rVal1 = ParseRVal (ref token, colonOrDotDotDot);
+                    if (tokenSwitchCase.rVal1 == null) return null;
+                    if (token is TokenKwDotDotDot) {
+                        token = token.nextToken;
+                        tokenSwitchCase.rVal2 = ParseRVal (ref token, colonOnly);
+                        if (tokenSwitchCase.rVal2 == null) return null;
+                    } else {
+                        if (!(token is TokenKwColon)) {
+                            ErrorMsg (token, "expecting : or ...");
+                            token = SkipPastSemi (token);
+                            return null;
+                        }
+                        token = token.nextToken;
+                    }
+                } else if (token is TokenKwDefault) {
+                    tokenSwitchCase = new TokenSwitchCase (token);
+                    if (tokenStmtSwitch.lastCase == null) {
+                        tokenStmtSwitch.cases = tokenSwitchCase;
+                    } else {
+                        tokenStmtSwitch.lastCase.nextCase = tokenSwitchCase;
+                    }
+                    tokenStmtSwitch.lastCase =  tokenSwitchCase;
+
+                    token = token.nextToken;
+                    if (!(token is TokenKwColon)) {
+                        ErrorMsg (token, "expecting :");
+                        token = SkipPastSemi (token);
+                        return null;
+                    }
+                    token = token.nextToken;
+                } else if (tokenSwitchCase != null) {
+                    TokenStmt bodyStmt = ParseStmt (ref token);
+                    if (bodyStmt == null) return null;
+                    if (tokenSwitchCase.lastStmt == null) {
+                        tokenSwitchCase.stmts = bodyStmt;
+                    } else {
+                        tokenSwitchCase.lastStmt.nextToken = bodyStmt;
+                    }
+                    tokenSwitchCase.lastStmt = bodyStmt;
+                    bodyStmt.nextToken = null;
+                } else if (!haveComplained) {
+                    ErrorMsg (token, "expecting case or default label");
+                    token = SkipPastSemi (token);
+                    haveComplained = true;
+                }
+            }
+            token = token.nextToken;
+            return tokenStmtSwitch;
+        }
+
+        private TokenStmtState ParseStmtState (ref Token token)
+        {
+            TokenStmtState tokenStmtState = new TokenStmtState (token);
+            token = token.nextToken;
+            if ((!(token is TokenName) && !(token is TokenKwDefault)) || !(token.nextToken is TokenKwSemi)) {
+                ErrorMsg (token, "expecting state;");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            if (token is TokenName) {
+                tokenStmtState.state = (TokenName)token;
+            }
+            token = token.nextToken.nextToken;
+            return tokenStmtState;
+        }
+
+        private TokenStmtThrow ParseStmtThrow (ref Token token)
+        {
+            TokenStmtThrow tokenStmtThrow = new TokenStmtThrow (token);
+            token = token.nextToken;
+            if (token is TokenKwSemi) {
+                token = token.nextToken;
+            } else {
+                tokenStmtThrow.rVal = ParseRVal (ref token, semiOnly);
+                if (tokenStmtThrow.rVal == null) return null;
+            }
+            return tokenStmtThrow;
+        }
+
+        /**
+         * @brief Parse a try { ... } catch { ... } finally { ... } statement block
+         * @param token = point to 'try' keyword on entry
+         *                points past last '}' processed on return
+         * @returns encapsulated try/catch/finally or null if parsing error
+         */
+        private TokenStmtTry ParseStmtTry (ref Token token)
+        {
+            /*
+             * Parse out the 'try { ... }' part
+             */
+            Token tryKw = token;
+            token = token.nextToken;
+            TokenStmt body = ParseStmtBlock (ref token);
+
+            while (true) {
+                TokenStmtTry tokenStmtTry;
+                if (token is TokenKwCatch) {
+                    if (!(token.nextToken is TokenKwParOpen) ||
+                        !(token.nextToken.nextToken is TokenType) ||
+                        !(token.nextToken.nextToken.nextToken is TokenName) ||
+                        !(token.nextToken.nextToken.nextToken.nextToken is TokenKwParClose)) {
+                        ErrorMsg (token, "catch must be followed by ( <type> <varname> ) { <statement>... }");
+                        return null;
+                    }
+                    token = token.nextToken.nextToken;     // skip over 'catch' '('
+                    TokenDeclVar tag = new TokenDeclVar (token.nextToken, currentDeclFunc, tokenScript);
+                    tag.type = (TokenType)token;
+                    token    = token.nextToken;            // skip over <type>
+                    tag.name = (TokenName)token;
+                    token    = token.nextToken.nextToken;  // skip over <varname> ')'
+
+                    if ((!(tag.type is TokenTypeExc)) && (!(tag.type is TokenTypeStr))) {
+                        ErrorMsg (tag.type, "must be type 'exception' or 'string'");
+                    }
+
+                    tokenStmtTry = new TokenStmtTry (tryKw);
+                    tokenStmtTry.tryStmt           = WrapTryCatFinInBlock (body);
+                    tokenStmtTry.catchVar          = tag;
+                    tokenScript.PushVarFrame (false);
+                    tokenScript.AddVarEntry (tag);
+                    tokenStmtTry.catchStmt         = ParseStmtBlock (ref token);
+                    tokenScript.PopVarFrame ();
+                    if (tokenStmtTry.catchStmt == null) return null;
+                    tokenStmtTry.tryStmt.isTry     = true;
+                    tokenStmtTry.tryStmt.tryStmt   = tokenStmtTry;
+                    tokenStmtTry.catchStmt.isCatch = true;
+                    tokenStmtTry.catchStmt.tryStmt = tokenStmtTry;
+                }
+                else if (token is TokenKwFinally) {
+                    token = token.nextToken;
+
+                    tokenStmtTry = new TokenStmtTry (tryKw);
+                    tokenStmtTry.tryStmt               = WrapTryCatFinInBlock (body);
+                    tokenStmtTry.finallyStmt           = ParseStmtBlock (ref token);
+                    if (tokenStmtTry.finallyStmt == null) return null;
+                    tokenStmtTry.tryStmt.isTry         = true;
+                    tokenStmtTry.tryStmt.tryStmt       = tokenStmtTry;
+                    tokenStmtTry.finallyStmt.isFinally = true;
+                    tokenStmtTry.finallyStmt.tryStmt   = tokenStmtTry;
+                }
+                else break;
+
+                body = tokenStmtTry;
+            }
+
+            if (!(body is TokenStmtTry)) {
+                ErrorMsg (body, "try must have a matching catch and/or finally");
+                return null;
+            }
+            return (TokenStmtTry)body;
+        }
+
+        /**
+         * @brief Wrap a possible try/catch/finally statement block in a block statement.
+         *
+         * Given body = try { } catch (string s) { }
+         *
+         * we return { try { } catch (string s) { } }
+         *
+         * @param body = a TokenStmtTry or a TokenStmtBlock
+         * @returns a TokenStmtBlock
+         */
+        private TokenStmtBlock WrapTryCatFinInBlock (TokenStmt body)
+        {
+            if (body is TokenStmtBlock) return (TokenStmtBlock)body;
+
+            TokenStmtTry innerTry = (TokenStmtTry)body;
+
+            TokenStmtBlock wrapper = new TokenStmtBlock (body);
+            wrapper.statements     = innerTry;
+            wrapper.outerStmtBlock = currentStmtBlock;
+            wrapper.function       = currentDeclFunc;
+
+            innerTry.tryStmt.outerStmtBlock = wrapper;
+            if (innerTry.catchStmt   != null) innerTry.catchStmt.outerStmtBlock   = wrapper;
+            if (innerTry.finallyStmt != null) innerTry.finallyStmt.outerStmtBlock = wrapper;
+
+            return wrapper;
+        }
+
+        private TokenStmtWhile ParseStmtWhile (ref Token token)
+        {
+            currentDeclFunc.triviality = Triviality.complex;
+            TokenStmtWhile tokenStmtWhile = new TokenStmtWhile (token);
+            token = token.nextToken;
+            tokenStmtWhile.testRVal = ParseRValParen (ref token);
+            if (tokenStmtWhile.testRVal == null) return null;
+            tokenStmtWhile.bodyStmt = ParseStmt (ref token);
+            if (tokenStmtWhile.bodyStmt == null) return null;
+            return tokenStmtWhile;
+        }
+
+        /**
+         * @brief parse a variable declaration statement, including init value if any.
+         * @param token = points to type or 'constant' token
+         * @param initFunc = null: parsing a local var declaration
+         *                         put initialization code in .init
+         *                   else: parsing a global var or field var declaration
+         *                         put initialization code in initFunc.body
+         * @returns null: parsing error
+         *          else: variable declaration encapulating token
+         *          token = advanced just past semi-colon
+         *          variables = modified to include the new variable
+         */
+        private TokenDeclVar ParseDeclVar (ref Token token, TokenDeclVar initFunc)
+        {
+            TokenDeclVar tokenDeclVar = new TokenDeclVar (token.nextToken, currentDeclFunc, tokenScript);
+
+            /*
+             * Handle constant declaration.
+             * It ends up in the declared variables list for the statement block just like
+             * any other variable, except it has .constant = true.
+             * The code generator will test that the initialization expression is constant.
+             *
+             *  constant <name> = <value> ;
+             */
+            if (token is TokenKwConst) {
+                token = token.nextToken;
+                if (!(token is TokenName)) {
+                    ErrorMsg (token, "expecting constant name");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+                tokenDeclVar.name = (TokenName)token;
+                token = token.nextToken;
+                if (!(token is TokenKwAssign)) {
+                    ErrorMsg (token, "expecting =");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+                token = token.nextToken;
+                TokenRVal rVal = ParseRVal (ref token, semiOnly);
+                if (rVal == null) return null;
+                tokenDeclVar.init = rVal;
+                tokenDeclVar.constant = true;
+            }
+
+            /*
+             * Otherwise, normal variable declaration with optional initialization value.
+             */
+            else {
+                /*
+                 * Build basic encapsulating token with type and name.
+                 */
+                tokenDeclVar.type = (TokenType)token;
+                token = token.nextToken;
+                if (!(token is TokenName)) {
+                    ErrorMsg (token, "expecting variable name");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+                tokenDeclVar.name = (TokenName)token;
+                token = token.nextToken;
+
+                /*
+                 * If just a ;, there is no explicit initialization value.
+                 * Otherwise, look for an =RVal; expression that has init value.
+                 */
+                if (token is TokenKwSemi) {
+                    token = token.nextToken;
+                    if (initFunc != null) {
+                        tokenDeclVar.init = TokenRValInitDef.Construct (tokenDeclVar);
+                    }
+                } else if (token is TokenKwAssign) {
+                    token = token.nextToken;
+                    if (initFunc != null) {
+                        currentDeclFunc = initFunc;
+                        tokenDeclVar.init = ParseRVal (ref token, semiOnly);
+                        currentDeclFunc = null;
+                    } else {
+                        tokenDeclVar.init = ParseRVal (ref token, semiOnly);
+                    }
+                    if (tokenDeclVar.init == null) return null;
+                } else {
+                    ErrorMsg (token, "expecting = or ;");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+            }
+
+            /*
+             * If doing local vars, each var goes in its own var frame,
+             * to make sure no code before this point can reference it.
+             */
+            if (currentStmtBlock != null) {
+                tokenScript.PushVarFrame (true);
+            }
+
+            /*
+             * Can't be same name already in block.
+             */
+            if (!tokenScript.AddVarEntry (tokenDeclVar)) {
+                ErrorMsg (tokenDeclVar, "duplicate variable " + tokenDeclVar.name.val);
+                return null;
+            }
+            return tokenDeclVar;
+        }
+
+        /**
+         * @brief Add variable initialization to $globalvarinit, $staticfieldinit or $instfieldinit function.
+         * @param initFunc = $globalvarinit, $staticfieldinit or $instfieldinit function
+         * @param left = variable being initialized
+         * @param init = null: initialize to default value
+         *               else: initialize to this value
+         */
+        private void DoVarInit (TokenDeclVar initFunc, TokenLVal left, TokenRVal init)
+        {
+            /*
+             * Make a statement that assigns the initialization value to the variable.
+             */
+            TokenStmt stmt;
+            if (init == null) {
+                TokenStmtVarIniDef tsvid = new TokenStmtVarIniDef (left);
+                tsvid.var = left;
+                stmt = tsvid;
+            } else {
+                TokenKw op = new TokenKwAssign (left);
+                TokenStmtRVal tsrv = new TokenStmtRVal (init);
+                tsrv.rVal = new TokenRValOpBin (left, op, init);
+                stmt = tsrv;
+            }
+
+            /*
+             * Add statement to end of initialization function.
+             * Be sure to execute them in same order as in source
+             * as some doofus scripts depend on it.
+             */
+            Token lastStmt = initFunc.body.statements;
+            if (lastStmt == null) {
+                initFunc.body.statements = stmt;
+            } else {
+                Token nextStmt;
+                while ((nextStmt = lastStmt.nextToken) != null) {
+                    lastStmt = nextStmt;
+                }
+                lastStmt.nextToken = stmt;
+            }
+        }
+
+        /**
+         * @brief parse function declaration argument list
+         * @param token = points to TokenKwParOpen
+         * @returns null: parse error
+         *          else: points to token with types and names
+         *          token = updated past the TokenKw{Brk,Par}Close
+         */
+        private TokenArgDecl ParseFuncArgs (ref Token token, Type end)
+        {
+            TokenArgDecl tokenArgDecl = new TokenArgDecl (token);
+
+            bool first = true;
+            do {
+                token = token.nextToken;
+                if ((token.GetType () == end) && first) break;
+                if (!(token is TokenType)) {
+                    ErrorMsg (token, "expecting arg type");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+                TokenType type = (TokenType)token;
+                token = token.nextToken;
+                if (!(token is TokenName)) {
+                    ErrorMsg (token, "expecting arg name");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+                TokenName name = (TokenName)token;
+                token = token.nextToken;
+
+                if (!tokenArgDecl.AddArg (type, name)) {
+                    ErrorMsg (name, "duplicate arg name");
+                }
+                first = false;
+            } while (token is TokenKwComma);
+
+            if (token.GetType () != end) {
+                ErrorMsg (token, "expecting comma or close bracket/paren");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            token = token.nextToken;
+
+            return tokenArgDecl;
+        }
+
+        /**
+         * @brief parse right-hand value expression
+         *        this is where arithmetic-like expressions are processed
+         * @param token = points to first token expression
+         * @param termTokenType = expression termination token type
+         * @returns null: not an RVal
+         *          else: single token representing whole expression
+         *          token = if termTokenType.Length == 1, points just past terminating token
+         *                                          else, points right at terminating token
+         */
+        public TokenRVal ParseRVal (ref Token token, Type[] termTokenTypes)
+        {
+            /*
+             * Start with pushing the first operand on operand stack.
+             */
+            BinOp binOps = null;
+            TokenRVal operands = GetOperand (ref token);
+            if (operands == null) return null;
+
+            /*
+             * Keep scanning until we hit the termination token.
+             */
+            while (true) {
+                Type tokType = token.GetType ();
+                for (int i = termTokenTypes.Length; -- i >= 0;) {
+                    if (tokType == termTokenTypes[i]) goto done;
+                }
+
+                /*
+                 * Special form:
+                 *   <operand> is <typeexp>
+                 */
+                if (token is TokenKwIs) {
+                    TokenRValIsType tokenRValIsType = new TokenRValIsType (token);
+                    token = token.nextToken;
+
+                    /*
+                     * Parse the <typeexp>.
+                     */
+                    tokenRValIsType.typeExp = ParseTypeExp (ref token);
+                    if (tokenRValIsType.typeExp == null) return null;
+
+                    /*
+                     * Replace top operand with result of <operand> is <typeexp>
+                     */
+                    tokenRValIsType.rValExp   = operands;
+                    tokenRValIsType.nextToken = operands.nextToken;
+                    operands = tokenRValIsType;
+
+                    /*
+                     * token points just past <typeexp> so see if it is another operator.
+                     */
+                    continue;
+                }
+
+                /*
+                 * Peek at next operator.
+                 */
+                BinOp binOp = GetOperator (ref token);
+                if (binOp == null) return null;
+
+                /*
+                 * If there are stacked operators of higher or same precedence than new one,
+                 * perform their computation then push result back on operand stack.
+                 *
+                 *  higher or same = left-to-right application of operators
+                 *                   eg, a - b - c becomes (a - b) - c
+                 *
+                 *  higher precedence = right-to-left application of operators
+                 *                      eg, a - b - c becomes a - (b - c)
+                 *
+                 * Now of course, there is some ugliness necessary:
+                 *      we want:  a  - b - c  =>  (a - b) - c    so we do 'higher or same'
+                 *  but we want:  a += b = c  =>  a += (b = c)   so we do 'higher only'
+                 *
+                 * binOps is the first operator (or null if only one)
+                 * binOp is the second operator (or first if only one)
+                 */
+                while (binOps != null) {
+                    if (binOps.preced < binOp.preced) break;       // 1st operator lower than 2nd, so leave 1st on stack to do later
+                    if (binOps.preced > binOp.preced) goto do1st;  // 1st op higher than 2nd, so we always do 1st op first
+                    if (binOps.preced == ASNPR) break;             // equal preced, if assignment type, leave 1st on stack to do later
+                                                                   //               if non-asn type, do 1st op first (ie left-to-right)
+                do1st:
+                    TokenRVal result = PerformBinOp ((TokenRVal)operands.prevToken, binOps, (TokenRVal)operands);
+                    result.prevToken = operands.prevToken.prevToken;
+                    operands = result;
+                    binOps   = binOps.pop;
+                }
+
+                /*
+                 * Handle conditional expression as a special form:
+                 *    <condexp> ? <trueexp> : <falseexp>
+                 */
+                if (binOp.token is TokenKwQMark) {
+                    TokenRValCondExpr condExpr = new TokenRValCondExpr (binOp.token);
+                    condExpr.condExpr  = operands;
+                    condExpr.trueExpr  = ParseRVal (ref token, new Type[] { typeof (TokenKwColon) });
+                    condExpr.falseExpr = ParseRVal (ref token, termTokenTypes);
+                    condExpr.prevToken = operands.prevToken;
+                    operands = condExpr;
+                    termTokenTypes = new Type[0];
+                    goto done;
+                }
+
+                /*
+                 * Push new operator on its stack.
+                 */
+                binOp.pop = binOps;
+                binOps = binOp;
+
+                /*
+                 * Push next operand on its stack.
+                 */
+                TokenRVal operand = GetOperand (ref token);
+                if (operand == null) return null;
+                operand.prevToken = operands;
+                operands = operand;
+            }
+        done:
+
+            /*
+             * At end of expression, perform any stacked computations.
+             */
+            while (binOps != null) {
+                TokenRVal result = PerformBinOp ((TokenRVal)operands.prevToken, binOps, (TokenRVal)operands);
+                result.prevToken = operands.prevToken.prevToken;
+                operands = result;
+                binOps   = binOps.pop;
+            }
+
+            /*
+             * There should be exactly one remaining operand on the stack which is our final result.
+             */
+            if (operands.prevToken != null) throw new Exception ("too many operands");
+
+            /*
+             * If only one terminator type possible, advance past the terminator.
+             */
+            if (termTokenTypes.Length == 1) token = token.nextToken;
+
+            return operands;
+        }
+
+        private TokenTypeExp ParseTypeExp (ref Token token)
+        {
+            TokenTypeExp leftOperand = GetTypeExp (ref token);
+            if (leftOperand == null) return null;
+
+            while ((token is TokenKwAnd) || (token is TokenKwOr)) {
+                Token typeBinOp = token;
+                token = token.nextToken;
+                TokenTypeExp rightOperand = GetTypeExp (ref token);
+                if (rightOperand == null) return null;
+                TokenTypeExpBinOp typeExpBinOp = new TokenTypeExpBinOp (typeBinOp);
+                typeExpBinOp.leftOp  = leftOperand;
+                typeExpBinOp.binOp   = typeBinOp;
+                typeExpBinOp.rightOp = rightOperand;
+                leftOperand = typeExpBinOp;
+            }
+            return leftOperand;
+        }
+
+        private TokenTypeExp GetTypeExp (ref Token token)
+        {
+            if (token is TokenKwTilde) {
+                TokenTypeExpNot typeExpNot = new TokenTypeExpNot (token);
+                token = token.nextToken;
+                typeExpNot.typeExp = GetTypeExp (ref token);
+                if (typeExpNot.typeExp == null) return null;
+                return typeExpNot;
+            }
+            if (token is TokenKwParOpen) {
+                TokenTypeExpPar typeExpPar = new TokenTypeExpPar (token);
+                token = token.nextToken;
+                typeExpPar.typeExp = GetTypeExp (ref token);
+                if (typeExpPar.typeExp == null) return null;
+                if (!(token is TokenKwParClose)) {
+                    ErrorMsg (token, "expected close parenthesis");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+                return typeExpPar;
+            }
+            if (token is TokenKwUndef) {
+                TokenTypeExpUndef typeExpUndef = new TokenTypeExpUndef (token);
+                token = token.nextToken;
+                return typeExpUndef;
+            }
+            if (token is TokenType) {
+                TokenTypeExpType typeExpType = new TokenTypeExpType (token);
+                typeExpType.typeToken = (TokenType)token;
+                token = token.nextToken;
+                return typeExpType;
+            }
+            ErrorMsg (token, "expected type");
+            token = SkipPastSemi (token);
+            return null;
+        }
+
+        /**
+         * @brief get a right-hand operand expression token
+         * @param token = first token of operand to parse
+         * @returns null: invalid operand
+         *          else: token that bundles or wraps the operand
+         *          token = points to token following last operand token
+         */
+        private TokenRVal GetOperand (ref Token token)
+        {
+            /*
+             * Prefix unary operators (eg ++, --) requiring an L-value.
+             */
+            if ((token is TokenKwIncr) || (token is TokenKwDecr)) {
+                TokenRValAsnPre asnPre = new TokenRValAsnPre (token);
+                asnPre.prefix = token;
+                token = token.nextToken;
+                TokenRVal op = GetOperand (ref token);
+                if (op == null) return null;
+                if (!(op is TokenLVal)) {
+                    ErrorMsg (op, "can pre{in,de}crement only an L-value");
+                    return null;
+                }
+                asnPre.lVal = (TokenLVal)op;
+                return asnPre;
+            }
+
+            /*
+             * Get the bulk of the operand, ie, without any of the below suffixes.
+             */
+            TokenRVal operand = GetOperandNoMods (ref token);
+            if (operand == null) return null;
+        modifiers:
+
+            /*
+             * If followed by '++' or '--', it is post-{in,de}cremented.
+             */
+            if ((token is TokenKwIncr) || (token is TokenKwDecr)) {
+                TokenRValAsnPost asnPost = new TokenRValAsnPost (token);
+                asnPost.postfix = token;
+                token = token.nextToken;
+                if (!(operand is TokenLVal)) {
+                    ErrorMsg (operand, "can post{in,de}crement only an L-value");
+                    return null;
+                }
+                asnPost.lVal = (TokenLVal)operand;
+                return asnPost;
+            }
+
+            /*
+             * If followed by a '.', it is an instance field or instance method reference.
+             */
+            if (token is TokenKwDot) {
+                token = token.nextToken;
+                if (!(token is TokenName)) {
+                    ErrorMsg (token, ". must be followed by field/method name");
+                    return null;
+                }
+                TokenLValIField field = new TokenLValIField (token);
+                field.baseRVal = operand;
+                field.fieldName = (TokenName)token;
+                operand = field;
+                token = token.nextToken;
+                goto modifiers;
+            }
+
+            /*
+             * If followed by a '[', it is an array subscript.
+             */
+            if (token is TokenKwBrkOpen) {
+                TokenLValArEle tokenLValArEle = new TokenLValArEle (token);
+                token = token.nextToken;
+
+                /*
+                 * Parse subscript(s) expression.
+                 */
+                tokenLValArEle.subRVal = ParseRVal (ref token, brkCloseOnly);
+                if (tokenLValArEle.subRVal == null) {
+                    ErrorMsg (tokenLValArEle, "invalid subscript");
+                    return null;
+                }
+
+                /*
+                 * See if comma-separated list of values.
+                 */
+                TokenRVal subscriptRVals;
+                int numSubscripts = SplitCommaRVals (tokenLValArEle.subRVal, out subscriptRVals);
+                if (numSubscripts > 1) {
+
+                    /*
+                     * If so, put the values in an LSL_List object.
+                     */
+                    TokenRValList rValList = new TokenRValList (tokenLValArEle);
+                    rValList.rVal = subscriptRVals;
+                    rValList.nItems = numSubscripts;
+                    tokenLValArEle.subRVal = rValList;
+                }
+
+                /*
+                 * Either way, save array variable name
+                 * and substitute whole reference for L-value
+                 */
+                tokenLValArEle.baseRVal = operand;
+                operand = tokenLValArEle;
+                goto modifiers;
+            }
+
+            /*
+             * If followed by a '(', it is a function/method call.
+             */
+            if (token is TokenKwParOpen) {
+                operand = ParseRValCall (ref token, operand);
+                goto modifiers;
+            }
+
+            /*
+             * If 'new' arraytipe '{', it is an array initializer.
+             */
+            if ((token is TokenKwBrcOpen) && (operand is TokenLValSField) && 
+                (((TokenLValSField)operand).fieldName.val == "$new") && 
+                ((TokenLValSField)operand).baseType.ToString ().EndsWith ("]")) {
+                operand = ParseRValNewArIni (ref token, (TokenLValSField)operand);
+                if (operand != null) goto modifiers;
+            }
+
+            return operand;
+        }
+
+        /**
+         * @brief same as GetOperand() except doesn't check for any modifiers
+         */
+        private TokenRVal GetOperandNoMods (ref Token token)
+        {
+            /*
+             * Simple unary operators.
+             */
+            if ((token is TokenKwSub) || 
+                (token is TokenKwTilde) ||
+                (token is TokenKwExclam)) {
+                Token uop = token;
+                token = token.nextToken;
+                TokenRVal rVal = GetOperand (ref token);
+                if (rVal == null) return null;
+                return PerformUnOp (uop, rVal);
+            }
+
+            /*
+             * Type casting.
+             */
+            if ((token is TokenKwParOpen) &&
+                (token.nextToken is TokenType) &&
+                (token.nextToken.nextToken is TokenKwParClose)) {
+                TokenType type = (TokenType)token.nextToken;
+                token = token.nextToken.nextToken.nextToken;
+                TokenRVal rVal = GetOperand (ref token);
+                if (rVal == null) return null;
+                return new TokenRValCast (type, rVal);
+            }
+
+            /*
+             * Parenthesized expression.
+             */
+            if (token is TokenKwParOpen) {
+                return ParseRValParen (ref token);
+            }
+
+            /*
+             * Constants.
+             */
+            if (token is TokenChar) {
+                TokenRValConst rValConst = new TokenRValConst (token, ((TokenChar)token).val);
+                token = token.nextToken;
+                return rValConst;
+            }
+            if (token is TokenFloat) {
+                TokenRValConst rValConst = new TokenRValConst (token, ((TokenFloat)token).val);
+                token = token.nextToken;
+                return rValConst;
+            }
+            if (token is TokenInt) {
+                TokenRValConst rValConst = new TokenRValConst (token, ((TokenInt)token).val);
+                token = token.nextToken;
+                return rValConst;
+            }
+            if (token is TokenStr) {
+                TokenRValConst rValConst = new TokenRValConst (token, ((TokenStr)token).val);
+                token = token.nextToken;
+                return rValConst;
+            }
+            if (token is TokenKwUndef) {
+                TokenRValUndef rValUndef = new TokenRValUndef ((TokenKwUndef)token);
+                token = token.nextToken;
+                return rValUndef;
+            }
+
+            /*
+             * '<'value,...'>', ie, rotation or vector
+             */
+            if (token is TokenKwCmpLT) {
+                Token openBkt = token;
+                token = token.nextToken;
+                TokenRVal rValAll = ParseRVal (ref token, cmpGTOnly);
+                if (rValAll == null) return null;
+                TokenRVal rVals;
+                int nVals = SplitCommaRVals (rValAll, out rVals);
+                switch (nVals) {
+                    case 3: {
+                        TokenRValVec rValVec = new TokenRValVec (openBkt);
+                        rValVec.xRVal = rVals;
+                        rValVec.yRVal = (TokenRVal)rVals.nextToken;
+                        rValVec.zRVal = (TokenRVal)rVals.nextToken.nextToken;
+                        return rValVec;
+                    }
+                    case 4: {
+                        TokenRValRot rValRot = new TokenRValRot (openBkt);
+                        rValRot.xRVal = rVals;
+                        rValRot.yRVal = (TokenRVal)rVals.nextToken;
+                        rValRot.zRVal = (TokenRVal)rVals.nextToken.nextToken;
+                        rValRot.wRVal = (TokenRVal)rVals.nextToken.nextToken.nextToken;
+                        return rValRot;
+                    }
+                    default: {
+                        ErrorMsg (openBkt, "bad rotation/vector");
+                        token = SkipPastSemi (token);
+                        return null;
+                    }
+                }
+            }
+
+            /*
+             * '['value,...']', ie, list
+             */
+            if (token is TokenKwBrkOpen) {
+                TokenRValList rValList = new TokenRValList (token);
+                token = token.nextToken;
+                if (token is TokenKwBrkClose) {
+                    token = token.nextToken;  // empty list
+                } else {
+                    TokenRVal rValAll = ParseRVal (ref token, brkCloseOnly);
+                    if (rValAll == null) return null;
+                    rValList.nItems = SplitCommaRVals (rValAll, out rValList.rVal);
+                }
+                return rValList;
+            }
+
+            /*
+             * Maybe we have <type>.<name> referencing a static field or method of some type.
+             */
+            if ((token is TokenType) && (token.nextToken is TokenKwDot) && (token.nextToken.nextToken is TokenName)) {
+                TokenLValSField field = new TokenLValSField (token.nextToken.nextToken);
+                field.baseType = (TokenType)token;
+                field.fieldName = (TokenName)token.nextToken.nextToken;
+                token = token.nextToken.nextToken.nextToken;
+                return field;
+            }
+
+            /*
+             * Maybe we have 'this' referring to the object of the instance method.
+             */
+            if (token is TokenKwThis) {
+                if ((currentDeclSDType == null) || !(currentDeclSDType is TokenDeclSDTypeClass)) {
+                    ErrorMsg (token, "using 'this' outside class definition");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+                TokenRValThis zhis = new TokenRValThis (token, (TokenDeclSDTypeClass)currentDeclSDType);
+                token = token.nextToken;
+                return zhis;
+            }
+
+            /*
+             * Maybe we have 'base' referring to a field/method of the extended class.
+             */
+            if (token is TokenKwBase) {
+                if ((currentDeclFunc == null) || (currentDeclFunc.sdtClass == null) || !(currentDeclFunc.sdtClass is TokenDeclSDTypeClass)) {
+                    ErrorMsg (token, "using 'base' outside method");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+                if (!(token.nextToken is TokenKwDot) || !(token.nextToken.nextToken is TokenName)) {
+                    ErrorMsg (token, "base must be followed by . then field or method name");
+                    TokenRValThis zhis = new TokenRValThis (token, (TokenDeclSDTypeClass)currentDeclFunc.sdtClass);
+                    token = token.nextToken;
+                    return zhis;
+                }
+                TokenLValBaseField baseField = new TokenLValBaseField (token, 
+                                               (TokenName)token.nextToken.nextToken, 
+                                               (TokenDeclSDTypeClass)currentDeclFunc.sdtClass);
+                token = token.nextToken.nextToken.nextToken;
+                return baseField;
+            }
+
+            /*
+             * Maybe we have 'new <script-defined-type>' saying to create an object instance.
+             * This ends up generating a call to static function <script-defined-type>.$new(...)
+             * whose CIL code is generated by GenerateNewobjBody().
+             */
+            if (token is TokenKwNew) {
+                if (!(token.nextToken is TokenType)) {
+                    ErrorMsg (token.nextToken, "new must be followed by type");
+                    token = SkipPastSemi (token);
+                    return null;
+                }
+                TokenLValSField field = new TokenLValSField (token.nextToken.nextToken);
+                field.baseType  = (TokenType)token.nextToken;
+                field.fieldName = new TokenName (token, "$new");
+                token = token.nextToken.nextToken;
+                return field;
+            }
+
+            /*
+             * All we got left is <name>, eg, arg, function, global or local variable reference
+             */
+            if (token is TokenName) {
+                TokenLValName name = new TokenLValName ((TokenName)token, tokenScript.variablesStack);
+                token = token.nextToken;
+                return name;
+            }
+
+            /*
+             * Who knows what it is supposed to be?
+             */
+            ErrorMsg (token, "invalid operand token");
+            token = SkipPastSemi (token);
+            return null;
+        }
+
+        /**
+         * @brief Parse a call expression
+         * @param token = points to arg list '('
+         * @param meth = points to method name being called
+         * @returns call expression value
+         *          token = points just past arg list ')'
+         */
+        private TokenRValCall ParseRValCall (ref Token token, TokenRVal meth)
+        {
+            /*
+             * Set up basic function call struct with function name.
+             */
+            TokenRValCall rValCall = new TokenRValCall (token);
+            rValCall.meth = meth;
+
+            /*
+             * Parse the call parameters, if any.
+             */
+            token = token.nextToken;
+            if (token is TokenKwParClose) {
+                token = token.nextToken;
+            } else {
+                rValCall.args = ParseRVal (ref token, parCloseOnly);
+                if (rValCall.args == null) return null;
+                rValCall.nArgs = SplitCommaRVals (rValCall.args, out rValCall.args);
+            }
+
+            currentDeclFunc.unknownTrivialityCalls.AddLast (rValCall);
+
+            return rValCall;
+        }
+
+        /**
+         * @brief decode binary operator token
+         * @param token = points to token to decode
+         * @returns null: invalid operator token
+         *          else: operator token and precedence
+         */
+        private BinOp GetOperator (ref Token token)
+        {
+            BinOp binOp = new BinOp ();
+            if (precedence.TryGetValue (token.GetType (), out binOp.preced)) {
+                binOp.token = (TokenKw)token;
+                token = token.nextToken;
+                return binOp;
+            }
+
+            if ((token is TokenKwSemi) || (token is TokenKwBrcOpen) || (token is TokenKwBrcClose)) {
+                ErrorMsg (token, "premature expression end");
+            } else {
+                ErrorMsg (token, "invalid operator");
+            }
+            token = SkipPastSemi (token);
+            return null;
+        }
+
+        private class BinOp {
+            public BinOp pop;
+            public TokenKw token;
+            public int preced;
+        }
+
+        /**
+         * @brief Return an R-value expression token that will be used to
+         *        generate code to perform the operation at runtime.
+         * @param left  = left-hand operand
+         * @param binOp = operator
+         * @param right = right-hand operand
+         * @returns resultant expression
+         */
+        private TokenRVal PerformBinOp (TokenRVal left, BinOp binOp, TokenRVal right)
+        {
+            return new TokenRValOpBin (left, binOp.token, right);
+        }
+
+        /**
+         * @brief Return an R-value expression token that will be used to
+         *        generate code to perform the operation at runtime.
+         * @param unOp  = operator
+         * @param right = right-hand operand
+         * @returns resultant constant or expression
+         */
+        private TokenRVal PerformUnOp (Token unOp, TokenRVal right)
+        {
+            return new TokenRValOpUn ((TokenKw)unOp, right);
+        }
+
+        /**
+         * @brief Parse an array initialization expression.
+         * @param token = points to '{' on entry
+         * @param newCall = encapsulates a '$new' call
+         * @return resultant operand encapsulating '$new' call and initializers
+         *             token = points just past terminating '}'
+         *         ...or null if parse error
+         */
+        private TokenRVal ParseRValNewArIni (ref Token token, TokenLValSField newCall)
+        {
+            Stack<TokenList> stack  = new Stack<TokenList> ();
+            TokenRValNewArIni arini = new TokenRValNewArIni (token);
+            arini.arrayType  = newCall.baseType;
+            TokenList values = null;
+            while (true) {
+
+                // open brace means start a (sub-)list
+                if (token is TokenKwBrcOpen) {
+                    stack.Push (values);
+                    values = new TokenList (token);
+                    token  = token.nextToken;
+                    continue;
+                }
+
+                // close brace means end of (sub-)list
+                // if final '}' all done parsing
+                if (token is TokenKwBrcClose) {
+                    token  = token.nextToken;          // skip over the '}'
+                    TokenList innerds = values;        // save the list just closed
+                    arini.valueList = innerds;         // it's the top list if it's the last closed
+                    values = stack.Pop ();             // pop to next outer list
+                    if (values == null) return arini;  // final '}', we are done
+                    values.tl.Add (innerds);           // put the inner list on end of outer list
+                    if (token is TokenKwComma) {       // should have a ',' or '}' next
+                        token = token.nextToken;   // skip over the ','
+                    } else if (!(token is TokenKwBrcClose)) {
+                        ErrorMsg (token, "expecting , or } after sublist");
+                    }
+                    continue;
+                }
+
+                // this is a comma that doesn't have a value expression before it
+                // so we take it to mean skip initializing element (leave it zeroes/null etc)
+                if (token is TokenKwComma) {
+                    values.tl.Add (token);
+                    token = token.nextToken;
+                    continue;
+                }
+
+                // parse value expression and skip terminating ',' if any
+                TokenRVal append = ParseRVal (ref token, commaOrBrcClose);
+                if (append == null) return null;
+                values.tl.Add (append);
+                if (token is TokenKwComma) {
+                    token = token.nextToken;
+                }
+            }
+        }
+
+        /**
+         * @brief parse out a parenthesized expression.
+         * @param token = points to open parenthesis
+         * @returns null: invalid expression
+         *          else: parenthesized expression token or constant token
+         *          token = points past the close parenthesis
+         */
+        private TokenRValParen ParseRValParen (ref Token token)
+        {
+            if (!(token is TokenKwParOpen)) {
+                ErrorMsg (token, "expecting (");
+                token = SkipPastSemi (token);
+                return null;
+            }
+            TokenRValParen tokenRValParen = new TokenRValParen (token);
+            token = token.nextToken;
+            tokenRValParen.rVal = ParseRVal (ref token, parCloseOnly);
+            if (tokenRValParen.rVal == null) return null;
+            return tokenRValParen;
+        }
+
+        /**
+         * @brief Split a comma'd RVal into separate expressions
+         * @param rValAll = expression containing commas
+         * @returns number of comma separated values
+         *          rVals = values in a null-terminated list linked by rVals.nextToken
+         */
+        private int SplitCommaRVals (TokenRVal rValAll, out TokenRVal rVals)
+        {
+            if (!(rValAll is TokenRValOpBin) || !(((TokenRValOpBin)rValAll).opcode is TokenKwComma)) {
+                rVals = rValAll;
+                if (rVals.nextToken != null) throw new Exception ("expected null");
+                return 1;
+            }
+            TokenRValOpBin opBin = (TokenRValOpBin)rValAll;
+            TokenRVal rValLeft, rValRight;
+            int leftCount  = SplitCommaRVals (opBin.rValLeft,  out rValLeft);
+            int rightCount = SplitCommaRVals (opBin.rValRight, out rValRight);
+            rVals = rValLeft;
+            while (rValLeft.nextToken != null) rValLeft = (TokenRVal)rValLeft.nextToken;
+            rValLeft.nextToken = rValRight;
+            return leftCount + rightCount;
+        }
+
+        /**
+         * @brief output error message and remember that there is an error.
+         * @param token = what token is associated with the error
+         * @param message = error message string
+         */
+        private void ErrorMsg (Token token, string message)
+        {
+            if (!errors || (token.file != lastErrorFile) || (token.line > lastErrorLine)) {
+                errors = true;
+                lastErrorFile = token.file;
+                lastErrorLine = token.line;
+                token.ErrorMsg (message);
+            }
+        }
+
+        /**
+         * @brief Skip past the next semicolon (or matched braces)
+         * @param token = points to token to skip over
+         * @returns token just after the semicolon or close brace
+         */
+        private Token SkipPastSemi (Token token)
+        {
+            int braceLevel = 0;
+
+            while (!(token is TokenEnd)) {
+                if ((token is TokenKwSemi) && (braceLevel == 0)) {
+                    return token.nextToken;
+                }
+                if (token is TokenKwBrcOpen) {
+                    braceLevel ++;
+                }
+                if ((token is TokenKwBrcClose) && (-- braceLevel <= 0)) {
+                    return token.nextToken;
+                }
+                token = token.nextToken;
+            }
+            return token;
+        }
+    }
+
+    /**
+     * @brief Script-defined type declarations
+     */
+    public abstract class TokenDeclSDType : Token {
+        protected const byte CLASS     = 0;
+        protected const byte DELEGATE  = 1;
+        protected const byte INTERFACE = 2;
+        protected const byte TYPEDEF   = 3;
+
+        // stuff that gets cloned/copied/transformed when instantiating a generic
+        // see InstantiateGeneric() below
+        public TokenDeclSDType outerSDType;          // null if top-level
+                                                     // else points to defining script-defined type
+        public Dictionary<string, TokenDeclSDType> innerSDTypes = new Dictionary<string, TokenDeclSDType> ();
+                                                     // indexed by shortName
+        public Token begToken;                       // token that begins the definition (might be this or something like 'public')
+        public Token endToken;                       // the '}' or ';' that ends the definition
+
+        // generic instantiation assumes none of the rest needs to be cloned (well except for the shortName)
+        public int sdTypeIndex = -1;                 // index in scriptObjCode.sdObjTypesIndx[] array
+        public TokenDeclSDTypeClass extends;         // only non-null for TokenDeclSDTypeClass's
+        public uint accessLevel;                     // SDT_PRIVATE, SDT_PROTECTED or SDT_PUBLIC
+                                                     // ... all top-level types are SDT_PUBLIC
+        public VarDict members = new VarDict (false);  // declared fields, methods, properties if any
+
+        public Dictionary<string, int> genParams;    // list of parameters for generic prototypes
+                                                     // null for non-generic prototypes
+                                                     // eg, for 'Dictionary<K,V>'
+                                                     //     ...genParams gives K->0; V->1
+
+        public bool isPartial;                       // was declared with 'partial' keyword
+                                                     // classes only, all others always false
+
+        /*
+         * Name of the type.
+         *   shortName = doesn't include outer class type names
+         *               eg, 'Engine' for non-generic
+         *                   'Dictionary<,>' for generic prototype
+         *                   'Dictionary<string,integer>' for generic instantiation
+         *   longName = includes all outer class type names if any
+         */
+        private TokenName _shortName;
+        private TokenName _longName;
+
+        public TokenName shortName {
+            get {
+                return _shortName;
+            }
+            set {
+                _shortName = value;
+                _longName  = null;
+            }
+        }
+
+        public TokenName longName {
+            get {
+                if (_longName == null) {
+                    _longName = _shortName;
+                    if (outerSDType != null) {
+                        _longName = new TokenName (_shortName, outerSDType.longName.val + "." + _shortName.val);
+                    }
+                }
+                return _longName;
+            }
+        }
+
+        /*
+         * Dictionary used when reading from object file that holds all script-defined types.
+         * Not complete though until all types have been read from the object file.
+         */
+        private Dictionary<string, TokenDeclSDType> sdTypes;
+
+        public TokenDeclSDType (Token t) : base (t) { }
+        protected abstract TokenDeclSDType MakeBlank (TokenName shortName);
+        public abstract TokenType MakeRefToken (Token t);
+        public abstract Type GetSysType ();
+        public abstract void WriteToFile (BinaryWriter objFileWriter);
+        public abstract void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter);
+
+        /**
+         * @brief Given that this is a generic prototype, apply the supplied genArgs 
+         *        to create an equivalent instantiated non-generic.  This basically 
+         *        makes a copy replacing all the parameter types with the actual 
+         *        argument types.
+         * @param this = the prototype to be instantiated, eg, 'Dictionary<string,integer>.Converter'
+         * @param name = short name with arguments, eg, 'Converter<float>'.
+         * @param genArgs = argument types of just this level, eg, 'float'.
+         * @returns clone of this but with arguments applied and spliced in source token stream
+         */
+        public TokenDeclSDType InstantiateGeneric (string name, TokenType[] genArgs, ScriptReduce reduce)
+        {
+            /*
+             * Malloc the struct and give it a name.
+             */
+            TokenDeclSDType instdecl = this.MakeBlank (new TokenName (this, name));
+
+            /*
+             * If the original had an outer type, then so does the new one.
+             * The outer type will never be a generic prototype, eg, if this 
+             * is 'ValueList' it will always be inside 'Dictionary<string,integer>'
+             * not 'Dictionary' at this point.
+             */
+            if ((this.outerSDType != null) && (this.outerSDType.genParams != null)) throw new Exception ();
+            instdecl.outerSDType = this.outerSDType;
+
+            /*
+             * The generic prototype may have stuff like 'public' just before it and we need to copy that too.
+             */
+            Token prefix;
+            for (prefix = this; (prefix = prefix.prevToken) != null;) {
+                if (!(prefix is TokenKwPublic) && !(prefix is TokenKwProtected) && !(prefix is TokenKwPrivate)) break;
+            }
+            this.begToken = prefix.nextToken;
+
+            /*
+             * Splice in a copy of the prefix tokens, just before the beginning token of prototype (this.begToken).
+             */
+            while ((prefix = prefix.nextToken) != this) {
+                SpliceSourceToken (prefix.CopyToken (prefix));
+            }
+
+            /*
+             * Splice instantiation (instdecl) in just before the beginning token of prototype (this.begToken).
+             */
+            SpliceSourceToken (instdecl);
+
+            /*
+             * Now for the fun part...  Copy the rest of the prototype body to the 
+             * instantiated body, replacing all generic parameter type tokens with 
+             * the corresponding generic argument types.  Note that the parameters 
+             * are numbered starting with the outermost so we need the full genArgs 
+             * array.  Eg if we are doing 'Converter<V=float>' from 
+             * 'Dictionary<T=string,U=integer>.Converter<V=float>', any V's are 
+             * numbered [2].  Any [0]s or [1]s should be gone by now but it doesn't 
+             * matter.
+             */
+            int index;
+            Token it, pt;
+            TokenDeclSDType innerProto = this;
+            TokenDeclSDType innerInst  = instdecl;
+            for (pt = this; (pt = pt.nextToken) != this.endToken;) {
+
+                /*
+                 * Coming across a sub-type's declaration involves a deep copy of the 
+                 * declaration token.  Fortunately we are early on in parsing, so there 
+                 * really isn't much to copy:
+                 *   1) short name is the same, eg, doing List of Dictionary<string,integer>.List is same short name as Dictionary<T,U>.List
+                 *      if generic, eg doing Converter<W> of Dictionary<T,U>.Converter<W>, we have to manually copy the W as well.
+                 *   2) outerSDType is transformed from Dictionary<T,U> to Dictionary<string,integer>.
+                 *   3) innerSDTypes is rebuilt when/if we find classes that are inner to this one.
+                 */
+                if (pt is TokenDeclSDType) {
+
+                    /*
+                     * Make a new TokenDeclSDType{Class,Delegate,Interface}.
+                     */
+                    TokenDeclSDType ptSDType = (TokenDeclSDType)pt;
+                    TokenDeclSDType itSDType = ptSDType.MakeBlank (new TokenName (ptSDType.shortName, ptSDType.shortName.val));
+
+                    /*
+                     * Set up the transformed outerSDType.
+                     * Eg, if we are creating Enumerator of Dictionary<string,integer>.Enumerator,
+                     * innerProto = Dictionary<T,U> and innerInst = Dictionary<string,integer>.
+                     */
+                    itSDType.outerSDType = innerInst;
+
+                    /*
+                     * This clone is an inner type of its next outer level.
+                     */
+                    reduce.CatalogSDTypeDecl (itSDType);
+
+                    /*
+                     * We need to manually copy any generic parameters of the class declaration being cloned.
+                     * eg, if we are cloning Converter<W>, this is where the W gets copied.
+                     * Since it is an immutable array of strings, just copy the array pointer, if any.
+                     */
+                    itSDType.genParams = ptSDType.genParams;
+
+                    /*
+                     * We are now processing tokens for this cloned type declaration.
+                     */
+                    innerProto = ptSDType;
+                    innerInst  = itSDType;
+
+                    /*
+                     * Splice this clone token in.
+                     */
+                    it = itSDType;
+                }
+
+                /*
+                 * Check for an generic parameter to substitute out.
+                 */
+                else if ((pt is TokenName) && this.genParams.TryGetValue (((TokenName)pt).val, out index)) {
+                    it = genArgs[index].CopyToken (pt);
+                }
+
+                /*
+                 * Everything else is a simple copy.
+                 */
+                else it = pt.CopyToken (pt);
+
+                /*
+                 * Whatever we came up with, splice it into the source token stream.
+                 */
+                SpliceSourceToken (it);
+
+                /*
+                 * Maybe we just finished copying an inner type definition.
+                 * If so, remember where it ends and pop it from the stack.
+                 */
+                if (innerProto.endToken == pt) {
+                    innerInst.endToken = it;
+                    innerProto = innerProto.outerSDType;
+                    innerInst  = innerInst.outerSDType;
+                }
+            }
+
+            /*
+             * Clone and insert the terminator, either '}' or ';'
+             */
+            it = pt.CopyToken (pt);
+            SpliceSourceToken (it);
+            instdecl.endToken = it;
+
+            return instdecl;
+        }
+
+        /**
+         * @brief Splice a source token in just before the type's beginning keyword.
+         */
+        private void SpliceSourceToken (Token it)
+        {
+            it.nextToken = this.begToken;
+            (it.prevToken = this.begToken.prevToken).nextToken = it;
+            this.begToken.prevToken = it;
+        }
+
+        /**
+         * @brief Read one of these in from the object file.
+         * @param sdTypes = dictionary of script-defined types, not yet complete
+         * @param name = script-visible name of this type
+         * @param objFileReader = reads from the object file
+         * @param asmFileWriter = writes to the disassembly file (might be null)
+         */
+        public static TokenDeclSDType ReadFromFile (Dictionary<string, TokenDeclSDType> sdTypes, string name, 
+                                                    BinaryReader objFileReader, TextWriter asmFileWriter)
+        {
+            string file = objFileReader.ReadString ();
+            int    line = objFileReader.ReadInt32 ();
+            int    posn = objFileReader.ReadInt32 ();
+            byte   code = objFileReader.ReadByte ();
+            TokenName n = new TokenName (null, file, line, posn, name);
+            TokenDeclSDType sdt;
+            switch (code) {
+                case CLASS: {
+                    sdt = new TokenDeclSDTypeClass (n, false);
+                    break;
+                }
+                case DELEGATE: {
+                    sdt = new TokenDeclSDTypeDelegate (n);
+                    break;
+                }
+                case INTERFACE: {
+                    sdt = new TokenDeclSDTypeInterface (n);
+                    break;
+                }
+                case TYPEDEF: {
+                    sdt = new TokenDeclSDTypeTypedef (n);
+                    break;
+                }
+                default: throw new Exception ();
+            }
+            sdt.sdTypes = sdTypes;
+            sdt.sdTypeIndex = objFileReader.ReadInt32 ();
+            sdt.ReadFromFile (objFileReader, asmFileWriter);
+            return sdt;
+        }
+
+        /**
+         * @brief Convert a typename string to a type token
+         * @param name = script-visible name of token to create, 
+         *               either a script-defined type or an LSL-defined type
+         * @returns type token
+         */
+        protected TokenType MakeTypeToken (string name)
+        {
+            TokenDeclSDType sdtdecl;
+            if (sdTypes.TryGetValue (name, out sdtdecl)) return sdtdecl.MakeRefToken (this);
+            return TokenType.FromLSLType (this, name);
+        }
+
+        // debugging - returns, eg, 'Dictionary<T,U>.Enumerator.Node'
+        public override void DebString (StringBuilder sb)
+        {
+            // get long name broken down into segments from outermost to this
+            Stack<TokenDeclSDType> declStack = new Stack<TokenDeclSDType> ();
+            for (TokenDeclSDType decl = this; decl != null; decl = decl.outerSDType) {
+                declStack.Push (decl);
+            }
+
+            // output each segment's name followed by our args for it
+            // starting with outermost and ending with this
+            while (declStack.Count > 0) {
+                TokenDeclSDType decl = declStack.Pop ();
+                sb.Append (decl.shortName.val);
+                if (decl.genParams != null) {
+                    sb.Append ('<');
+                    string[] parms = new string[decl.genParams.Count];
+                    foreach (KeyValuePair<string, int> kvp in decl.genParams) {
+                        parms[kvp.Value] = kvp.Key;
+                    }
+                    for (int j = 0; j < parms.Length;) {
+                        sb.Append (parms[j]);
+                        if (++ j < parms.Length) sb.Append (',');
+                    }
+                    sb.Append ('>');
+                }
+                if (declStack.Count > 0) sb.Append ('.');
+            }
+        }
+    }
+
+    public class TokenDeclSDTypeClass : TokenDeclSDType {
+        public List<TokenDeclSDTypeInterface> implements = new List<TokenDeclSDTypeInterface> ();
+        public TokenDeclVar instFieldInit;         // $instfieldinit function to do instance field initializations
+        public TokenDeclVar staticFieldInit;       // $staticfieldinit function to do static field initializations
+
+        public Dictionary<string, int> intfIndices = new Dictionary<string, int> ();  // longname => this.iFaces index
+        public TokenDeclSDTypeInterface[] iFaces;  // array of implemented interfaces
+                                                   //   low-end entries copied from rootward classes
+        public TokenDeclVar[][] iImplFunc;         // iImplFunc[i][j]:
+                                                   //   low-end [i] entries copied from rootward classes
+                                                   //   i = interface number from this.intfIndices[name]
+                                                   //   j = method of interface from iface.methods[name].vTableIndex
+
+        public TokenType arrayOfType;     // if array, it's an array of this type, else null
+        public int arrayOfRank;           // if array, it has this number of dimensions, else zero
+
+        public bool slotsAssigned;        // set true when slots have been assigned...
+        public XMRInstArSizes instSizes = new XMRInstArSizes ();
+                                          // number of instance fields of various types
+        public int numVirtFuncs;          // number of virtual functions
+        public int numInterfaces;         // number of implemented interfaces
+
+        private string extendsStr;
+        private string arrayOfTypeStr;
+        private List<StackedMethod> stackedMethods;
+        private List<StackedIFace>  stackedIFaces;
+
+        public DynamicMethod[] vDynMeths;    // virtual method entrypoints
+        public Type[] vMethTypes;            // virtual method delegate types
+        public DynamicMethod[][] iDynMeths;  // interface method entrypoints
+        public Type[][] iMethTypes;          // interface method types
+                                             //   low-end [i] entries copied from rootward classes
+                                             //   i = interface number from this.intfIndices[name]
+                                             //   j = method of interface from iface.methods[name].vTableIndex
+
+        public TokenDeclSDTypeClass (TokenName shortName, bool isPartial) : base (shortName)
+        {
+            this.shortName = shortName;
+            this.isPartial = isPartial;
+        }
+
+        protected override TokenDeclSDType MakeBlank (TokenName shortName)
+        {
+            return new TokenDeclSDTypeClass (shortName, false);
+        }
+
+        public override TokenType MakeRefToken (Token t)
+        {
+            return new TokenTypeSDTypeClass (t, this);
+        }
+
+        public override Type GetSysType ()
+        {
+            return typeof (XMRSDTypeClObj);
+        }
+
+        /**
+         * @brief See if the class implements the interface.
+         *        Do a recursive (deep) check in all rootward classes.
+         */
+        public bool CanCastToIntf (TokenDeclSDTypeInterface intf)
+        {
+            if (this.implements.Contains (intf)) return true;
+            if (this.extends == null) return false;
+            return this.extends.CanCastToIntf (intf);
+        }
+
+        /**
+         * @brief Write enough out so we can reconstruct with ReadFromFile.
+         */
+        public override void WriteToFile (BinaryWriter objFileWriter)
+        {
+            objFileWriter.Write (this.file);
+            objFileWriter.Write (this.line);
+            objFileWriter.Write (this.posn);
+            objFileWriter.Write ((byte)CLASS);
+            objFileWriter.Write (this.sdTypeIndex);
+
+            this.instSizes.WriteToFile (objFileWriter);
+            objFileWriter.Write (numVirtFuncs);
+
+            if (extends == null) {
+                objFileWriter.Write ("");
+            } else {
+                objFileWriter.Write (extends.longName.val);
+            }
+
+            objFileWriter.Write (arrayOfRank);
+            if (arrayOfRank > 0) objFileWriter.Write (arrayOfType.ToString ());
+
+            foreach (TokenDeclVar meth in members) {
+                if ((meth.retType != null) && (meth.vTableIndex >= 0)) {
+                    objFileWriter.Write (meth.vTableIndex);
+                    objFileWriter.Write (meth.GetObjCodeName ());
+                    objFileWriter.Write (meth.GetDelType ().decl.GetWholeSig ());
+                }
+            }
+            objFileWriter.Write (-1);
+
+            int numIFaces = iImplFunc.Length;
+            objFileWriter.Write (numIFaces);
+            for (int i = 0; i < numIFaces; i ++) {
+                objFileWriter.Write (iFaces[i].longName.val);
+                TokenDeclVar[] meths = iImplFunc[i];
+                int numMeths = 0;
+                if (meths != null) numMeths = meths.Length;
+                objFileWriter.Write (numMeths);
+                for (int j = 0; j < numMeths; j ++) {
+                    TokenDeclVar meth = meths[j];
+                    objFileWriter.Write (meth.vTableIndex);
+                    objFileWriter.Write (meth.GetObjCodeName ());
+                    objFileWriter.Write (meth.GetDelType ().decl.GetWholeSig ());
+                }
+            }
+        }
+
+        /**
+         * @brief Reconstruct from the file.
+         */
+        public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
+        {
+            instSizes.ReadFromFile (objFileReader);
+            numVirtFuncs = objFileReader.ReadInt32 ();
+
+            extendsStr = objFileReader.ReadString ();
+            arrayOfRank = objFileReader.ReadInt32 ();
+            if (arrayOfRank > 0) arrayOfTypeStr = objFileReader.ReadString ();
+
+            if (asmFileWriter != null) {
+                instSizes.WriteAsmFile (asmFileWriter, extendsStr + "." + shortName.val + ".numInst");
+            }
+
+            stackedMethods = new List<StackedMethod> ();
+            int vTableIndex;
+            while ((vTableIndex = objFileReader.ReadInt32 ()) >= 0) {
+                StackedMethod sm;
+                sm.methVTI  = vTableIndex;
+                sm.methName = objFileReader.ReadString ();
+                sm.methSig  = objFileReader.ReadString ();
+                stackedMethods.Add (sm);
+            }
+
+            int numIFaces = objFileReader.ReadInt32 ();
+            if (numIFaces > 0) {
+                iDynMeths  = new DynamicMethod[numIFaces][];
+                iMethTypes = new Type[numIFaces][];
+                stackedIFaces = new List<StackedIFace> ();
+                for (int i = 0; i < numIFaces; i ++) {
+                    string iFaceName = objFileReader.ReadString ();
+                    intfIndices[iFaceName] = i;
+                    int numMeths  = objFileReader.ReadInt32 ();
+                    iDynMeths[i]  = new DynamicMethod[numMeths];
+                    iMethTypes[i] = new Type[numMeths];
+                    for (int j = 0; j < numMeths; j ++) {
+                        StackedIFace si;
+                        si.iFaceIndex  = i;
+                        si.methIndex   = j;
+                        si.vTableIndex = objFileReader.ReadInt32 ();
+                        si.methName    = objFileReader.ReadString ();
+                        si.methSig     = objFileReader.ReadString ();
+                        stackedIFaces.Add (si);
+                    }
+                }
+            }
+        }
+
+        private struct StackedMethod {
+            public int methVTI;
+            public string methName;
+            public string methSig;
+        }
+
+        private struct StackedIFace {
+            public int iFaceIndex;   // which implemented interface
+            public int methIndex;    // which method of that interface
+            public int vTableIndex;  // <0: implemented by non-virtual; else: implemented by virtual
+            public string methName;  // object code name of implementing method (GetObjCodeName)
+            public string methSig;   // method signature incl return type (GetWholeSig)
+        }
+
+        /**
+         * @brief Called after all dynamic method code has been generated to fill in vDynMeths and vMethTypes
+         *        Also fills in iDynMeths, iMethTypes.
+         */
+        public void FillVTables (ScriptObjCode scriptObjCode)
+        {
+            if (extendsStr != null) {
+                if (extendsStr != "") {
+                    extends = (TokenDeclSDTypeClass)scriptObjCode.sdObjTypesName[extendsStr];
+                    extends.FillVTables (scriptObjCode);
+                }
+                extendsStr = null;
+            }
+            if (arrayOfTypeStr != null) {
+                arrayOfType = MakeTypeToken (arrayOfTypeStr);
+                arrayOfTypeStr = null;
+            }
+
+            if ((numVirtFuncs > 0) && (stackedMethods != null)) {
+
+                /*
+                 * Allocate arrays big enough for mine plus type we are extending.
+                 */
+                vDynMeths  = new DynamicMethod[numVirtFuncs];
+                vMethTypes = new Type[numVirtFuncs];
+
+                /*
+                 * Fill in low parts from type we are extending.
+                 */
+                if (extends != null) {
+                    int n = extends.numVirtFuncs;
+                    for (int i = 0; i < n; i ++) {
+                        vDynMeths[i]  = extends.vDynMeths[i];
+                        vMethTypes[i] = extends.vMethTypes[i];
+                    }
+                }
+
+                /*
+                 * Fill in high parts with my own methods.
+                 * Might also overwrite lower ones with 'override' methods.
+                 */
+                foreach (StackedMethod sm in stackedMethods) {
+                    int i = sm.methVTI;
+                    string methName = sm.methName;
+                    DynamicMethod dm;
+                    if (scriptObjCode.dynamicMethods.TryGetValue (methName, out dm)) {
+                        // method is not abstract
+                        vDynMeths[i]  = dm;
+                        vMethTypes[i] = GetDynamicMethodDelegateType (dm, sm.methSig);
+                    }
+                }
+                stackedMethods = null;
+            }
+
+            if (stackedIFaces != null) {
+                foreach (StackedIFace si in stackedIFaces) {
+                    int i   = si.iFaceIndex;
+                    int j   = si.methIndex;
+                    int vti = si.vTableIndex;
+                    string methName  = si.methName;
+                    DynamicMethod dm = scriptObjCode.dynamicMethods[methName];
+                    iDynMeths[i][j]  = (vti < 0) ? dm : vDynMeths[vti];
+                    iMethTypes[i][j] = GetDynamicMethodDelegateType (dm, si.methSig);
+                }
+                stackedIFaces = null;
+            }
+        }
+
+        private Type GetDynamicMethodDelegateType (DynamicMethod dm, string methSig)
+        {
+            Type retType = dm.ReturnType;
+            ParameterInfo[] pi = dm.GetParameters ();
+            Type[] argTypes = new Type[pi.Length];
+            for (int j = 0; j < pi.Length; j ++) {
+                argTypes[j] = pi[j].ParameterType;
+            }
+            return DelegateCommon.GetType (retType, argTypes, methSig);
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            /*
+             * Don't output if array of some type.
+             * They will be re-instantiated as referenced by rest of script.
+             */
+            if (arrayOfType != null) return;
+
+            /*
+             * This class name and extended/implemented type declaration.
+             */
+            sb.Append ("class ");
+            sb.Append (shortName.val);
+            bool first = true;
+            if (extends != null) {
+                sb.Append (" : ");
+                sb.Append (extends.longName);
+                first = false;
+            }
+            foreach (TokenDeclSDType impld in implements) {
+                sb.Append (first ? " : " : ", ");
+                sb.Append (impld.longName);
+                first = false;
+            }
+            sb.Append (" {");
+
+            /*
+             * Inner type definitions.
+             */
+            foreach (TokenDeclSDType subs in innerSDTypes.Values) {
+                subs.DebString (sb);
+            }
+
+            /*
+             * Members (fields, methods, properties).
+             */
+            foreach (TokenDeclVar memb in members) {
+                if ((memb == instFieldInit) || (memb == staticFieldInit)) {
+                    memb.DebStringInitFields (sb);
+                } else if (memb.retType != null) {
+                    memb.DebString (sb);
+                }
+            }
+
+            sb.Append ('}');
+        }
+    }
+
+    public class TokenDeclSDTypeDelegate : TokenDeclSDType {
+        private TokenType retType;
+        private TokenType[] argTypes;
+
+        private string argSig;
+        private string wholeSig;
+        private Type sysType;
+        private Type retSysType;
+        private Type[] argSysTypes;
+
+        private string retStr;
+        private string[] argStrs;
+
+        private static Dictionary<string, TokenDeclSDTypeDelegate> inlines = new Dictionary<string, TokenDeclSDTypeDelegate> ();
+        private static Dictionary<Type, string> inlrevs = new Dictionary<Type, string> ();
+
+        public TokenDeclSDTypeDelegate (TokenName shortName) : base (shortName)
+        {
+            this.shortName = shortName;
+        }
+        public void SetRetArgTypes (TokenType retType, TokenType[] argTypes)
+        {
+            this.retType  = retType;
+            this.argTypes = argTypes;
+        }
+
+        protected override TokenDeclSDType MakeBlank (TokenName shortName)
+        {
+            return new TokenDeclSDTypeDelegate (shortName);
+        }
+
+        public override TokenType MakeRefToken (Token t)
+        {
+            return new TokenTypeSDTypeDelegate (t, this);
+        }
+
+        /**
+         * @brief Get system type for the whole delegate.
+         */
+        public override Type GetSysType ()
+        {
+            if (sysType == null) FillInStuff ();
+            return sysType;
+        }
+
+        /**
+         * @brief Get the function's return value type (TokenTypeVoid if void, never null)
+         */
+        public TokenType GetRetType ()
+        {
+            if (retType == null) FillInStuff ();
+            return retType;
+        }
+
+        /**
+         * @brief Get the function's argument types
+         */
+        public TokenType[] GetArgTypes ()
+        {
+            if (argTypes == null) FillInStuff ();
+            return argTypes;
+        }
+
+        /**
+         * @brief Get signature for the whole delegate, eg, "void(integer,list)"
+         */
+        public string GetWholeSig ()
+        {
+            if (wholeSig == null) FillInStuff ();
+            return wholeSig;
+        }
+
+        /**
+         * @brief Get signature for the arguments, eg, "(integer,list)"
+         */
+        public string GetArgSig ()
+        {
+            if (argSig == null) FillInStuff ();
+            return argSig;
+        }
+
+        /**
+         * @brief Find out how to create one of these delegates.
+         */
+        public ConstructorInfo GetConstructorInfo ()
+        {
+            if (sysType == null) FillInStuff ();
+            return sysType.GetConstructor (DelegateCommon.constructorArgTypes);
+        }
+
+        /**
+         * @brief Find out how to call what one of these delegates points to.
+         */
+        public MethodInfo GetInvokerInfo ()
+        {
+            if (sysType == null) FillInStuff ();
+            return sysType.GetMethod ("Invoke", argSysTypes);
+        }
+
+        /**
+         * @brief Write enough out to a file so delegate can be reconstructed in ReadFromFile().
+         */
+        public override void WriteToFile (BinaryWriter objFileWriter)
+        {
+            objFileWriter.Write (this.file);
+            objFileWriter.Write (this.line);
+            objFileWriter.Write (this.posn);
+            objFileWriter.Write ((byte)DELEGATE);
+            objFileWriter.Write (this.sdTypeIndex);
+
+            objFileWriter.Write (retType.ToString ());
+            int nArgs = argTypes.Length;
+            objFileWriter.Write (nArgs);
+            for (int i = 0; i < nArgs; i ++) {
+                objFileWriter.Write (argTypes[i].ToString ());
+            }
+        }
+
+        /**
+         * @brief Read that data from file so we can reconstruct.
+         *        Don't actually reconstruct yet in case any forward-referenced types are undefined.
+         */
+        public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
+        {
+            retStr = objFileReader.ReadString ();
+            int nArgs = objFileReader.ReadInt32 ();
+            if (asmFileWriter != null) {
+                asmFileWriter.Write ("  delegate " + retStr + " " + longName.val + "(");
+            }
+            argStrs = new string[nArgs];
+            for (int i = 0; i < nArgs; i ++) {
+                argStrs[i] = objFileReader.ReadString ();
+                if (asmFileWriter != null) {
+                    if (i > 0) asmFileWriter.Write (",");
+                    asmFileWriter.Write (argStrs[i]);
+                }
+            }
+            if (asmFileWriter != null) {
+                asmFileWriter.WriteLine (");");
+            }
+        }
+
+        /**
+         * @brief Fill in missing internal data.
+         */
+        private void FillInStuff ()
+        {
+            int nArgs;
+
+            /*
+             * This happens when the node was restored via ReadFromFile().
+             * It leaves the types in retStr/argStrs for resolution after
+             * all definitions have been read from the object file in case
+             * there are forward references.
+             */
+            if (retType == null) {
+                retType = MakeTypeToken (retStr);
+            }
+            if (argTypes == null) {
+                nArgs = argStrs.Length;
+                argTypes = new TokenType[nArgs];
+                for (int i = 0; i < nArgs; i ++) {
+                    argTypes[i] = MakeTypeToken (argStrs[i]);
+                }
+            }
+
+            /*
+             * Fill in system types from token types.
+             * Might as well build the signature strings too from token types.
+             */
+            retSysType = retType.ToSysType();
+
+            nArgs = argTypes.Length;
+            StringBuilder sb = new StringBuilder ();
+            argSysTypes = new Type[nArgs];
+            sb.Append ('(');
+            for (int i = 0; i < nArgs; i ++) {
+                if (i > 0) sb.Append (',');
+                sb.Append (argTypes[i].ToString ());
+                argSysTypes[i] = argTypes[i].ToSysType ();
+            }
+            sb.Append (')');
+            argSig = sb.ToString ();
+            wholeSig = retType.ToString () + argSig;
+
+            /*
+             * Now we can create a system delegate type from the given
+             * return and argument types.  Give it an unique name using
+             * the whole signature string.
+             */
+            sysType = DelegateCommon.GetType (retSysType, argSysTypes, wholeSig);
+        }
+
+        /**
+         * @brief create delegate reference token for inline functions.
+         *        there is just one instance of these per inline function
+         *        shared by all scripts, and it is just used when the
+         *        script engine is loaded.
+         */
+        public static TokenDeclSDTypeDelegate CreateInline (TokenType retType, TokenType[] argTypes)
+        {
+            TokenDeclSDTypeDelegate decldel;
+
+            /*
+             * Name it after the whole signature string.
+             */
+            StringBuilder sb = new StringBuilder ("$inline");
+            sb.Append (retType.ToString ());
+            sb.Append ("(");
+            bool first = true;
+            foreach (TokenType at in argTypes) {
+                if (!first) sb.Append (",");
+                sb.Append (at.ToString ());
+                first = false;
+            }
+            sb.Append (")");
+            string inlname = sb.ToString ();
+            if (!inlines.TryGetValue (inlname, out decldel)) {
+
+                /*
+                 * Create the corresponding declaration and link to it
+                 */
+                TokenName name   = new TokenName (null, inlname);
+                decldel          = new TokenDeclSDTypeDelegate (name);
+                decldel.retType  = retType;
+                decldel.argTypes = argTypes;
+                inlines.Add (inlname, decldel);
+                inlrevs.Add (decldel.GetSysType (), inlname);
+            }
+            return decldel;
+        }
+
+        public static string TryGetInlineName (Type sysType)
+        {
+            string name;
+            if (!inlrevs.TryGetValue (sysType, out name)) return null;
+            return name;
+        }
+
+        public static Type TryGetInlineSysType (string name)
+        {
+            TokenDeclSDTypeDelegate decl;
+            if (!inlines.TryGetValue (name, out decl)) return null;
+            return decl.GetSysType ();
+        }
+    }
+
+    public class TokenDeclSDTypeInterface : TokenDeclSDType {
+        public VarDict methsNProps = new VarDict (false);
+                                // any class that implements this interface
+                                // must implement all of these methods & properties
+
+        public List<TokenDeclSDTypeInterface> implements = new List<TokenDeclSDTypeInterface> ();
+                                // any class that implements this interface
+                                // must also implement all of the methods & properties 
+                                // of all of these interfaces
+
+        public TokenDeclSDTypeInterface (TokenName shortName) : base (shortName)
+        {
+            this.shortName = shortName;
+        }
+
+        protected override TokenDeclSDType MakeBlank (TokenName shortName)
+        {
+            return new TokenDeclSDTypeInterface (shortName);
+        }
+
+        public override TokenType MakeRefToken (Token t)
+        {
+            return new TokenTypeSDTypeInterface (t, this);
+        }
+
+        public override Type GetSysType ()
+        {
+            // interfaces are implemented as arrays of delegates
+            // they are taken from iDynMeths[interfaceIndex] of a script-defined class object
+            return typeof (Delegate[]);
+        }
+
+        public override void WriteToFile (BinaryWriter objFileWriter)
+        {
+            objFileWriter.Write (this.file);
+            objFileWriter.Write (this.line);
+            objFileWriter.Write (this.posn);
+            objFileWriter.Write ((byte)INTERFACE);
+            objFileWriter.Write (this.sdTypeIndex);
+        }
+
+        public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
+        { }
+
+        /**
+         * @brief Add this interface to the list of interfaces implemented by a class if not already.
+         *        And also add this interface's implemented interfaces to the class for those not already there, 
+         *        just as if the class itself had declared to implement those interfaces.
+         */
+        public void AddToClassDecl (TokenDeclSDTypeClass tokdeclcl)
+        {
+            if (!tokdeclcl.implements.Contains (this)) {
+                tokdeclcl.implements.Add (this);
+                foreach (TokenDeclSDTypeInterface subimpl in this.implements) {
+                    subimpl.AddToClassDecl (tokdeclcl);
+                }
+            }
+        }
+
+        /**
+         * @brief See if the 'this' interface implements the new interface.
+         *        Do a recursive (deep) check.
+         */
+        public bool Implements (TokenDeclSDTypeInterface newDecl)
+        {
+            foreach (TokenDeclSDTypeInterface ii in this.implements) {
+                if (ii == newDecl) return true;
+                if (ii.Implements (newDecl)) return true;
+            }
+            return false;
+        }
+
+        /**
+         * @brief Scan an interface and all its implemented interfaces for a method or property
+         * @param scg = script code generator (ie, which script is being compiled)
+         * @param fieldName = name of the member being looked for
+         * @param argsig = the method's argument signature
+         * @returns null: no such member; intf = undefined
+         *          else: member; intf = which interface actually found in
+         */
+        public TokenDeclVar FindIFaceMember (ScriptCodeGen scg, TokenName fieldName, TokenType[] argsig, out TokenDeclSDTypeInterface intf)
+        {
+            intf = this;
+            TokenDeclVar var = scg.FindSingleMember (this.methsNProps, fieldName, argsig);
+            if (var == null) {
+                foreach (TokenDeclSDTypeInterface ii in this.implements) {
+                    var = ii.FindIFaceMember (scg, fieldName, argsig, out intf);
+                    if (var != null) break;
+                }
+            }
+            return var;
+        }
+    }
+
+    public class TokenDeclSDTypeTypedef : TokenDeclSDType {
+
+        public TokenDeclSDTypeTypedef (TokenName shortName) : base (shortName)
+        {
+            this.shortName = shortName;
+        }
+
+        protected override TokenDeclSDType MakeBlank (TokenName shortName)
+        {
+            return new TokenDeclSDTypeTypedef (shortName);
+        }
+
+        public override TokenType MakeRefToken (Token t)
+        {
+            // if our body is a single type token, that is what we return
+            // otherwise return null saying maybe our body needs some substitutions
+            if (!(this.nextToken is TokenType)) return null;
+            if (this.nextToken.nextToken != this.endToken) {
+                this.nextToken.nextToken.ErrorMsg ("extra tokens for typedef");
+                return null;
+            }
+            return (TokenType)this.nextToken.CopyToken (t);
+        }
+
+        public override Type GetSysType ()
+        {
+            // we are just a macro
+            // we are asked for system type because we are cataloged
+            // but we don't really have one so return null
+            return null;
+        }
+
+        public override void WriteToFile (BinaryWriter objFileWriter)
+        {
+            objFileWriter.Write (this.file);
+            objFileWriter.Write (this.line);
+            objFileWriter.Write (this.posn);
+            objFileWriter.Write ((byte)TYPEDEF);
+            objFileWriter.Write (this.sdTypeIndex);
+        }
+
+        public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
+        { }
+    }
+
+    /**
+     * @brief Script-defined type references.
+     *        These occur in the source code wherever it specifies (eg, variable declaration) a script-defined type.
+     *        These must be copyable via CopyToken().
+     */
+    public abstract class TokenTypeSDType : TokenType {
+        public TokenTypeSDType (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeSDType (Token t) : base (t) { }
+        public abstract TokenDeclSDType GetDecl ();
+        public abstract void SetDecl (TokenDeclSDType decl);
+    }
+
+    public class TokenTypeSDTypeClass : TokenTypeSDType {
+        private static readonly FieldInfo iarSDTClObjsFieldInfo = typeof (XMRInstArrays).GetField ("iarSDTClObjs");
+
+        public TokenDeclSDTypeClass decl;
+
+        public TokenTypeSDTypeClass (Token t, TokenDeclSDTypeClass decl) : base (t)
+        {
+            this.decl = decl;
+        }
+        public override TokenDeclSDType GetDecl ()
+        {
+            return decl;
+        }
+        public override void SetDecl (TokenDeclSDType decl)
+        {
+            this.decl = (TokenDeclSDTypeClass)decl;
+        }
+        public override string ToString ()
+        {
+            return decl.longName.val;
+        }
+        public override Type ToSysType ()
+        {
+            return typeof (XMRSDTypeClObj);
+        }
+
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias)
+        {
+            declVar.vTableArray = iarSDTClObjsFieldInfo;
+            declVar.vTableIndex = ias.iasSDTClObjs ++;
+        }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append (decl.longName);
+        }
+    }
+
+    public class TokenTypeSDTypeDelegate : TokenTypeSDType {
+        private static readonly FieldInfo iarObjectsFieldInfo = typeof (XMRInstArrays).GetField ("iarObjects");
+
+        public TokenDeclSDTypeDelegate decl;
+
+        /**
+         * @brief create a reference to an explicitly declared delegate
+         * @param t = where the reference is being made in the source file
+         * @param decl = the explicit delegate declaration
+         */
+        public TokenTypeSDTypeDelegate (Token t, TokenDeclSDTypeDelegate decl) : base (t)
+        {
+            this.decl = decl;
+        }
+        public override TokenDeclSDType GetDecl ()
+        {
+            return decl;
+        }
+        public override void SetDecl (TokenDeclSDType decl)
+        {
+            this.decl = (TokenDeclSDTypeDelegate)decl;
+        }
+
+        /**
+         * @brief create a reference to a possibly anonymous delegate
+         * @param t = where the reference is being made in the source file
+         * @param retType = return type (TokenTypeVoid if void, never null)
+         * @param argTypes = script-visible argument types
+         * @param tokenScript = what script this is part of
+         */
+        public TokenTypeSDTypeDelegate (Token t, TokenType retType, TokenType[] argTypes, TokenScript tokenScript) : base (t)
+        {
+            TokenDeclSDTypeDelegate decldel;
+
+            /*
+             * See if we already have a matching declared one cataloged.
+             */
+            int nArgs = argTypes.Length;
+            foreach (TokenDeclSDType decl in tokenScript.sdSrcTypesValues) {
+                if (decl is TokenDeclSDTypeDelegate) {
+                    decldel         = (TokenDeclSDTypeDelegate)decl;
+                    TokenType   rt  = decldel.GetRetType  ();
+                    TokenType[] ats = decldel.GetArgTypes ();
+                    if ((rt.ToString () == retType.ToString ()) && (ats.Length == nArgs)) {
+                        for (int i = 0; i < nArgs; i ++) {
+                            if (ats[i].ToString () != argTypes[i].ToString ()) goto nomatch;
+                        }
+                        this.decl = decldel;
+                        return;
+                    }
+                }
+            nomatch:;
+            }
+
+            /*
+             * No such luck, create a new anonymous declaration.
+             */
+            StringBuilder sb = new StringBuilder ("$anondel$");
+            sb.Append (retType.ToString ());
+            sb.Append ("(");
+            bool first = true;
+            foreach (TokenType at in argTypes) {
+                if (!first) sb.Append (",");
+                sb.Append (at.ToString ());
+                first = false;
+            }
+            sb.Append (")");
+            TokenName name = new TokenName (t, sb.ToString ());
+            decldel        = new TokenDeclSDTypeDelegate (name);
+            decldel.SetRetArgTypes (retType, argTypes);
+            tokenScript.sdSrcTypesAdd (name.val, decldel);
+            this.decl = decldel;
+        }
+
+        public override Type ToSysType ()
+        {
+            return decl.GetSysType ();
+        }
+
+        public override string ToString ()
+        {
+            return decl.longName.val;
+        }
+
+        /**
+         * @brief Assign slots in the gblObjects[] array because we have to typecast out in any case.
+         *        Likewise with the sdtcObjects[] array.
+         */
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias)
+        {
+            declVar.vTableArray = iarObjectsFieldInfo;
+            declVar.vTableIndex = ias.iasObjects ++;
+        }
+
+        /**
+         * @brief create delegate reference token for inline functions.
+         */
+        public TokenTypeSDTypeDelegate (TokenType retType, TokenType[] argTypes) : base (null)
+        {
+            this.decl = TokenDeclSDTypeDelegate.CreateInline (retType, argTypes);
+        }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append (decl.longName);
+        }
+    }
+
+    public class TokenTypeSDTypeInterface : TokenTypeSDType {
+        private static readonly FieldInfo iarSDTIntfObjsFieldInfo = typeof (XMRInstArrays).GetField ("iarSDTIntfObjs");
+
+        public TokenDeclSDTypeInterface decl;
+
+        public TokenTypeSDTypeInterface (Token t, TokenDeclSDTypeInterface decl) : base (t)
+        {
+            this.decl = decl;
+        }
+        public override TokenDeclSDType GetDecl ()
+        {
+            return decl;
+        }
+        public override void SetDecl (TokenDeclSDType decl)
+        {
+            this.decl = (TokenDeclSDTypeInterface)decl;
+        }
+
+        public override string ToString ()
+        {
+            return decl.longName.val;
+        }
+        public override Type ToSysType ()
+        {
+            return typeof (Delegate[]);
+        }
+
+        /**
+         * @brief Assign slots in the gblSDTIntfObjs[] array
+         *        Likewise with the sdtcSDTIntfObjs[] array.
+         */
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias)
+        {
+            declVar.vTableArray = iarSDTIntfObjsFieldInfo;
+            declVar.vTableIndex = ias.iasSDTIntfObjs ++;
+        }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append (decl.longName);
+        }
+    }
+
+    /**
+     * @brief function argument list declaration
+     */
+    public class TokenArgDecl : Token
+    {
+        public VarDict varDict = new VarDict (false);
+
+        public TokenArgDecl (Token original) : base (original) { }
+
+        public bool AddArg (TokenType type, TokenName name)
+        {
+            TokenDeclVar var = new TokenDeclVar (name, null, null);
+            var.name = name;
+            var.type = type;
+            var.vTableIndex = varDict.Count;
+            return varDict.AddEntry (var);
+        }
+
+        /**
+         * @brief Get an array of the argument types.
+         */
+        private TokenType[] _types;
+        public TokenType[] types {
+            get {
+                if (_types == null) {
+                    _types = new TokenType[varDict.Count];
+                    foreach (TokenDeclVar var in varDict) {
+                        _types[var.vTableIndex] = var.type;
+                    }
+                }
+                return _types;
+            }
+        }
+
+        /**
+         * @brief Access the arguments as an array of variables.
+         */
+        private TokenDeclVar[] _vars;
+        public TokenDeclVar[] vars {
+            get {
+                if (_vars == null) {
+                    _vars = new TokenDeclVar[varDict.Count];
+                    foreach (TokenDeclVar var in varDict) {
+                        _vars[var.vTableIndex] = var;
+                    }
+                }
+                return _vars;
+            }
+        }
+
+        /**
+         * @brief Get argument signature string, eg, "(list,vector,integer)"
+         */
+        private string argSig = null;
+        public string GetArgSig ()
+        {
+            if (argSig == null) {
+                argSig = ScriptCodeGen.ArgSigString (types);
+            }
+            return argSig;
+        }
+    }
+
+    /**
+     * @brief encapsulate a state declaration in a single token
+     */
+    public class TokenDeclState : Token {
+
+        public TokenName name;  // null for default state
+        public TokenStateBody body;
+
+        public TokenDeclState (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            if (name == null) {
+                sb.Append ("default");
+            } else {
+                sb.Append ("state ");
+                sb.Append (name);
+            }
+            body.DebString (sb);
+        }
+    }
+
+    /**
+     * @brief encapsulate the declaration of a field/function/method/property/variable.
+     */
+
+    public enum Triviality {  // function triviality: has no loops and doesn't call anything that has loops
+                              // such a function does not need all the CheckRun() and stack serialization stuff
+        unknown,   // function's Triviality unknown as of yet
+                   // - it does not have any loops or backward gotos
+                   // - nothing it calls is known to be complex
+        trivial,   // function known to be trivial
+                   // - it does not have any loops or backward gotos
+                   // - everything it calls is known to be trivial
+        complex,   // function known to be complex
+                   // - it has loops or backward gotos
+                   // - something it calls is known to be complex
+        analyzing  // triviality is being analyzed (used to detect recursive loops)
+    };
+
+    public class TokenDeclVar : TokenStmt {
+        public TokenName name;                 // vars: name; funcs: bare name, ie, no signature
+        public TokenRVal init;                 // vars: null if none; funcs: null
+        public bool constant;                  // vars: 'constant'; funcs: false
+        public uint sdtFlags;                  // SDT_<*> flags
+
+        public CompValu location;              // used by codegen to keep track of location
+        public FieldInfo vTableArray;
+        public int vTableIndex = -1;           // local vars: not used (-1)
+                                               // arg vars: index in the arg list
+                                               // global vars: which slot in gbl<Type>s[] array it is stored
+                                               // instance vars: which slot in inst<Types>s[] array it is stored
+                                               // static vars: which slot in gbl<Type>s[] array it is stored
+                                               // global funcs: not used (-1)
+                                               // virt funcs: which slot in vTable[] array it is stored
+                                               // instance func: not used (-1)
+        public TokenDeclVar getProp;           // if property, function that reads value
+        public TokenDeclVar setProp;           // if property, function that writes value
+
+        public TokenScript tokenScript;        // what script this function is part of
+        public TokenDeclSDType sdtClass;       // null: script global member
+                                               // else: member is part of this script-defined type
+
+        // function-only data:
+
+        public TokenType retType;              // vars: null; funcs: TokenTypeVoid if void
+        public TokenArgDecl argDecl;           // vars: null; funcs: argument list prototypes
+        public TokenStmtBlock body;            // vars: null; funcs: statements (null iff abstract)
+        public Dictionary<string, TokenStmtLabel> labels = new Dictionary<string, TokenStmtLabel> ();
+                                               // all labels defined in the function
+        public LinkedList<TokenDeclVar> localVars = new LinkedList<TokenDeclVar> ();
+                                               // all local variables declared by this function
+                                               // - doesn't include argument variables
+        public TokenIntfImpl implements;       // if script-defined type method, what interface method(s) this func implements
+        public TokenRValCall baseCtorCall;     // if script-defined type constructor, call to base constructor, if any
+        public Triviality triviality = Triviality.unknown;
+                                               // vars: unknown (not used for any thing); funcs: unknown/trivial/complex
+        public LinkedList<TokenRValCall> unknownTrivialityCalls = new LinkedList<TokenRValCall> ();
+                                               // reduction puts all calls here
+                                               // compilation sorts it all out
+
+        public ScriptObjWriter ilGen;          // codegen stores emitted code here
+
+        /**
+         * @brief Set up a variable declaration token.
+         * @param original = original source token that triggered definition
+         *                   (for error messages)
+         * @param func = null: global variable
+         *               else: local to the given function
+         */
+        public TokenDeclVar (Token original, TokenDeclVar func, TokenScript ts) : base (original)
+        {
+            if (func != null) {
+                func.localVars.AddLast (this);
+            }
+            tokenScript = ts;
+        }
+
+        /**
+         * @brief Get/Set overall type
+         *        For vars, this is the type of the location
+         *        For funcs, this is the delegate type
+         */
+        private TokenType _type;
+        public TokenType type {
+            get {
+                if (_type == null) {
+                    GetDelType ();
+                }
+                return _type;
+            }
+            set {
+                _type = value;
+            }
+        }
+
+        /**
+         * @brief Full name: <fulltype>.<name>(<argsig>)
+         *        (<argsig>) missing for fields/variables
+         *        <fulltype>. missing for top-level functions/variables
+         */
+        public string fullName {
+            get {
+                if (sdtClass == null) {
+                    if (retType == null) return name.val;
+                    return funcNameSig.val;
+                }
+                string ln = sdtClass.longName.val;
+                if (retType == null) return ln + "." + name.val;
+                return ln + "." + funcNameSig.val;
+            }
+        }
+
+        /**
+         * @brief See if reading or writing the variable is trivial.
+         *        Note that for functions, this is reading the function itself, 
+         *        as in 'someDelegate = SomeFunction;', not calling it as such.
+         *        The triviality of actually calling the function is IsFuncTrivial().
+         */
+        public bool IsVarTrivial (ScriptCodeGen scg)
+        {
+            // reading or writing a property involves a function call however
+            // so we need to check the triviality of the property functions
+            if ((getProp != null) && !getProp.IsFuncTrivial (scg)) return false;
+            if ((setProp != null) && !setProp.IsFuncTrivial (scg)) return false;
+
+            // otherwise for variables it is a trivial access
+            // and likewise for getting a delegate that points to a function
+            return true;
+        }
+
+        /***************************\
+         *  FUNCTION-only methods  *
+        \***************************/
+
+        private TokenName _funcNameSig;  // vars: null; funcs: function name including argumet signature, eg, "PrintStuff(list,string)"
+        public TokenName funcNameSig {
+            get {
+                if (_funcNameSig == null) {
+                    if (argDecl == null) return null;
+                    _funcNameSig = new TokenName (name, name.val + argDecl.GetArgSig ());
+                }
+                return _funcNameSig;
+            }
+        }
+
+        /**
+         * @brief The bare function name, ie, without any signature info
+         */
+        public string GetSimpleName ()
+        {
+            return name.val;
+        }
+
+        /**
+         * @brief The function name as it appears in the object code,
+         *        ie, script-defined type name if any,
+         *        bare function name and argument signature, 
+         *        eg, "MyClass.PrintStuff(string)"
+         */
+        public string GetObjCodeName ()
+        {
+                        string objCodeName = "";
+                        if (sdtClass != null) {
+                                objCodeName += sdtClass.longName.val + ".";
+                        }
+                        objCodeName += funcNameSig.val;
+            return objCodeName;
+        }
+
+        /**
+         * @brief Get delegate type.
+         *        This is the function's script-visible type,
+         *        It includes return type and all script-visible argument types.
+         * @returns null for vars; else delegate type for funcs
+         */
+        public TokenTypeSDTypeDelegate GetDelType ()
+        {
+            if (argDecl == null) return null;
+            if (_type == null) {
+                if (tokenScript == null) {
+                    // used during startup to define inline function delegate types
+                    _type = new TokenTypeSDTypeDelegate (retType, argDecl.types);
+                } else {
+                    // used for normal script processing
+                    _type = new TokenTypeSDTypeDelegate (this, retType, argDecl.types, tokenScript);
+                }
+            }
+            if (!(_type is TokenTypeSDTypeDelegate)) return null;
+            return (TokenTypeSDTypeDelegate)_type;
+        }
+
+        /**
+         * @brief See if the function's code itself is trivial or not.
+         *        If it contains any loops (calls to CheckRun()), it is not trivial.
+         *        If it calls anything that is not trivial, it is not trivial.
+         *        Otherwise it is trivial.
+         */
+        public bool IsFuncTrivial (ScriptCodeGen scg)
+        {
+            /*
+             * If not really a function, assume it's a delegate.
+             * And since we don't really know what functions it can point to, 
+             * assume it can point to a non-trivial one.
+             */
+            if (retType == null) return false;
+
+            /*
+             * All virtual functions are non-trivial because although a particular 
+             * one might be trivial, it might be overidden with a non-trivial one.
+             */
+            if ((sdtFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE | 
+                             ScriptReduce.SDT_VIRTUAL)) != 0) {
+                return false;
+            }
+
+            /*
+             * Check the triviality status of the function.
+             */
+            switch (triviality) {
+
+                /*
+                 * Don't yet know if it is trivial.
+                 * We know at this point it doesn't have any direct looping.
+                 * But if it calls something that has loops, it isn't trivial.
+                 * Otherwise it is trivial.
+                 */
+                case Triviality.unknown: {
+
+                    /*
+                     * Mark that we are analyzing this function now.  So if there are 
+                     * any recursive call loops, that will show that the function is 
+                     * non-trivial and the analysis will terminate at that point.
+                     */
+                    triviality = Triviality.analyzing;
+
+                    /*
+                     * Check out everything else this function calls.  If any say they 
+                     * aren't trivial, then we say this function isn't trivial.
+                     */
+                    foreach (TokenRValCall call in unknownTrivialityCalls) {
+                        if (!call.IsRValTrivial (scg, null)) {
+                            triviality = Triviality.complex;
+                            return false;
+                        }
+                    }
+
+                    /*
+                     * All functions called by this function are trivial, and this 
+                     * function's code doesn't have any loops, so we can mark this 
+                     * function as being trivial.
+                     */
+                    triviality = Triviality.trivial;
+                    return true;
+                }
+
+                /*
+                 * We already know that it is trivial.
+                 */
+                case Triviality.trivial: {
+                    return true;
+                }
+
+                /*
+                 * We either know it is complex or are trying to analyze it already.
+                 * If we are already analyzing it, it means it has a recursive loop
+                 * and we assume those are non-trivial.
+                 */
+                default: return false;
+            }
+        }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            DebStringSDTFlags (sb);
+
+            if (retType == null) {
+                sb.Append (constant ? "constant" : type.ToString ());
+                sb.Append (' ');
+                sb.Append (name.val);
+                if (init != null) {
+                    sb.Append (" = ");
+                    init.DebString (sb);
+                }
+                sb.Append (';');
+            } else {
+                if (!(retType is TokenTypeVoid)) {
+                    sb.Append (retType.ToString ());
+                    sb.Append (' ');
+                }
+                string namestr = name.val;
+                if (namestr == "$ctor") namestr = "constructor";
+                sb.Append (namestr);
+                sb.Append (" (");
+                for (int i = 0; i < argDecl.vars.Length; i ++) {
+                    if (i > 0) sb.Append (", ");
+                    sb.Append (argDecl.vars[i].type.ToString ());
+                    sb.Append (' ');
+                    sb.Append (argDecl.vars[i].name.val);
+                }
+                sb.Append (')');
+                if (body == null) sb.Append (';');
+                else {
+                    sb.Append (' ');
+                    body.DebString (sb);
+                }
+            }
+        }
+
+        // debugging
+        // - used to output contents of a $globalvarinit(), $instfieldinit() or $statisfieldinit() function
+        //   as a series of variable declaration statements with initial value assignments
+        //   so we get the initial value assignments done in same order as specified in script
+        public void DebStringInitFields (StringBuilder sb)
+        {
+            if ((retType == null) || !(retType is TokenTypeVoid)) throw new Exception ("bad return type " + retType.GetType ().Name);
+            if (argDecl.vars.Length != 0) throw new Exception ("has " + argDecl.vars.Length + " arg(s)");
+
+            for (Token stmt = body.statements; stmt != null; stmt = stmt.nextToken) {
+
+                /*
+                 * Body of the function should all be arithmetic statements (not eg for loops, if statements etc).
+                 */
+                TokenRVal rval = ((TokenStmtRVal)stmt).rVal;
+
+                /*
+                 * And the opcode should be a simple assignment operator.
+                 */
+                TokenRValOpBin rvob = (TokenRValOpBin)rval;
+                if (!(rvob.opcode is TokenKwAssign)) throw new Exception ("bad op type " + rvob.opcode.GetType ().Name);
+
+                /*
+                 * Get field or variable being assigned to.
+                 */
+                TokenDeclVar var = null;
+                TokenRVal left = rvob.rValLeft;
+                if (left is TokenLValIField) {
+                    TokenLValIField ifield = (TokenLValIField)left;
+                    TokenRValThis zhis = (TokenRValThis)ifield.baseRVal;
+                    TokenDeclSDTypeClass sdt = zhis.sdtClass;
+                    var = sdt.members.FindExact (ifield.fieldName.val, null);
+                }
+                if (left is TokenLValName) {
+                    TokenLValName global = (TokenLValName)left;
+                    var = global.stack.FindExact (global.name.val, null);
+                }
+                if (left is TokenLValSField) {
+                    TokenLValSField sfield = (TokenLValSField)left;
+                    TokenTypeSDTypeClass sdtc = (TokenTypeSDTypeClass)sfield.baseType;
+                    TokenDeclSDTypeClass decl = sdtc.decl;
+                    var = decl.members.FindExact (sfield.fieldName.val, null);
+                }
+                if (var == null) throw new Exception ("unknown var type " + left.GetType ().Name);
+
+                /*
+                 * Output flags, type name and bare variable name.
+                 * This should look like a declaration in the 'sb'
+                 * as it is not enclosed in a function.
+                 */
+                var.DebStringSDTFlags (sb);
+                var.type.DebString (sb);
+                sb.Append (' ');
+                sb.Append (var.name.val);
+
+                /*
+                 * Maybe it has a non-default initialization value.
+                 */
+                if ((var.init != null) && !(var.init is TokenRValInitDef)) {
+                    sb.Append (" = ");
+                    var.init.DebString (sb);
+                }
+
+                /*
+                 * End of declaration statement.
+                 */
+                sb.Append (';');
+            }
+        }
+
+        private void DebStringSDTFlags (StringBuilder sb)
+        {
+            if ((sdtFlags & ScriptReduce.SDT_PRIVATE)   != 0) sb.Append ("private ");
+            if ((sdtFlags & ScriptReduce.SDT_PROTECTED) != 0) sb.Append ("protected ");
+            if ((sdtFlags & ScriptReduce.SDT_PUBLIC)    != 0) sb.Append ("public ");
+            if ((sdtFlags & ScriptReduce.SDT_ABSTRACT)  != 0) sb.Append ("abstract ");
+            if ((sdtFlags & ScriptReduce.SDT_FINAL)     != 0) sb.Append ("final ");
+            if ((sdtFlags & ScriptReduce.SDT_NEW)       != 0) sb.Append ("new ");
+            if ((sdtFlags & ScriptReduce.SDT_OVERRIDE)  != 0) sb.Append ("override ");
+            if ((sdtFlags & ScriptReduce.SDT_STATIC)    != 0) sb.Append ("static ");
+            if ((sdtFlags & ScriptReduce.SDT_VIRTUAL)   != 0) sb.Append ("virtual ");
+        }
+    }
+
+    /**
+     * @brief Indicates an interface type.method that is implemented by the function
+     */
+    public class TokenIntfImpl : Token {
+        public TokenTypeSDTypeInterface intfType;
+        public TokenName methName;  // simple name, no arg signature
+
+        public TokenIntfImpl (TokenTypeSDTypeInterface intfType, TokenName methName) : base (intfType)
+        {
+            this.intfType = intfType;
+            this.methName = methName;
+        }
+    }
+
+    /**
+     * @brief any expression that can go on left side of an "="
+     */
+    public abstract class TokenLVal : TokenRVal {
+        public TokenLVal (Token original) : base (original) { }
+        public abstract override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig);
+        public abstract override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig);
+    }
+
+    /**
+     * @brief an element of an array is an L-value
+     */
+    public class TokenLValArEle : TokenLVal {
+        public TokenRVal baseRVal;
+        public TokenRVal subRVal;
+
+        public TokenLValArEle (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            TokenType baseType = baseRVal.GetRValType (scg, null);
+
+            /*
+             * Maybe referencing element of a fixed-dimension array.
+             */
+            if ((baseType is TokenTypeSDTypeClass) && (((TokenTypeSDTypeClass)baseType).decl.arrayOfType != null)) {
+                return ((TokenTypeSDTypeClass)baseType).decl.arrayOfType;
+            }
+
+            /*
+             * Maybe referencing $idxprop property of script-defined class or interface.
+             */
+            if (baseType is TokenTypeSDTypeClass) {
+                TokenDeclSDTypeClass sdtDecl = ((TokenTypeSDTypeClass)baseType).decl;
+                TokenDeclVar idxProp = scg.FindSingleMember (sdtDecl.members, new TokenName (this, "$idxprop"), null);
+                if (idxProp != null) return idxProp.type;
+            }
+            if (baseType is TokenTypeSDTypeInterface) {
+                TokenDeclSDTypeInterface sdtDecl = ((TokenTypeSDTypeInterface)baseType).decl;
+                TokenDeclVar idxProp = sdtDecl.FindIFaceMember (scg, new TokenName (this, "$idxprop"), null, out sdtDecl);
+                if (idxProp != null) return idxProp.type;
+            }
+
+            /*
+             * Maybe referencing single character of a string.
+             */
+            if ((baseType is TokenTypeKey) || (baseType is TokenTypeStr)) {
+                return new TokenTypeChar (this);
+            }
+
+            /*
+             * Assume XMR_Array element or extracting element from list.
+             */
+            if ((baseType is TokenTypeArray) || (baseType is TokenTypeList)) {
+                return new TokenTypeObject (this);
+            }
+
+            scg.ErrorMsg (this, "unknown array reference");
+            return new TokenTypeVoid (this);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return baseRVal.IsRValTrivial (scg, null) && subRVal.IsRValTrivial (scg, null);
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            baseRVal.DebString (sb);
+            sb.Append ('[');
+            subRVal.DebString (sb);
+            sb.Append (']');
+        }
+    }
+
+    /**
+     * @brief 'base.' being used to reference a field/method of the extended class.
+     */
+    public class TokenLValBaseField : TokenLVal {
+        public TokenName fieldName;
+        private TokenDeclSDTypeClass thisClass;
+
+        public TokenLValBaseField (Token original, TokenName fieldName, TokenDeclSDTypeClass thisClass) : base (original)
+        {
+            this.fieldName = fieldName;
+            this.thisClass = thisClass;
+        }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig);
+            if (var != null) return var.type;
+            scg.ErrorMsg (fieldName, "unknown member of " + thisClass.extends.ToString ());
+            return new TokenTypeVoid (fieldName);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig);
+            return (var != null) && var.IsVarTrivial (scg);
+        }
+
+        public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig);
+            return (var != null) && var.IsFuncTrivial (scg);
+        }
+    }
+
+    /**
+     * @brief a field within an struct is an L-value
+     */
+    public class TokenLValIField : TokenLVal {
+        public TokenRVal baseRVal;
+        public TokenName fieldName;
+
+        public TokenLValIField (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            TokenType baseType = baseRVal.GetRValType (scg, null);
+            if (baseType is TokenTypeSDTypeClass) {
+                TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
+                if (var != null) return var.type;
+            }
+            if (baseType is TokenTypeSDTypeInterface) {
+                TokenDeclSDTypeInterface baseIntfDecl = ((TokenTypeSDTypeInterface)baseType).decl;
+                TokenDeclVar var = baseIntfDecl.FindIFaceMember (scg, fieldName, argsig, out baseIntfDecl);
+                if (var != null) return var.type;
+            }
+            if (baseType is TokenTypeArray) {
+                return XMR_Array.GetRValType (fieldName);
+            }
+            if ((baseType is TokenTypeRot) || (baseType is TokenTypeVec)) {
+                return new TokenTypeFloat (fieldName);
+            }
+            scg.ErrorMsg (fieldName, "unknown member of " + baseType.ToString ());
+            return new TokenTypeVoid (fieldName);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            /*
+             * If getting pointer to instance isn't trivial, then accessing the member isn't trivial either.
+             */
+            if (!baseRVal.IsRValTrivial (scg, null)) return false;
+
+            /*
+             * Accessing a member of a class depends on the member.
+             * In the case of a method, this is accessing it as a delegate, not calling it, and 
+             * argsig simply serves as selecting which of possibly overloaded methods to select.
+             * The case of accessing a property, however, depends on the property implementation, 
+             * as there could be looping inside the property code.
+             */
+            TokenType baseType = baseRVal.GetRValType (scg, null);
+            if (baseType is TokenTypeSDTypeClass) {
+                TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
+                return (var != null) && var.IsVarTrivial (scg);
+            }
+
+            /*
+             * Accessing the members of anything else (arrays, rotations, vectors) is always trivial.
+             */
+            return true;
+        }
+
+        /**
+         * @brief Check to see if the case of calling an instance method of some object is trivial.
+         * @param scg = script making the call
+         * @param argsig = argument types of the call (used to select among overloads)
+         * @returns true iff we can tell at compile time that the call will always call a trivial method
+         */
+        public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            /*
+             * If getting pointer to instance isn't trivial, then calling the method isn't trivial either.
+             */
+            if (!baseRVal.IsRValTrivial (scg, null)) return false;
+
+            /*
+             * Calling a method of a class depends on the method.
+             */
+            TokenType baseType = baseRVal.GetRValType (scg, null);
+            if (baseType is TokenTypeSDTypeClass) {
+                TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
+                return (var != null) && var.IsFuncTrivial (scg);
+            }
+
+            /*
+             * Calling via a pointer to an interface instance is never trivial.
+             * (It is really a pointer to an array of delegates).
+             * We can't tell for this call site whether the actual method being called is trivial or not,
+             * so we have to assume it isn't.
+             * ??? We could theoretically check to see if *all* implementations of this method of 
+             *     this interface are trivial, then we could conclude that this call is trivial.
+             */
+            if (baseType is TokenTypeSDTypeInterface) return false;
+
+            /*
+             * Calling a method of anything else (arrays, rotations, vectors) is always trivial.
+             * Even methods of delegates, such as ".GetArgTypes()" that we may someday do is trivial.
+             */
+            return true;
+        }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            baseRVal.DebString (sb);
+            sb.Append ('.');
+            sb.Append (fieldName.val);
+        }
+    }
+
+    /**
+     * @brief a name is being used as an L-value
+     */
+    public class TokenLValName : TokenLVal {
+        public TokenName name;
+        public VarDict stack;
+
+        public TokenLValName (TokenName name, VarDict stack) : base (name)
+        {
+            /*
+             * Save name of variable/method/function/field.
+             */
+            this.name  = name;
+
+            /*
+             * Save where in the stack it can be looked up.
+             * If the current stack is for locals, do not allow forward references.
+             *     this allows idiocy like:
+             *         list buttons = [ 1, 2, 3 ];
+             *         x () {
+             *             list buttons = llList2List (buttons, 0, 1);
+             *             llOwnerSay (llList2CSV (buttons));
+             *         }
+             * If it is not locals, allow forward references.
+             *     this allows function X() to call Y() and Y() to call X().
+             */
+            this.stack = stack.FreezeLocals ();
+        }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            TokenDeclVar var = scg.FindNamedVar (this, argsig);
+            if (var != null) return var.type;
+            scg.ErrorMsg (name, "undefined name " + name.val + ScriptCodeGen.ArgSigString (argsig));
+            return new TokenTypeVoid (name);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            TokenDeclVar var = scg.FindNamedVar (this, argsig);
+            return (var != null) && var.IsVarTrivial (scg);
+        }
+
+        /**
+         * @brief Check to see if the case of calling a global method is trivial.
+         * @param scg = script making the call
+         * @param argsig = argument types of the call (used to select among overloads)
+         * @returns true iff we can tell at compile time that the call will always call a trivial method
+         */
+        public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            TokenDeclVar var = scg.FindNamedVar (this, argsig);
+            return (var != null) && var.IsFuncTrivial (scg);
+        }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append (name.val);
+        }
+    }
+
+    /**
+     * @brief a static field within a struct is an L-value
+     */
+    public class TokenLValSField : TokenLVal {
+        public TokenType baseType;
+        public TokenName fieldName;
+
+        public TokenLValSField (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            if (baseType is TokenTypeSDTypeClass) {
+                TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
+                if (var != null) return var.type;
+            }
+            scg.ErrorMsg (fieldName, "unknown member of " + baseType.ToString ());
+            return new TokenTypeVoid (fieldName);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            /*
+             * Accessing a member of a class depends on the member.
+             * In the case of a method, this is accessing it as a delegate, not calling it, and 
+             * argsig simply serves as selecting which of possibly overloaded methods to select.
+             * The case of accessing a property, however, depends on the property implementation, 
+             * as there could be looping inside the property code.
+             */
+            if (baseType is TokenTypeSDTypeClass) {
+                TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
+                return (var != null) && var.IsVarTrivial (scg);
+            }
+
+            /*
+             * Accessing the fields/methods/properties of anything else (arrays, rotations, vectors) is always trivial.
+             */
+            return true;
+        }
+
+        /**
+         * @brief Check to see if the case of calling a class' static method is trivial.
+         * @param scg = script making the call
+         * @param argsig = argument types of the call (used to select among overloads)
+         * @returns true iff we can tell at compile time that the call will always call a trivial method
+         */
+        public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            /*
+             * Calling a static method of a class depends on the method.
+             */
+            if (baseType is TokenTypeSDTypeClass) {
+                TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
+                return (var != null) && var.IsFuncTrivial (scg);
+            }
+
+            /*
+             * Calling a static method of anything else (arrays, rotations, vectors) is always trivial.
+             */
+            return true;
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            if (fieldName.val == "$new") {
+                sb.Append ("new ");
+                baseType.DebString (sb);
+            } else {
+                baseType.DebString (sb);
+                sb.Append ('.');
+                fieldName.DebString (sb);
+            }
+        }
+    }
+
+    /**
+     * @brief any expression that can go on right side of "="
+     */
+    public delegate TokenRVal TCCLookup (TokenRVal rVal, ref bool didOne);
+    public abstract class TokenRVal : Token {
+        public TokenRVal (Token original) : base (original) { }
+
+        /**
+         * @brief Tell us the type of the expression.
+         */
+        public abstract TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig);
+
+        /**
+         * @brief Tell us if reading and writing the value is trivial.
+         *
+         * @param scg = script code generator of script making the access
+         * @param argsig = argument types of the call (used to select among overloads)
+         * @returns true: we can tell at compile time that reading/writing this location
+         *                will always be trivial (no looping or CheckRun() calls possible).
+         *         false: otherwise
+         */
+        public abstract bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig);
+
+        /**
+         * @brief Tell us if calling the method is trivial.
+         *
+         *        This is the default implementation that returns false.
+         *        It is only used if the location is holding a delegate
+         *        and the method that the delegate is pointing to is being
+         *        called.  Since we can't tell if the actual runtime method
+         *        is trivial or not, we assume it isn't.
+         *
+         *        For the more usual ways of calling functions, see the
+         *        various overrides of IsCallTrivial().
+         *
+         * @param scg = script code generator of script making the call
+         * @param argsig = argument types of the call (used to select among overloads)
+         * @returns true: we can tell at compile time that this call will always
+         *                be to a trivial function/method (no looping or CheckRun() 
+         *                calls possible).
+         *         false: otherwise
+         */
+        public virtual bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return false;
+        }
+
+        /**
+         * @brief If the result of the expression is a constant,
+         *        create a TokenRValConst equivalent, set didOne, and return that.
+         *        Otherwise, just return the original without changing didOne.
+         */
+        public virtual TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
+        {
+            return lookup (this, ref didOne);
+        }
+    }
+
+    /**
+     * @brief a postfix operator and corresponding L-value
+     */
+    public class TokenRValAsnPost : TokenRVal {
+        public TokenLVal lVal;
+        public Token postfix;
+
+        public TokenRValAsnPost (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return lVal.GetRValType (scg, argsig);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return lVal.IsRValTrivial (scg, null);
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            lVal.DebString (sb);
+            sb.Append (' ');
+            postfix.DebString (sb);
+        }
+    }
+
+    /**
+     * @brief a prefix operator and corresponding L-value
+     */
+    public class TokenRValAsnPre : TokenRVal {
+        public Token prefix;
+        public TokenLVal lVal;
+
+        public TokenRValAsnPre (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return lVal.GetRValType (scg, argsig);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return lVal.IsRValTrivial (scg, null);
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            prefix.DebString (sb);
+            sb.Append (' ');
+            lVal.DebString (sb);
+        }
+    }
+
+    /**
+     * @brief calling a function or method, ie, may have side-effects
+     */
+    public class TokenRValCall : TokenRVal {
+
+        public TokenRVal meth;  // points to the function to be called
+                                // - might be a reference to a global function (TokenLValName)
+                                // - or an instance method of a class (TokenLValIField)
+                                // - or a static method of a class (TokenLValSField)
+                                // - or a delegate stored in a variable (assumption for anything else)
+        public TokenRVal args;  // null-terminated TokenRVal list
+        public int nArgs;       // number of elements in args
+
+        public TokenRValCall (Token original) : base (original) { }
+
+        private TokenType[] myArgSig;
+
+        /**
+         * @brief The type of a call is the type of the return value.
+         */
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            /*
+             * Build type signature so we select correct overloaded function.
+             */
+            if (myArgSig == null) {
+                myArgSig = new TokenType[nArgs];
+                int i = 0;
+                for (Token t = args; t != null; t = t.nextToken) {
+                    myArgSig[i++] = ((TokenRVal)t).GetRValType (scg, null);
+                }
+            }
+
+            /*
+             * Get the type of the method itself.  This should get us a delegate type.
+             */
+            TokenType delType = meth.GetRValType (scg, myArgSig);
+            if (!(delType is TokenTypeSDTypeDelegate)) {
+                scg.ErrorMsg (meth, "must be function or method");
+                return new TokenTypeVoid (meth);
+            }
+
+            /*
+             * Get the return type from the delegate type.
+             */
+            return ((TokenTypeSDTypeDelegate)delType).decl.GetRetType ();
+        }
+
+        /**
+         * @brief See if the call to the function/method is trivial.
+         *        It is trivial if all the argument computations are trivial and
+         *        the function is not being called via virtual table or delegate
+         *        and the function body is trivial.
+         */
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            /*
+             * Build type signature so we select correct overloaded function.
+             */
+            if (myArgSig == null) {
+                myArgSig = new TokenType[nArgs];
+                int i = 0;
+                for (Token t = args; t != null; t = t.nextToken) {
+                    myArgSig[i++] = ((TokenRVal)t).GetRValType (scg, null);
+                }
+            }
+
+            /*
+             * Make sure all arguments can be computed trivially.
+             */
+            for (Token t = args; t != null; t = t.nextToken) {
+                if (!((TokenRVal)t).IsRValTrivial (scg, null)) return false;
+            }
+
+            /*
+             * See if the function call itself and the function body are trivial.
+             */
+            return meth.IsCallTrivial (scg, myArgSig);
+        }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            meth.DebString (sb);
+            sb.Append (" (");
+            bool first = true;
+            for (Token t = args; t != null; t = t.nextToken) {
+                if (!first) sb.Append (", ");
+                t.DebString (sb);
+                first = false;
+            }
+            sb.Append (")");
+        }
+    }
+
+    /**
+     * @brief encapsulates a typecast, ie, (type)
+     */
+    public class TokenRValCast : TokenRVal {
+        public TokenType castTo;
+        public TokenRVal rVal;
+
+        public TokenRValCast (TokenType type, TokenRVal value) : base (type)
+        {
+            castTo = type;
+            rVal   = value;
+        }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return castTo;
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            argsig = null;
+            if (castTo is TokenTypeSDTypeDelegate) {
+                argsig = ((TokenTypeSDTypeDelegate)castTo).decl.GetArgTypes ();
+            }
+            return rVal.IsRValTrivial (scg, argsig);
+        }
+
+        /**
+         * @brief If operand is constant, maybe we can say the whole thing is a constant.
+         */
+        public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
+        {
+            rVal = rVal.TryComputeConstant (lookup, ref didOne);
+            if (rVal is TokenRValConst) {
+                try {
+                    object val = ((TokenRValConst)rVal).val;
+                    object nval = null;
+                    if (castTo is TokenTypeChar) {
+                        if (val is char)         return rVal;
+                        if (val is int)          nval = (char)(int)val;
+                    }
+                    if (castTo is TokenTypeFloat) {
+                        if (val is double)  return rVal;
+                        if (val is int)          nval = (double)(int)val;
+                        if (val is string)       nval = new LSL_Float ((string)val).value;
+                    }
+                    if (castTo is TokenTypeInt) {
+                        if (val is int)          return rVal;
+                        if (val is char)         nval = (int)(char)val;
+                        if (val is double)  nval = (int)(double)val;
+                        if (val is string)       nval = new LSL_Integer ((string)val).value;
+                    }
+                    if (castTo is TokenTypeRot) {
+                        if (val is LSL_Rotation) return rVal;
+                        if (val is string)       nval = new LSL_Rotation ((string)val);
+                    }
+                    if ((castTo is TokenTypeKey) || (castTo is TokenTypeStr)) {
+                        if (val is string)       nval = val;  // in case of key/string conversion
+                        if (val is char)         nval = TypeCast.CharToString ((char)val);
+                        if (val is double)  nval = TypeCast.FloatToString ((double)val);
+                        if (val is int)          nval = TypeCast.IntegerToString ((int)val);
+                        if (val is LSL_Rotation) nval = TypeCast.RotationToString ((LSL_Rotation)val);
+                        if (val is LSL_Vector)   nval = TypeCast.VectorToString ((LSL_Vector)val);
+                    }
+                    if (castTo is TokenTypeVec) {
+                        if (val is LSL_Vector)   return rVal;
+                        if (val is string)       nval = new LSL_Vector ((string)val);
+                    }
+                    if (nval != null) {
+                        TokenRVal rValConst = new TokenRValConst (castTo, nval);
+                        didOne = true;
+                        return rValConst;
+                    }
+                } catch {
+                }
+            }
+            return this;
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ('(');
+            castTo.DebString (sb);
+            sb.Append (')');
+            rVal.DebString (sb);
+        }
+    }
+
+    /**
+     * @brief Encapsulate a conditional expression:
+     *        <condExpr> ? <trueExpr> : <falseExpr>
+     */
+    public class TokenRValCondExpr : TokenRVal {
+        public TokenRVal condExpr;
+        public TokenRVal trueExpr;
+        public TokenRVal falseExpr;
+
+        public TokenRValCondExpr (Token original) : base (original)
+        { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            TokenType trueType = trueExpr.GetRValType (scg, argsig);
+            TokenType falseType = falseExpr.GetRValType (scg, argsig);
+            if (trueType.ToString () != falseType.ToString ()) {
+                scg.ErrorMsg (condExpr, "true & false expr types don't match");
+            }
+            return trueType;
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return condExpr.IsRValTrivial (scg, null) &&
+                   trueExpr.IsRValTrivial (scg, argsig) &&
+                  falseExpr.IsRValTrivial (scg, argsig);
+        }
+
+        /**
+         * @brief If condition is constant, then the whole expression is constant
+         *      iff the corresponding trueExpr or falseExpr is constant.
+         */
+        public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
+        {
+            TokenRVal rValCond = condExpr.TryComputeConstant  (lookup, ref didOne);
+            if (rValCond is TokenRValConst) {
+                didOne = true;
+                bool isTrue = ((TokenRValConst)rValCond).IsConstBoolTrue ();
+                return (isTrue ? trueExpr : falseExpr).TryComputeConstant  (lookup, ref didOne);
+            }
+            return this;
+        }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            condExpr.DebString (sb);
+            sb.Append (" ? ");
+            trueExpr.DebString (sb);
+            sb.Append (" : ");
+            falseExpr.DebString (sb);
+        }
+    }
+
+    /**
+     * @brief all constants supposed to end up here
+     */
+    public enum TokenRValConstType : byte { CHAR = 0, FLOAT = 1, INT = 2, KEY = 3, STRING = 4 };
+    public class TokenRValConst : TokenRVal {
+        public object val;  // always a system type (char, int, double, string), never LSL-wrapped
+        public TokenRValConstType type;
+        public TokenType tokType;
+
+        public TokenRValConst (Token original, object value) : base (original)
+        {
+            val = value;
+
+            TokenType tt = null;
+            if (val is char) {
+                type = TokenRValConstType.CHAR;
+                tt = new TokenTypeChar (this);
+            } else if (val is int) {
+                type = TokenRValConstType.INT;
+                tt = new TokenTypeInt (this);
+            } else if (val is double) {
+                type = TokenRValConstType.FLOAT;
+                tt = new TokenTypeFloat (this);
+            } else if (val is string) {
+                type = TokenRValConstType.STRING;
+                tt = new TokenTypeStr (this);
+            } else {
+                throw new Exception ("invalid constant type " + val.GetType ());
+            }
+
+            tokType = (original is TokenType) ? (TokenType)original : tt;
+            if (tokType is TokenTypeKey) {
+                type = TokenRValConstType.KEY;
+            }
+        }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return tokType;
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return true;
+        }
+
+        public CompValu GetCompValu ()
+        {
+            switch (type) {
+                case TokenRValConstType.CHAR:   { return new CompValuChar    (tokType, (char)val);        }
+                case TokenRValConstType.FLOAT:  { return new CompValuFloat   (tokType, (double)val); }
+                case TokenRValConstType.INT:    { return new CompValuInteger (tokType, (int)val);         }
+                case TokenRValConstType.KEY:
+                case TokenRValConstType.STRING: { return new CompValuString  (tokType, (string)val);      }
+                default: throw new Exception ("unknown type");
+            }
+        }
+
+        public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
+        {
+            // gotta end somewhere
+            return this;
+        }
+
+        public bool IsConstBoolTrue ()
+        {
+            switch (type) {
+                case TokenRValConstType.CHAR:   { return (char)val        != 0;  }
+                case TokenRValConstType.FLOAT:  { return (double)val != 0;  }
+                case TokenRValConstType.INT:    { return (int)val         != 0;  }
+                case TokenRValConstType.KEY:    { return (string)val != "" && (string)val != ScriptBaseClass.NULL_KEY; }
+                case TokenRValConstType.STRING: { return (string)val      != ""; }
+                default: throw new Exception ("unknown type");
+            }
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            if (val is char) {
+                sb.Append ('\'');
+                EscapeQuotes (sb, new string (new char [] { (char)val }));
+                sb.Append ('\'');
+            } else if (val is int) {
+                sb.Append ((int)val);
+            } else if (val is double) {
+                string str = ((double)val).ToString ();
+                sb.Append (str);
+                if ((str.IndexOf ('.') < 0) &&
+                    (str.IndexOf ('E') < 0) &&
+                    (str.IndexOf ('e') < 0)) {
+                    sb.Append (".0");
+                }
+            } else if (val is string) {
+                sb.Append ('"');
+                EscapeQuotes (sb, (string)val);
+                sb.Append ('"');
+            } else {
+                throw new Exception ("invalid constant type " + val.GetType ());
+            }
+        }
+        private static void EscapeQuotes (StringBuilder sb, string s)
+        {
+            foreach (char c in s) {
+                switch (c) {
+                    case '\n': {
+                        sb.Append ("\\n");
+                        break;
+                    }
+                    case '\t': {
+                        sb.Append ("\\t");
+                        break;
+                    }
+                    case '\\': {
+                        sb.Append ("\\\\");
+                        break;
+                    }
+                    case '\'': {
+                        sb.Append ("\\'");
+                        break;
+                    }
+                    case '\"': {
+                        sb.Append ("\\\"");
+                        break;
+                    }
+                    default: {
+                        sb.Append (c);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @brief Default initialization value for the corresponding variable.
+     */
+    public class TokenRValInitDef : TokenRVal {
+        public TokenType type;
+
+        public static TokenRValInitDef Construct (TokenDeclVar tokenDeclVar)
+        {
+            TokenRValInitDef zhis = new TokenRValInitDef (tokenDeclVar);
+            zhis.type = tokenDeclVar.type;
+            return zhis;
+        }
+        private TokenRValInitDef (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return type;
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            // it's always just a constant so it's always very trivial
+            return true;
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("<default ");
+            sb.Append (type.ToString ());
+            sb.Append ('>');
+        }
+    }
+
+    /**
+     * @brief encapsulation of <rval> is <typeexp>
+     */
+    public class TokenRValIsType : TokenRVal {
+        public TokenRVal    rValExp;
+        public TokenTypeExp typeExp;
+
+        public TokenRValIsType (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return new TokenTypeBool (rValExp);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return rValExp.IsRValTrivial (scg, argsig);
+        }
+    }
+
+    /**
+     * @brief an R-value enclosed in brackets is an LSLList
+     */
+    public class TokenRValList : TokenRVal {
+
+        public TokenRVal rVal;  // null-terminated list of TokenRVal objects
+        public int nItems;
+
+        public TokenRValList (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return new TokenTypeList (rVal);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            for (Token t = rVal; t != null; t = t.nextToken) {
+                if (!((TokenRVal)t).IsRValTrivial (scg, null)) return false;
+            }
+            return true;
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            bool first = true;
+            sb.Append ('[');
+            for (Token t = rVal; t != null; t = t.nextToken) {
+                if (!first) sb.Append (',');
+                sb.Append (' ');
+                t.DebString (sb);
+                first = false;
+            }
+            sb.Append (" ]");
+        }
+    }
+
+    /**
+     * @brief encapsulates '$new' arraytype '{' ... '}'
+     */
+    public class TokenRValNewArIni : TokenRVal {
+        public TokenType arrayType;
+        public TokenList valueList;  // TokenList : a sub-list
+                                     // TokenKwComma : a default value
+                                     // TokenRVal    : init expression
+
+        public TokenRValNewArIni (Token original) : base (original)
+        {
+            valueList = new TokenList (original);
+        }
+
+        // type of the expression = the array type allocated by $new()
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return arrayType;
+        }
+
+        // The expression is trivial if all the initializers are trivial.
+        // An array's constructor is always trivial (no CheckRun() calls).
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return ListIsTrivial (scg, valueList);
+        }
+        private bool ListIsTrivial (ScriptCodeGen scg, TokenList valList)
+        {
+            foreach (Token val in valList.tl) {
+                if (val is TokenRVal) {
+                    if (!((TokenRVal)val).IsRValTrivial (scg, null)) return false;
+                }
+                if (val is TokenList) {
+                    if (!ListIsTrivial (scg, (TokenList)val)) return false;
+                }
+            }
+            return true;
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("new ");
+            arrayType.DebString (sb);
+            sb.Append (' ');
+            valueList.DebString (sb);
+        }
+    }
+    public class TokenList : Token {
+        public List<Token> tl = new List<Token> ();
+        public TokenList (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ('{');
+            bool first = true;
+            foreach (Token t in tl) {
+                if (!first) sb.Append (", ");
+                t.DebString (sb);
+                first = false;
+            }
+            sb.Append ('}');
+        }
+    }
+
+    /**
+     * @brief a binary operator and its two operands
+     */
+    public class TokenRValOpBin : TokenRVal {
+        public TokenRVal rValLeft;
+        public TokenKw opcode;
+        public TokenRVal rValRight;
+
+        public TokenRValOpBin (TokenRVal left, TokenKw op, TokenRVal right) : base (op)
+        {
+            rValLeft  = left;
+            opcode    = op;
+            rValRight = right;
+        }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            /*
+             * Comparisons and the like always return bool.
+             */
+            string opstr = opcode.ToString ();
+            if ((opstr == "==") || (opstr == "!=") || (opstr == ">=") || (opstr == ">") || 
+                (opstr == "&&") || (opstr == "||") || (opstr == "<=") || (opstr == "<") || 
+                (opstr == "&&&") || (opstr == "|||")) {
+                return new TokenTypeBool (opcode);
+            }
+
+            /*
+             * Comma is always type of right-hand operand.
+             */
+            if (opstr == ",") return rValRight.GetRValType (scg, argsig);
+
+            /*
+             * Assignments are always the type of the left-hand operand, 
+             * including stuff like "+=".
+             */
+            if (opstr.EndsWith ("=")) {
+                return rValLeft.GetRValType (scg, argsig);
+            }
+
+            /*
+             * string+something or something+string is always string.
+             * except list+something or something+list is always a list.
+             */
+            string lType = rValLeft.GetRValType (scg, argsig).ToString ();
+            string rType = rValRight.GetRValType (scg, argsig).ToString ();
+            if ((opstr == "+") && ((lType == "list") || (rType == "list"))) {
+                return new TokenTypeList (opcode);
+            }
+            if ((opstr == "+") && ((lType == "key") || (lType == "string") ||
+                                   (rType == "key") || (rType == "string"))) {
+                return new TokenTypeStr (opcode);
+            }
+
+            /*
+             * Everything else depends on both operands.
+             */
+            string key = lType + opstr + rType;
+            BinOpStr binOpStr;
+            if (BinOpStr.defined.TryGetValue (key, out binOpStr)) {
+                return TokenType.FromSysType (opcode, binOpStr.outtype);
+            }
+
+            scg.ErrorMsg (opcode, "undefined operation " + key);
+            return new TokenTypeVoid (opcode);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return rValLeft.IsRValTrivial (scg, null) && rValRight.IsRValTrivial (scg, null);
+        }
+
+        /**
+         * @brief If both operands are constants, maybe we can say the whole thing is a constant.
+         */
+        public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
+        {
+            rValLeft  = rValLeft.TryComputeConstant  (lookup, ref didOne);
+            rValRight = rValRight.TryComputeConstant (lookup, ref didOne);
+            if ((rValLeft is TokenRValConst) && (rValRight is TokenRValConst)) {
+                try {
+                    object val = opcode.binOpConst (((TokenRValConst)rValLeft).val, 
+                                                    ((TokenRValConst)rValRight).val);
+                    TokenRVal rValConst = new TokenRValConst (opcode, val);
+                    didOne = true;
+                    return rValConst;
+                } catch {
+                }
+            }
+            return this;
+        }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            rValLeft.DebString (sb);
+            sb.Append (' ');
+            sb.Append (opcode.ToString ());
+            sb.Append (' ');
+            rValRight.DebString (sb);
+        }
+    }
+
+    /**
+     * @brief an unary operator and its one operand
+     */
+    public class TokenRValOpUn : TokenRVal {
+        public TokenKw opcode;
+        public TokenRVal rVal;
+
+        public TokenRValOpUn (TokenKw op, TokenRVal right) : base (op)
+        {
+            opcode = op;
+            rVal   = right;
+        }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            if (opcode is TokenKwExclam) return new TokenTypeInt (opcode);
+            return rVal.GetRValType (scg, null);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return rVal.IsRValTrivial (scg, null);
+        }
+
+        /**
+         * @brief If operand is constant, maybe we can say the whole thing is a constant.
+         */
+        public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
+        {
+            rVal = rVal.TryComputeConstant (lookup, ref didOne);
+            if (rVal is TokenRValConst) {
+                try {
+                    object val = opcode.unOpConst (((TokenRValConst)rVal).val);
+                    TokenRVal rValConst = new TokenRValConst (opcode, val);
+                    didOne = true;
+                    return rValConst;
+                } catch {
+                }
+            }
+            return this;
+        }
+
+        /**
+         * @brief Serialization/Deserialization.
+         */
+        public TokenRValOpUn (Token original) : base (original) { }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append (opcode.ToString ());
+            rVal.DebString (sb);
+        }
+    }
+
+    /**
+     * @brief an R-value enclosed in parentheses
+     */
+    public class TokenRValParen : TokenRVal {
+
+        public TokenRVal rVal;
+
+        public TokenRValParen (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            // pass argsig through in this simple case, ie, let 
+            // them do something like (llOwnerSay)("blabla...");
+            return rVal.GetRValType (scg, argsig);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            // pass argsig through in this simple case, ie, let 
+            // them do something like (llOwnerSay)("blabla...");
+            return rVal.IsRValTrivial (scg, argsig);
+        }
+
+        /**
+         * @brief If operand is constant, we can say the whole thing is a constant.
+         */
+        public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
+        {
+            rVal = rVal.TryComputeConstant (lookup, ref didOne);
+            if (rVal is TokenRValConst) {
+                didOne = true;
+                return rVal;
+            }
+            return this;
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ('(');
+            rVal.DebString (sb);
+            sb.Append (')');
+        }
+    }
+
+    public class TokenRValRot : TokenRVal {
+
+        public TokenRVal xRVal;
+        public TokenRVal yRVal;
+        public TokenRVal zRVal;
+        public TokenRVal wRVal;
+
+        public TokenRValRot (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return new TokenTypeRot (xRVal);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return xRVal.IsRValTrivial (scg, null) && 
+                   yRVal.IsRValTrivial (scg, null) && 
+                   zRVal.IsRValTrivial (scg, null) && 
+                   wRVal.IsRValTrivial (scg, null);
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ('<');
+            xRVal.DebString (sb);
+            sb.Append (',');
+            yRVal.DebString (sb);
+            sb.Append (',');
+            zRVal.DebString (sb);
+            sb.Append (',');
+            wRVal.DebString (sb);
+            sb.Append ('>');
+        }
+    }
+
+    /**
+     * @brief 'this' is being used as an rval inside an instance method.
+     */
+    public class TokenRValThis : TokenRVal {
+        public Token original;
+        public TokenDeclSDTypeClass sdtClass;
+
+        public TokenRValThis (Token original, TokenDeclSDTypeClass sdtClass) : base (original)
+        {
+            this.original = original;
+            this.sdtClass = sdtClass;
+        }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return sdtClass.MakeRefToken (original);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return true;  // ldarg.0/starg.0 can't possibly loop
+        }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("this");
+        }
+    }
+
+    /**
+     * @brief the 'undef' keyword is being used as a value in an expression.
+     *        It is the null object pointer and has type TokenTypeUndef.
+     */
+    public class TokenRValUndef : TokenRVal {
+        Token original;
+
+        public TokenRValUndef (Token original) : base (original)
+        {
+            this.original = original;
+        }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return new TokenTypeUndef (original);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return true;
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("undef");
+        }
+    }
+
+    /**
+     * @brief put 3 RVals together as a Vector value.
+     */
+    public class TokenRValVec : TokenRVal {
+
+        public TokenRVal xRVal;
+        public TokenRVal yRVal;
+        public TokenRVal zRVal;
+
+        public TokenRValVec (Token original) : base (original) { }
+
+        public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return new TokenTypeVec (xRVal);
+        }
+
+        public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
+        {
+            return xRVal.IsRValTrivial (scg, null) && 
+                   yRVal.IsRValTrivial (scg, null) && 
+                   zRVal.IsRValTrivial (scg, null);
+        }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ('<');
+            xRVal.DebString (sb);
+            sb.Append (',');
+            yRVal.DebString (sb);
+            sb.Append (',');
+            zRVal.DebString (sb);
+            sb.Append ('>');
+        }
+    }
+    
+    /**
+     * @brief encapsulates the whole script in a single token
+     */
+    public class TokenScript : Token {
+        public int expiryDays = Int32.MaxValue;
+        public TokenDeclState defaultState;
+        public Dictionary<string, TokenDeclState> states = new Dictionary<string, TokenDeclState>  ();
+        public VarDict variablesStack = new VarDict (false);  // initial one is used for global functions and variables
+        public TokenDeclVar globalVarInit;                    // $globalvarinit function
+                                                              // - performs explicit global var and static field inits
+
+        private Dictionary<string, TokenDeclSDType> sdSrcTypes = new Dictionary<string, TokenDeclSDType> ();
+        private bool sdSrcTypesSealed = false;
+
+        public TokenScript (Token original) : base (original) { }
+
+        /*
+         * Handle variable definition stack.
+         * Generally a '{' pushes a new frame and a '}' pops the frame.
+         * Function parameters are pushed in an additional frame (just outside the body's { ... } block)
+         */
+        public void PushVarFrame (bool locals)
+        {
+            PushVarFrame (new VarDict (locals));
+        }
+        public void PushVarFrame (VarDict newFrame)
+        {
+            newFrame.outerVarDict = variablesStack;
+            variablesStack = newFrame;
+        }
+        public void PopVarFrame ()
+        {
+            variablesStack = variablesStack.outerVarDict;
+        }
+        public bool AddVarEntry (TokenDeclVar var)
+        {
+            return variablesStack.AddEntry (var);
+        }
+
+        /*
+         * Handle list of script-defined types.
+         */
+        public void sdSrcTypesSeal ()
+        {
+            sdSrcTypesSealed = true;
+        }
+        public bool sdSrcTypesContainsKey (string key)
+        {
+            return sdSrcTypes.ContainsKey (key);
+        }
+        public bool sdSrcTypesTryGetValue (string key, out TokenDeclSDType value)
+        {
+            return sdSrcTypes.TryGetValue (key, out value);
+        }
+        public void sdSrcTypesAdd (string key, TokenDeclSDType value)
+        {
+            if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed");
+            value.sdTypeIndex = sdSrcTypes.Count;
+            sdSrcTypes.Add (key, value);
+        }
+        public void sdSrcTypesRep (string key, TokenDeclSDType value)
+        {
+            if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed");
+            value.sdTypeIndex = sdSrcTypes[key].sdTypeIndex;
+            sdSrcTypes[key] = value;
+        }
+        public void sdSrcTypesReplace (string key, TokenDeclSDType value)
+        {
+            if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed");
+            sdSrcTypes[key] = value;
+        }
+        public Dictionary<string, TokenDeclSDType>.ValueCollection sdSrcTypesValues
+        {
+            get {
+                return sdSrcTypes.Values;
+            }
+        }
+        public int sdSrcTypesCount
+        {
+            get {
+                return sdSrcTypes.Count;
+            }
+        }
+
+        /**
+         * @brief Debug output.
+         */
+        public override void DebString (StringBuilder sb)
+        {
+            /*
+             * Script-defined types.
+             */
+            foreach (TokenDeclSDType srcType in sdSrcTypes.Values) {
+                srcType.DebString (sb);
+            }
+
+            /*
+             * Global constants.
+             * Variables are handled by outputting the $globalvarinit function.
+             */
+            foreach (TokenDeclVar var in variablesStack) {
+                if (var.constant) {
+                    var.DebString (sb);
+                }
+            }
+
+            /*
+             * Global functions.
+             */
+            foreach (TokenDeclVar var in variablesStack) {
+                if (var == globalVarInit) {
+                    var.DebStringInitFields (sb);
+                } else if (var.retType != null) {
+                    var.DebString (sb);
+                }
+            }
+
+            /*
+             * States and their event handler functions.
+             */
+            defaultState.DebString (sb);
+            foreach (TokenDeclState st in states.Values) {
+                st.DebString (sb);
+            }
+        }
+    }
+
+    /**
+     * @brief state body declaration
+     */
+    public class TokenStateBody : Token {
+
+        public TokenDeclVar eventFuncs;
+
+        public int index = -1;  // (codegen) row in ScriptHandlerEventTable (0=default)
+
+        public TokenStateBody (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append (" { ");
+            for (Token t = eventFuncs; t != null; t = t.nextToken) {
+                t.DebString (sb);
+            }
+            sb.Append (" } ");
+        }
+    }
+
+    /**
+     * @brief a single statement, such as ending on a semicolon or enclosed in braces
+     * TokenStmt includes the terminating semicolon or the enclosing braces
+     * Also includes @label; for jump targets.
+     * Also includes stray ; null statements.
+     * Also includes local variable declarations with or without initialization value.
+     */
+    public class TokenStmt : Token {
+        public TokenStmt (Token original) : base (original) { }
+    }
+
+    /**
+     * @brief a group of statements enclosed in braces
+     */
+    public class TokenStmtBlock : TokenStmt {
+
+        public Token statements;               // null-terminated list of statements, can also have TokenDeclVar's in here
+        public TokenStmtBlock outerStmtBlock;  // next outer stmtBlock or null if top-level, ie, function definition
+        public TokenDeclVar function;          // function it is part of
+        public bool isTry;                     // true iff it's a try statement block
+        public bool isCatch;                   // true iff it's a catch statement block
+        public bool isFinally;                 // true iff it's a finally statement block
+        public TokenStmtTry tryStmt;           // set iff isTry|isCatch|isFinally is set
+
+        public TokenStmtBlock (Token original) : base (original) { }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("{ ");
+            for (Token stmt = statements; stmt != null; stmt = stmt.nextToken) {
+                stmt.DebString (sb);
+            }
+            sb.Append ("} ");
+        }
+    }
+
+    /**
+     * @brief definition of branch target name
+     */
+    public class TokenStmtLabel : TokenStmt {
+
+        public TokenName name;        // the label's name
+        public TokenStmtBlock block;  // which block it is defined in
+        public bool hasBkwdRefs = false;
+
+        public bool labelTagged;      // code gen: location of label
+        public ScriptMyLabel labelStruct;
+
+        public TokenStmtLabel (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ('@');
+            name.DebString (sb);
+            sb.Append (';');
+        }
+    }
+
+    /**
+     * @brief those types of RVals with a semi-colon on the end
+     *        that are allowed to stand alone as statements
+     */
+    public class TokenStmtRVal : TokenStmt {
+        public TokenRVal rVal;
+
+        public TokenStmtRVal (Token original) : base (original) { }
+
+        // debugging
+        public override void DebString (StringBuilder sb)
+        {
+            rVal.DebString (sb);
+            sb.Append ("; ");
+        }
+    }
+
+    public class TokenStmtBreak : TokenStmt {
+        public TokenStmtBreak (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("break;");
+        }
+    }
+
+    public class TokenStmtCont : TokenStmt {
+        public TokenStmtCont (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("continue;");
+        }
+    }
+
+    /**
+     * @brief "do" statement
+     */
+    public class TokenStmtDo : TokenStmt {
+
+        public TokenStmt bodyStmt;
+        public TokenRValParen testRVal;
+
+        public TokenStmtDo (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("do ");
+            bodyStmt.DebString (sb);
+            sb.Append (" while ");
+            testRVal.DebString (sb);
+            sb.Append (';');
+        }
+    }
+
+    /**
+     * @brief "for" statement
+     */
+    public class TokenStmtFor : TokenStmt {
+
+        public TokenStmt initStmt;  // there is always an init statement, though it may be a null statement
+        public TokenRVal testRVal;  // there may or may not be a test (null if not)
+        public TokenRVal incrRVal;  // there may or may not be an increment (null if not)
+        public TokenStmt bodyStmt;  // there is always a body statement, though it may be a null statement
+
+        public TokenStmtFor (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("for (");
+            if (initStmt != null) initStmt.DebString (sb);
+                             else sb.Append (';');
+            if (testRVal != null) testRVal.DebString (sb);
+            sb.Append (';');
+            if (incrRVal != null) incrRVal.DebString (sb);
+            sb.Append (") ");
+            bodyStmt.DebString (sb);
+        }
+    }
+
+    /**
+     * @brief "foreach" statement
+     */
+    public class TokenStmtForEach : TokenStmt {
+
+        public TokenLVal keyLVal;
+        public TokenLVal valLVal;
+        public TokenRVal arrayRVal;
+        public TokenStmt bodyStmt;  // there is always a body statement, though it may be a null statement
+
+        public TokenStmtForEach (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("foreach (");
+            if (keyLVal != null) keyLVal.DebString (sb);
+            sb.Append (',');
+            if (valLVal != null) valLVal.DebString (sb);
+            sb.Append (" in ");
+            arrayRVal.DebString (sb);
+            sb.Append (')');
+            bodyStmt.DebString (sb);
+        }
+    }
+
+    public class TokenStmtIf : TokenStmt {
+
+        public TokenRValParen testRVal;
+        public TokenStmt trueStmt;
+        public TokenStmt elseStmt;
+
+        public TokenStmtIf (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+                {
+            sb.Append ("if ");
+            testRVal.DebString (sb);
+            sb.Append (" ");
+            trueStmt.DebString (sb);
+            if (elseStmt != null) {
+                sb.Append (" else ");
+                elseStmt.DebString (sb);
+            }
+        }
+    }
+
+    public class TokenStmtJump : TokenStmt {
+
+        public TokenName label;
+
+        public TokenStmtJump (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("jump ");
+            label.DebString (sb);
+            sb.Append (';');
+        }
+    }
+
+    public class TokenStmtNull : TokenStmt {
+
+        public TokenStmtNull (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append (';');
+        }
+    }
+
+    public class TokenStmtRet : TokenStmt {
+
+        public TokenRVal rVal;  // null if void
+
+        public TokenStmtRet (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("return");
+            if (rVal != null) {
+                sb.Append (' ');
+                rVal.DebString (sb);
+            }
+            sb.Append (';');
+        }
+    }
+
+    /**
+     * @brief statement that changes the current state.
+     */
+    public class TokenStmtState : TokenStmt {
+
+        public TokenName state;  // null for default
+
+        public TokenStmtState (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("state ");
+            sb.Append ((state == null) ? "default" : state.val);
+            sb.Append (';');
+        }
+    }
+
+    /**
+     * @brief Encapsulates a whole switch statement including the body and all cases.
+     */
+    public class TokenStmtSwitch : TokenStmt {
+
+        public TokenRValParen testRVal;          // the integer index expression
+        public TokenSwitchCase cases = null;     // list of all cases, linked by .nextCase
+        public TokenSwitchCase lastCase = null;  // used during reduce to point to last in 'cases' list
+
+        public TokenStmtSwitch (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("switch ");
+            testRVal.DebString (sb);
+            sb.Append ('{');
+            for (TokenSwitchCase kase = cases; kase != null; kase = kase.nextCase) {
+                kase.DebString (sb);
+            }
+            sb.Append ('}');
+        }
+    }
+
+    /**
+     * @brief Encapsulates a case/default clause from a switch statement including the
+     *        two values and the corresponding body statements.
+     */
+    public class TokenSwitchCase : Token {
+        public TokenSwitchCase nextCase;  // next case in source-code order
+        public TokenRVal rVal1;           // null means 'default', else 'case'
+        public TokenRVal rVal2;           // null means 'case expr:', else 'case expr ... expr:'
+        public TokenStmt stmts;           // statements associated with the case
+        public TokenStmt lastStmt;        // used during reduce for building statement list
+
+        public int val1;                        // codegen: value of rVal1 here
+        public int val2;                        // codegen: value of rVal2 here
+        public ScriptMyLabel label;             // codegen: target label here
+        public TokenSwitchCase nextSortedCase;  // codegen: next case in ascending val order
+
+        public string str1;
+        public string str2;
+        public TokenSwitchCase lowerCase;
+        public TokenSwitchCase higherCase;
+
+        public TokenSwitchCase (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            if (rVal1 == null) {
+                sb.Append ("default: ");
+            } else {
+                sb.Append ("case ");
+                rVal1.DebString (sb);
+                if (rVal2 != null) {
+                    sb.Append (" ... ");
+                    rVal2.DebString (sb);
+                }
+                sb.Append (": ");
+            }
+            for (Token t = stmts; t != null; t = t.nextToken) {
+                t.DebString (sb);
+            }
+        }
+    }
+
+    public class TokenStmtThrow : TokenStmt {
+
+        public TokenRVal rVal;  // null if rethrow style
+
+        public TokenStmtThrow (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("throw ");
+            rVal.DebString (sb);
+            sb.Append (';');
+        }
+    }
+
+    /**
+     * @brief Encapsulates related try, catch and finally statements.
+     */
+    public class TokenStmtTry : TokenStmt {
+
+        public TokenStmtBlock tryStmt;
+        public TokenDeclVar catchVar;       // null iff catchStmt is null
+        public TokenStmtBlock catchStmt;    // can be null
+        public TokenStmtBlock finallyStmt;  // can be null
+        public Dictionary<string, IntermediateLeave> iLeaves = new Dictionary<string, IntermediateLeave> ();
+
+        public TokenStmtTry (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("try ");
+            tryStmt.DebString (sb);
+            if (catchStmt != null) {
+                sb.Append ("catch (");
+                sb.Append (catchVar.type.ToString ());
+                sb.Append (' ');
+                sb.Append (catchVar.name.val);
+                sb.Append (") ");
+                catchStmt.DebString (sb);
+            }
+            if (finallyStmt != null) {
+                sb.Append ("finally ");
+                finallyStmt.DebString (sb);
+            }
+        }
+    }
+
+    public class IntermediateLeave {
+        public ScriptMyLabel jumpIntoLabel;
+        public ScriptMyLabel jumpAwayLabel;
+    }
+
+    public class TokenStmtVarIniDef : TokenStmt {
+        public TokenLVal var;
+        public TokenStmtVarIniDef (Token original) : base (original) { }
+    }
+
+    public class TokenStmtWhile : TokenStmt {
+
+        public TokenRValParen testRVal;
+        public TokenStmt bodyStmt;
+
+        public TokenStmtWhile (Token original) : base (original) { }
+
+        public override void DebString (StringBuilder sb)
+        {
+            sb.Append ("while ");
+            testRVal.DebString (sb);
+            sb.Append (' ');
+            bodyStmt.DebString (sb);
+        }
+    }
+
+    /**
+     * @brief type expressions (right-hand of 'is' keyword).
+     */
+    public class TokenTypeExp : Token {
+        public TokenTypeExp (Token original) : base (original) { }
+    }
+
+    public class TokenTypeExpBinOp : TokenTypeExp {
+        public TokenTypeExp leftOp;
+        public Token        binOp;
+        public TokenTypeExp rightOp;
+
+        public TokenTypeExpBinOp (Token original) : base (original) { }
+    }
+
+    public class TokenTypeExpNot : TokenTypeExp {
+        public TokenTypeExp typeExp;
+
+        public TokenTypeExpNot (Token original) : base (original) { }
+    }
+
+    public class TokenTypeExpPar : TokenTypeExp {
+        public TokenTypeExp typeExp;
+
+        public TokenTypeExpPar (Token original) : base (original) { }
+    }
+
+    public class TokenTypeExpType : TokenTypeExp {
+        public TokenType typeToken;
+
+        public TokenTypeExpType (Token original) : base (original) { }
+    }
+
+    public class TokenTypeExpUndef : TokenTypeExp {
+        public TokenTypeExpUndef (Token original) : base (original) { }
+    }
+}

+ 1729 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTokenize.cs

@@ -0,0 +1,1729 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @brief Parse raw source file string into token list.
+ *
+ * Usage:
+ *
+ *    emsg = some function to output error messages to
+ *    source = string containing entire source file
+ *
+ *    TokenBegin tokenBegin = TokenBegin.Construct (emsg, source);
+ *
+ *    tokenBegin = null: tokenizing error
+ *                 else: first (dummy) token in file
+ *                       the rest are chained by nextToken,prevToken
+ *                       final token is always a (dummy) TokenEnd
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    public delegate void TokenErrorMessage (Token token, string message);
+
+    /**
+     * @brief base class for all tokens
+     */
+    public class Token {
+        public static readonly int MAX_NAME_LEN = 255;
+        public static readonly int MAX_STRING_LEN = 4096;
+
+        public Token nextToken;
+        public Token prevToken;
+        public bool  nr2l;
+
+        // used for error message printing
+        public TokenErrorMessage emsg;
+        public string file = "";
+        public int line;
+        public int posn;
+        public Token copiedFrom;
+
+        /**
+         * @brief construct a token coming directly from a source file
+         * @param emsg = object that error messages get sent to
+         * @param file = source file name (or "" if none)
+         * @param line = source file line number
+         * @param posn = token's position within that source line
+         */
+        public Token (TokenErrorMessage emsg, string file, int line, int posn)
+        {
+            this.emsg = emsg;
+            this.file = file;
+            this.line = line;
+            this.posn = posn;
+        }
+
+        /**
+         * @brief construct a token with same error message parameters
+         * @param original = original token to create from
+         */
+        public Token (Token original)
+        {
+            if (original != null) {
+                this.emsg = original.emsg;
+                this.file = original.file;
+                this.line = original.line;
+                this.posn = original.posn;
+                this.nr2l = original.nr2l;
+            }
+        }
+
+        /**
+         * @brief output an error message associated with this token
+         *        sends the message to the token's error object
+         * @param message = error message string
+         */
+        public void ErrorMsg (string message)
+        {
+            if (emsg != null) {
+                emsg (this, message);
+            }
+        }
+
+        /*
+         * Generate a unique string (for use in CIL label names, etc)
+         */
+        public string Unique
+        {
+            get { return file + "_" + line + "_" + posn; }
+        }
+
+        /*
+         * Generate source location string (for use in error messages)
+         */
+        public string SrcLoc
+        {
+            get {
+                string loc = file + "(" + line + "," + posn + ")";
+                if (copiedFrom == null) return loc;
+                string fromLoc = copiedFrom.SrcLoc;
+                if (fromLoc.StartsWith (loc)) return fromLoc;
+                return loc + ":" + fromLoc;
+            }
+        }
+
+        /*
+         * Used in generic instantiation to copy token.
+         * Only valid for parsing tokens, not reduction tokens 
+         * because it is a shallow copy.
+         */
+        public Token CopyToken (Token src)
+        {
+            Token t = (Token)this.MemberwiseClone ();
+            t.file  = src.file;
+            t.line  = src.line;
+            t.posn  = src.posn;
+            t.copiedFrom = this;
+            return t;
+        }
+
+        /*
+         * Generate debugging string - should look like source code.
+         */
+        public virtual void DebString (StringBuilder sb)
+        {
+            sb.Append (this.ToString ());
+        }
+    }
+
+
+    /**
+     * @brief token that begins a source file
+     *        Along with TokenEnd, it keeps insertion/removal of intermediate tokens
+     *        simple as the intermediate tokens always have non-null nextToken,prevToken.
+     */
+    public class TokenBegin : Token {
+
+        public int expiryDays = Int32.MaxValue;  // has seen 'XMROption expiryDays;'
+
+        private class Options {
+            public bool arrays;         // has seen 'XMROption arrays;'
+            public bool advFlowCtl;     // has seen 'XMROption advFlowCtl;'
+            public bool tryCatch;       // has seen 'XMROption tryCatch;'
+            public bool objects;        // has seen 'XMROption objects;'
+            public bool chars;          // has seen 'XMROption chars;'
+            public bool noRightToLeft;  // has seen 'XMROption noRightToLeft;'
+            public bool dollarsigns;    // has seen 'XMROption dollarsigns;'
+        }
+
+        private bool youveAnError;      // there was some error tokenizing
+        private int bolIdx;             // index in 'source' at begining of current line
+        private int lineNo;             // current line in source file, starting at 0
+        private string filNam;          // current source file name
+        private string source;          // the whole script source code
+        private Token lastToken;        // last token created so far
+        private string cameFrom;        // where the source came from
+        private TextWriter saveSource;  // save copy of source here (or null)
+        private Options options = new Options ();
+
+        /**
+         * @brief convert a source file in the form of a string
+         *        to a list of raw tokens
+         * @param cameFrom = where the source came from
+         * @param emsg     = where to output messages to
+         * @param source   = whole source file contents
+         * @returns null: conversion error, message already output
+         *          else: list of tokens, starting with TokenBegin, ending with TokenEnd.
+         */
+        public static TokenBegin Construct (string cameFrom, TextWriter saveSource, TokenErrorMessage emsg, string source, out string sourceHash)
+        {
+            sourceHash = null;
+
+            /*
+             * Now do the tokenization.
+             */
+            TokenBegin tokenBegin = new TokenBegin (emsg, "", 0, 0);
+            tokenBegin.cameFrom   = cameFrom;
+            tokenBegin.saveSource = saveSource;
+            tokenBegin.lastToken  = tokenBegin;
+            tokenBegin.source     = source;
+            tokenBegin.filNam     = cameFrom;
+            if (saveSource != null) saveSource.WriteLine (source);
+            tokenBegin.Tokenize ();
+            if (tokenBegin.youveAnError) return null;
+            tokenBegin.AppendToken (new TokenEnd (emsg, tokenBegin.filNam, ++ tokenBegin.lineNo, 0));
+
+            /*
+             * Return source hash so caller can know if source changes.
+             */
+            System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create ();
+            byte[] hashBytes = md5.ComputeHash (new TokenStream (tokenBegin));
+            int hashBytesLen = hashBytes.Length;
+            StringBuilder sb = new StringBuilder (hashBytesLen * 2);
+            for (int i = 0; i < hashBytesLen; i ++) {
+                sb.Append (hashBytes[i].ToString ("X2"));
+            }
+            sourceHash = sb.ToString ();
+            if (saveSource != null) {
+                saveSource.WriteLine ("");
+                saveSource.WriteLine ("********************************************************************************");
+                saveSource.WriteLine ("****  source hash: " + sourceHash);
+                saveSource.WriteLine ("********************************************************************************");
+            }
+
+            return tokenBegin;
+        }
+
+        private TokenBegin (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+
+        /*
+         * Stream consisting of all the tokens.
+         * Null delimeters between the tokens.
+         * Used for creating the source hash.
+         */
+        private class TokenStream : Stream {
+            private Token curTok;
+            private bool delim;
+            private byte[] curBuf;
+            private int curOfs;
+            private int curLen;
+
+            public TokenStream (Token t)
+            {
+                curTok = t;
+            }
+
+            public override bool CanRead  { get { return true;  } }
+            public override bool CanSeek  { get { return false; } }
+            public override bool CanWrite { get { return false; } }
+            public override long Length   { get { return 0; } }
+            public override long Position { get { return 0; } set { } }
+
+            public override void Write (byte[] buffer, int offset, int count) { }
+            public override void Flush () { }
+            public override long Seek (long offset, SeekOrigin origin) { return 0; }
+            public override void SetLength (long value) { }
+
+            public override int Read (byte[] buffer, int offset, int count)
+            {
+                int len, total;
+                for (total = 0; total < count; total += len) {
+                    while ((len = curLen - curOfs) <= 0) {
+                        if (curTok is TokenEnd) goto done;
+                        curTok = curTok.nextToken;
+                        if (curTok is TokenEnd) goto done;
+                        curBuf = System.Text.Encoding.UTF8.GetBytes (curTok.ToString ());
+                        curOfs = 0;
+                        curLen = curBuf.Length;
+                        delim  = true;
+                    }
+                    if (delim) {
+                        buffer[offset+total] = 0;
+                        delim = false;
+                        len   = 1;
+                    } else {
+                        if (len > count - total) len = count - total;
+                        Array.Copy (curBuf, curOfs, buffer, offset + total, len);
+                        curOfs += len;
+                    }
+                }
+              done:
+                return total;
+            }
+        }
+
+        /*
+         * Produces raw token stream: names, numbers, strings, keywords/delimeters.
+         * @param this.source = whole source file in one string
+         * @returns this.nextToken = filled in with tokens
+         *          this.youveAnError = true: some tokenizing error
+         *                             false: successful
+         */
+        private void Tokenize ()
+        {
+            bolIdx = 0;
+            lineNo = 0;
+            for (int i = 0; i < source.Length; i ++) {
+                char c = source[i];
+                if (c == '\n') {
+
+                    /*
+                     * Increment source line number and set char index of beg of next line.
+                     */
+                    lineNo ++;
+                    bolIdx = i + 1;
+
+                    /*
+                     * Check for '#' lineno filename newline
+                     * lineno is line number of next line in file
+                     * If found, save values and remove tokens from stream
+                     */
+                    if ((lastToken is TokenStr) &&
+                        (lastToken.prevToken is TokenInt) &&
+                        (lastToken.prevToken.prevToken is TokenKwHash)) {
+                        filNam = ((TokenStr)lastToken).val;
+                        lineNo = ((TokenInt)lastToken.prevToken).val;
+                        lastToken = lastToken.prevToken.prevToken.prevToken;
+                        lastToken.nextToken = null;
+                    }
+                    continue;
+                }
+
+                /*
+                 * Skip over whitespace.
+                 */
+                if (c <= ' ') continue;
+
+                /*
+                 * Skip over comments.
+                 */
+                if ((i + 2 <= source.Length) && source.Substring (i, 2).Equals ("//")) {
+                    while ((i < source.Length) && (source[i] != '\n')) i ++;
+                    lineNo ++;
+                    bolIdx = i + 1;
+                    continue;
+                }
+                if ((i + 2 <= source.Length) && (source.Substring (i, 2).Equals ("/*"))) {
+                    i += 2;
+                    while ((i + 1 < source.Length) && (((c = source[i]) != '*') || (source[i+1] != '/'))) {
+                        if (c == '\n') {
+                            lineNo ++;
+                            bolIdx = i + 1;
+                        }
+                        i ++;
+                    }
+                    i ++;
+                    continue;
+                }
+
+                /*
+                 * Check for numbers.
+                 */
+                if ((c >= '0') && (c <= '9')) {
+                    int j = TryParseFloat (i);
+                    if (j == 0) j = TryParseInt (i);
+                    i = -- j;
+                    continue;
+                }
+                if ((c == '.') && (source[i+1] >= '0') && (source[i+1] <= '9')) {
+                    int j = TryParseFloat (i);
+                    if (j > 0) i = -- j;
+                    continue;
+                }
+
+                /*
+                 * Check for quoted strings.
+                 */
+                if (c == '"') {
+                    StringBuilder sb = new StringBuilder ();
+                    bool backslash;
+                    int j;
+
+                    backslash = false;
+                    for (j = i; ++ j < source.Length;) {
+                        c = source[j];
+                        if (c == '\\' && !backslash) {
+                            backslash = true;
+                            continue;
+                        }
+                        if (c == '\n') {
+                            lineNo ++;
+                            bolIdx = j + 1;
+                        } else {
+                            if (!backslash && (c == '"')) break;
+                            if (backslash && (c == 'n')) c = '\n';
+                            if (backslash && (c == 't')) {
+                                sb.Append ("   ");
+                                c = ' ';
+                            }
+                        }
+                        backslash = false;
+                        sb.Append (c);
+                    }
+                    if (j - i > MAX_STRING_LEN) {
+                        TokenError (i, "string too long, max " + MAX_STRING_LEN);
+                    } else {
+                        AppendToken (new TokenStr (emsg, filNam, lineNo, i - bolIdx, sb.ToString ()));
+                    }
+                    i = j;
+                    continue;
+                }
+
+                /*
+                 * Check for quoted characters.
+                 */
+                if (c == '\'') {
+                    char cb = (char)0;
+                    bool backslash, overflow, underflow;
+                    int j;
+
+                    backslash = false;
+                    overflow  = false;
+                    underflow = true;
+                    for (j = i; ++ j < source.Length;) {
+                        c = source[j];
+                        if (c == '\\' && !backslash) {
+                            backslash = true;
+                            continue;
+                        }
+                        if (c == '\n') {
+                            lineNo ++;
+                            bolIdx = j + 1;
+                        } else {
+                            if (!backslash && (c == '\'')) break;
+                            if (backslash && (c == 'n')) c = '\n';
+                            if (backslash && (c == 't')) c = '\t';
+                        }
+                        backslash = false;
+                        overflow  = !underflow;
+                        underflow = false;
+                        cb = c;
+                    }
+                    if (underflow || overflow) {
+                        TokenError (i, "character must be exactly one character");
+                    } else {
+                        AppendToken (new TokenChar (emsg, filNam, lineNo, i - bolIdx, cb));
+                    }
+                    i = j;
+                    continue;
+                }
+
+                /*
+                 * Check for keywords/names.
+                 */
+                if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || (c == '$' && options.dollarsigns)) {
+                    int j;
+
+                    for (j = i; ++ j < source.Length;) {
+                        c = source[j];
+                        if (c >= 'a' && c <= 'z') continue;
+                        if (c >= 'A' && c <= 'Z') continue;
+                        if (c >= '0' && c <= '9') continue;
+                        if (c == '$' && options.dollarsigns) continue;
+                        if (c != '_') break;
+                    }
+                    if (j - i > MAX_NAME_LEN) {
+                        TokenError (i, "name too long, max " + MAX_NAME_LEN);
+                    } else {
+                        string name = source.Substring (i, j - i);
+                        if (name == "quaternion") name = "rotation";  // see lslangtest1.lsl
+                        if (keywords.ContainsKey (name)) {
+                            Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
+                            AppendToken ((Token)keywords[name].Invoke (args));
+                        } else if (options.arrays && arrayKeywords.ContainsKey (name)) {
+                            Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
+                            AppendToken ((Token)arrayKeywords[name].Invoke (args));
+                        } else if (options.advFlowCtl && advFlowCtlKeywords.ContainsKey (name)) {
+                            Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
+                            AppendToken ((Token)advFlowCtlKeywords[name].Invoke (args));
+                        } else if (options.tryCatch && tryCatchKeywords.ContainsKey (name)) {
+                            Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
+                            AppendToken ((Token)tryCatchKeywords[name].Invoke (args));
+                        } else if (options.objects && objectsKeywords.ContainsKey (name)) {
+                            Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
+                            AppendToken ((Token)objectsKeywords[name].Invoke (args));
+                        } else if (options.chars && charsKeywords.ContainsKey (name)) {
+                            Object[] args = new Object[] { emsg, filNam, lineNo, i - bolIdx };
+                            AppendToken ((Token)charsKeywords[name].Invoke (args));
+                        } else {
+                            AppendToken (new TokenName (emsg, filNam, lineNo, i - bolIdx, name));
+                        }
+                    }
+                    i = -- j;
+                    continue;
+                }
+
+                /*
+                 * Check for option enables.
+                 */
+                if ((c == ';') && (lastToken is TokenName) && 
+                    (lastToken.prevToken is TokenName) && 
+                    (strcasecmp(((TokenName)lastToken.prevToken).val, "xmroption") == 0)) {
+                    string opt = ((TokenName)lastToken).val;
+                    if (strcasecmp (opt, "arrays") == 0) {
+                        options.arrays = true;
+                    } else if (strcasecmp (opt, "advflowctl") == 0) {
+                        options.advFlowCtl = true;
+                    } else if (strcasecmp (opt, "trycatch") == 0) {
+                        options.tryCatch = true;
+                    } else if (strcasecmp (opt, "objects") == 0) {
+                        options.objects = true;
+                    } else if (strcasecmp (opt, "chars") == 0) {
+                        options.chars = true;
+                    } else if (strcasecmp (opt, "norighttoleft") == 0) {
+                        options.noRightToLeft = true;
+                    } else if (strcasecmp (opt, "dollarsigns") == 0) {
+                        options.dollarsigns = true;
+                    } else {
+                        lastToken.ErrorMsg ("unknown XMROption");
+                    }
+                    lastToken = lastToken.prevToken.prevToken;
+                    lastToken.nextToken = null;
+                    continue;
+                }
+
+                /*
+                 * Handle 'xmroption' 'expirydays' numberofdays ';'
+                 */
+                if ((c == ';') && 
+                    (lastToken is TokenInt) && 
+                    (lastToken.prevToken is TokenName) && 
+                    (lastToken.prevToken.prevToken is TokenName) &&
+                    (strcasecmp(((TokenName)lastToken.prevToken.prevToken).val, "xmroption") == 0) && 
+                    (strcasecmp(((TokenName)lastToken.prevToken).val, "expirydays") == 0)) {
+                    expiryDays = ((TokenInt)lastToken).val;
+                    this.lastToken = lastToken.prevToken.prevToken.prevToken;
+                    this.lastToken.nextToken = null;
+                    continue;
+                }
+
+
+                /*
+                 * Handle 'xmroption' 'include' sourceurl ';'
+                 */
+                if ((c == ';') && 
+                    (lastToken is TokenStr) && 
+                    (lastToken.prevToken is TokenName) && 
+                    (lastToken.prevToken.prevToken is TokenName) &&
+                    (strcasecmp(((TokenName)lastToken.prevToken.prevToken).val, "xmroption") == 0) && 
+                    (strcasecmp(((TokenName)lastToken.prevToken).val, "include") == 0)) {
+
+                    string newURL      = ((TokenStr)lastToken).val;
+                    if (newURL == "") {
+                        lastToken.ErrorMsg ("empty URL string");
+                        continue;
+                    }
+                    string newCameFrom = CreateURL (this.cameFrom, newURL);
+                    string newSource   = ReadSourceFromURL (newCameFrom);
+
+                    this.lastToken     = lastToken.prevToken.prevToken.prevToken;
+                    this.lastToken.nextToken = null;
+
+                    if (newSource != null) {
+                        if (saveSource != null) {
+                            saveSource.WriteLine ("");
+                            saveSource.WriteLine ("********************************************************************************");
+                            saveSource.WriteLine ("****  include url: " + newCameFrom);
+                            saveSource.WriteLine ("********************************************************************************");
+                            saveSource.WriteLine (newSource);
+                        }
+
+                        string saveSourc    = this.source;
+                        string saveFilNam   = this.filNam;
+                        string saveCameFrom = this.cameFrom;
+                        int saveBolIdx      = this.bolIdx;
+                        int saveLineNo      = this.lineNo;
+                        Options saveOptions = this.options;
+                        this.source   = newSource;
+                        this.filNam   = newURL;
+                        this.cameFrom = newCameFrom;
+                        this.options  = new Options ();
+                        this.Tokenize ();
+                        this.source   = saveSourc;
+                        this.filNam   = saveFilNam;
+                        this.cameFrom = saveCameFrom;
+                        this.bolIdx   = saveBolIdx;
+                        this.lineNo   = saveLineNo;
+                        this.options  = saveOptions;
+                    }
+                    continue;
+                }
+
+                /*
+                 * Lastly, check for delimeters.
+                 */
+                {
+                    int j;
+                    int len = 0;
+
+                    for (j = 0; j < delims.Length; j ++) {
+                        len = delims[j].str.Length;
+                        if ((i + len <= source.Length) && (source.Substring (i, len).Equals (delims[j].str))) break;
+                    }
+                    if (j < delims.Length) {
+                        Object[] args = { emsg, filNam, lineNo, i - bolIdx };
+                        Token kwToken = (Token)delims[j].ctorInfo.Invoke (args);
+                        AppendToken (kwToken);
+                        i += -- len;
+                        continue;
+                    }
+                }
+
+                /*
+                 * Don't know what it is!
+                 */
+                TokenError (i, "unknown character '" + c + "'");
+            }
+        }
+
+        private static int strcasecmp (String s, String t)
+        {
+            return String.Compare(s,t,StringComparison.OrdinalIgnoreCase);
+        }
+
+        /**
+         * @brief try to parse a floating-point number from the source
+         * @param i = starting position within this.source of number
+         * @returns 0: not a floating point number, try something else
+         *       else: position in this.source of terminating character, ie, past number
+         *             TokenFloat appended to token list
+         *             or error message has been output
+         */
+        private int TryParseFloat (int i)
+        {
+            bool decimals, error, negexp, nulexp;
+            char c;
+            double f, f10;
+            int exponent, j, x, y;
+            ulong m, mantissa;
+
+            decimals = false;
+            error    = false;
+            exponent = 0;
+            mantissa = 0;
+            for (j = i; j < source.Length; j ++) {
+                c = source[j];
+                if ((c >= '0') && (c <= '9')) {
+                    m = mantissa * 10 + (ulong)(c - '0');
+                    if (m / 10 != mantissa) {
+                        if (!decimals) exponent ++;
+                    } else {
+                        mantissa = m;
+                        if (decimals) exponent --;
+                    }
+                    continue;
+                }
+                if (c == '.') {
+                    if (decimals) {
+                        TokenError (i, "more than one decimal point");
+                        return j;
+                    }
+                    decimals = true;
+                    continue;
+                }
+                if ((c == 'E') || (c == 'e')) {
+                    if (++ j >= source.Length) {
+                        TokenError (i, "floating exponent off end of source");
+                        return j;
+                    }
+                    c = source[j];
+                    negexp = (c == '-');
+                    if (negexp || (c == '+')) j ++;
+                    y = 0;
+                    nulexp = true;
+                    for (; j < source.Length; j ++) {
+                        c = source[j];
+                        if ((c < '0') || (c > '9')) break;
+                        x = y * 10 + (c - '0');
+                        if (x / 10 != y) {
+                            if (!error) TokenError (i, "floating exponent overflow");
+                            error = true;
+                        }
+                        y = x;
+                        nulexp = false;
+                    }
+                    if (nulexp) {
+                        TokenError (i, "bad or missing floating exponent");
+                        return j;
+                    }
+                    if (negexp) {
+                        x = exponent - y;
+                        if (x > exponent) {
+                            if (!error) TokenError (i, "floating exponent overflow");
+                            error = true;
+                        }
+                    } else {
+                        x = exponent + y;
+                        if (x < exponent) {
+                            if (!error) TokenError (i, "floating exponent overflow");
+                            error = true;
+                        }
+                    }
+                    exponent = x;
+                }
+                break;
+            }
+            if (!decimals) {
+                return 0;
+            }
+
+            f = mantissa;
+            if ((exponent != 0) && (mantissa != 0) && !error) {
+                f10 = 10.0;
+                if (exponent < 0) {
+                    exponent = -exponent;
+                    while (exponent > 0) {
+                        if ((exponent & 1) != 0) {
+                            f /= f10;
+                        }
+                        exponent /= 2;
+                        f10 *= f10;
+                    }
+                } else {
+                    while (exponent > 0) {
+                        if ((exponent & 1) != 0) {
+                            f *= f10;
+                        }
+                        exponent /= 2;
+                        f10 *= f10;
+                    }
+                }
+            }
+            if (!error) {
+                AppendToken (new TokenFloat (emsg, filNam, lineNo, i - bolIdx, f));
+            }
+            return j;
+        }
+
+        /**
+         * @brief try to parse an integer number from the source
+         * @param i = starting position within this.source of number
+         * @returns 0: not an integer number, try something else
+         *       else: position in this.source of terminating character, ie, past number
+         *             TokenInt appended to token list
+         *             or error message has been output
+         */
+        private int TryParseInt (int i)
+        {
+            bool error;
+            char c;
+            int j;
+            uint basse, m, mantissa;
+
+            basse    = 10;
+            error    = false;
+            mantissa = 0;
+            for (j = i; j < source.Length; j ++) {
+                c = source[j];
+                if ((c >= '0') && (c <= '9')) {
+                    m = mantissa * basse + (uint)(c - '0');
+                    if (m / basse != mantissa) {
+                        if (!error) TokenError (i, "integer overflow");
+                        error = true;
+                    }
+                    mantissa = m;
+                    continue;
+                }
+                if ((basse == 16) && ((c >= 'A') && (c <= 'F'))) {
+                    m = mantissa * basse + (uint)(c - 'A') + 10U;
+                    if (m / basse != mantissa) {
+                        if (!error) TokenError (i, "integer overflow");
+                        error = true;
+                    }
+                    mantissa = m;
+                    continue;
+                }
+                if ((basse == 16) && ((c >= 'a') && (c <= 'f'))) {
+                    m = mantissa * basse + (uint)(c - 'a') + 10U;
+                    if (m / basse != mantissa) {
+                        if (!error) TokenError (i, "integer overflow");
+                        error = true;
+                    }
+                    mantissa = m;
+                    continue;
+                }
+                if (((c == 'x') || (c == 'X')) && (mantissa == 0) && (basse == 10)) {
+                    basse = 16;
+                    continue;
+                }
+                break;
+            }
+            if (!error) {
+                AppendToken (new TokenInt (emsg, filNam, lineNo, i - bolIdx, (int)mantissa));
+            }
+            return j;
+        }
+
+        /**
+         * @brief append token on to end of list
+         * @param newToken = token to append
+         * @returns with token appended onto this.lastToken
+         */
+        private void AppendToken (Token newToken)
+        {
+            newToken.nextToken  = null;
+            newToken.prevToken  = lastToken;
+            newToken.nr2l       = this.options.noRightToLeft;
+            lastToken.nextToken = newToken;
+            lastToken           = newToken;
+        }
+
+        /**
+         * @brief print tokenizing error message
+         *        and remember that we've an error
+         * @param i = position within source file of the error
+         * @param message = error message text
+         * @returns with this.youveAnError set
+         */
+        private void TokenError (int i, string message)
+        {
+            Token temp = new Token (this.emsg, this.filNam, this.lineNo, i - this.bolIdx);
+            temp.ErrorMsg (message);
+            youveAnError = true;
+        }
+
+        /**
+         * @brief get a token's constructor
+         * @param tokenType = token's type
+         * @returns token's constructor
+         */
+        private static Type[] constrTypes = new Type[] {
+            typeof (TokenErrorMessage), typeof (string), typeof (int), typeof (int)
+        };
+
+        private static System.Reflection.ConstructorInfo GetTokenCtor (Type tokenType)
+        {
+            return tokenType.GetConstructor (constrTypes);
+        }
+
+        /**
+         * @brief delimeter table
+         */
+        private class Delim {
+            public string str;
+            public System.Reflection.ConstructorInfo ctorInfo;
+            public Delim (string str, Type type)
+            {
+                this.str = str;
+                ctorInfo = GetTokenCtor (type);
+            }
+        }
+
+        private static Delim[] delims = new Delim[] {
+            new Delim ("...", typeof (TokenKwDotDotDot)),
+            new Delim ("&&&", typeof (TokenKwAndAndAnd)),
+            new Delim ("|||", typeof (TokenKwOrOrOr)),
+            new Delim ("<<=", typeof (TokenKwAsnLSh)),
+            new Delim (">>=", typeof (TokenKwAsnRSh)),
+            new Delim ("<=",  typeof (TokenKwCmpLE)),
+            new Delim (">=",  typeof (TokenKwCmpGE)),
+            new Delim ("==",  typeof (TokenKwCmpEQ)),
+            new Delim ("!=",  typeof (TokenKwCmpNE)),
+            new Delim ("++",  typeof (TokenKwIncr)),
+            new Delim ("--",  typeof (TokenKwDecr)),
+            new Delim ("&&",  typeof (TokenKwAndAnd)),
+            new Delim ("||",  typeof (TokenKwOrOr)),
+            new Delim ("+=",  typeof (TokenKwAsnAdd)),
+            new Delim ("&=",  typeof (TokenKwAsnAnd)),
+            new Delim ("-=",  typeof (TokenKwAsnSub)),
+            new Delim ("*=",  typeof (TokenKwAsnMul)),
+            new Delim ("/=",  typeof (TokenKwAsnDiv)),
+            new Delim ("%=",  typeof (TokenKwAsnMod)),
+            new Delim ("|=",  typeof (TokenKwAsnOr)),
+            new Delim ("^=",  typeof (TokenKwAsnXor)),
+            new Delim ("<<",  typeof (TokenKwLSh)),
+            new Delim (">>",  typeof (TokenKwRSh)),
+            new Delim ("~",   typeof (TokenKwTilde)),
+            new Delim ("!",   typeof (TokenKwExclam)),
+            new Delim ("@",   typeof (TokenKwAt)),
+            new Delim ("%",   typeof (TokenKwMod)),
+            new Delim ("^",   typeof (TokenKwXor)),
+            new Delim ("&",   typeof (TokenKwAnd)),
+            new Delim ("*",   typeof (TokenKwMul)),
+            new Delim ("(",   typeof (TokenKwParOpen)),
+            new Delim (")",   typeof (TokenKwParClose)),
+            new Delim ("-",   typeof (TokenKwSub)),
+            new Delim ("+",   typeof (TokenKwAdd)),
+            new Delim ("=",   typeof (TokenKwAssign)),
+            new Delim ("{",   typeof (TokenKwBrcOpen)),
+            new Delim ("}",   typeof (TokenKwBrcClose)),
+            new Delim ("[",   typeof (TokenKwBrkOpen)),
+            new Delim ("]",   typeof (TokenKwBrkClose)),
+            new Delim (";",   typeof (TokenKwSemi)),
+            new Delim (":",   typeof (TokenKwColon)),
+            new Delim ("<",   typeof (TokenKwCmpLT)),
+            new Delim (">",   typeof (TokenKwCmpGT)),
+            new Delim (",",   typeof (TokenKwComma)),
+            new Delim (".",   typeof (TokenKwDot)),
+            new Delim ("?",   typeof (TokenKwQMark)),
+            new Delim ("/",   typeof (TokenKwDiv)),
+            new Delim ("|",   typeof (TokenKwOr)),
+            new Delim ("#",   typeof (TokenKwHash))
+        };
+
+        /**
+         * @brief keyword tables
+         *        The keyword tables translate a keyword string
+         *        to the corresponding token constructor.
+         */
+        private static Dictionary<string, System.Reflection.ConstructorInfo> keywords           = BuildKeywords ();
+        private static Dictionary<string, System.Reflection.ConstructorInfo> arrayKeywords      = BuildArrayKeywords ();
+        private static Dictionary<string, System.Reflection.ConstructorInfo> advFlowCtlKeywords = BuildAdvFlowCtlKeywords ();
+        private static Dictionary<string, System.Reflection.ConstructorInfo> tryCatchKeywords   = BuildTryCatchKeywords ();
+        private static Dictionary<string, System.Reflection.ConstructorInfo> objectsKeywords    = BuildObjectsKeywords ();
+        private static Dictionary<string, System.Reflection.ConstructorInfo> charsKeywords      = BuildCharsKeywords ();
+
+        private static Dictionary<string, System.Reflection.ConstructorInfo> BuildKeywords ()
+        {
+            Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
+
+            kws.Add ("default",  GetTokenCtor (typeof (TokenKwDefault)));
+            kws.Add ("do",       GetTokenCtor (typeof (TokenKwDo)));
+            kws.Add ("else",     GetTokenCtor (typeof (TokenKwElse)));
+            kws.Add ("float",    GetTokenCtor (typeof (TokenTypeFloat)));
+            kws.Add ("for",      GetTokenCtor (typeof (TokenKwFor)));
+            kws.Add ("if",       GetTokenCtor (typeof (TokenKwIf)));
+            kws.Add ("integer",  GetTokenCtor (typeof (TokenTypeInt)));
+            kws.Add ("list",     GetTokenCtor (typeof (TokenTypeList)));
+            kws.Add ("jump",     GetTokenCtor (typeof (TokenKwJump)));
+            kws.Add ("key",      GetTokenCtor (typeof (TokenTypeKey)));
+            kws.Add ("return",   GetTokenCtor (typeof (TokenKwRet)));
+            kws.Add ("rotation", GetTokenCtor (typeof (TokenTypeRot)));
+            kws.Add ("state",    GetTokenCtor (typeof (TokenKwState)));
+            kws.Add ("string",   GetTokenCtor (typeof (TokenTypeStr)));
+            kws.Add ("vector",   GetTokenCtor (typeof (TokenTypeVec)));
+            kws.Add ("while",    GetTokenCtor (typeof (TokenKwWhile)));
+
+            return kws;
+        }
+
+        private static Dictionary<string, System.Reflection.ConstructorInfo> BuildArrayKeywords ()
+        {
+            Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
+
+            kws.Add ("array",   GetTokenCtor (typeof (TokenTypeArray)));
+            kws.Add ("foreach", GetTokenCtor (typeof (TokenKwForEach)));
+            kws.Add ("in",      GetTokenCtor (typeof (TokenKwIn)));
+            kws.Add ("is",      GetTokenCtor (typeof (TokenKwIs)));
+            kws.Add ("object",  GetTokenCtor (typeof (TokenTypeObject)));
+            kws.Add ("undef",   GetTokenCtor (typeof (TokenKwUndef)));
+
+            return kws;
+        }
+
+        private static Dictionary<string, System.Reflection.ConstructorInfo> BuildAdvFlowCtlKeywords ()
+        {
+            Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
+
+            kws.Add ("break",    GetTokenCtor (typeof (TokenKwBreak)));
+            kws.Add ("case",     GetTokenCtor (typeof (TokenKwCase)));
+            kws.Add ("constant", GetTokenCtor (typeof (TokenKwConst)));
+            kws.Add ("continue", GetTokenCtor (typeof (TokenKwCont)));
+            kws.Add ("switch",   GetTokenCtor (typeof (TokenKwSwitch)));
+
+            return kws;
+        }
+
+        private static Dictionary<string, System.Reflection.ConstructorInfo> BuildTryCatchKeywords ()
+        {
+            Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
+
+            kws.Add ("catch",     GetTokenCtor (typeof (TokenKwCatch)));
+            kws.Add ("exception", GetTokenCtor (typeof (TokenTypeExc)));
+            kws.Add ("finally",   GetTokenCtor (typeof (TokenKwFinally)));
+            kws.Add ("throw",     GetTokenCtor (typeof (TokenKwThrow)));
+            kws.Add ("try",       GetTokenCtor (typeof (TokenKwTry)));
+
+            return kws;
+        }
+
+        private static Dictionary<string, System.Reflection.ConstructorInfo> BuildObjectsKeywords ()
+        {
+            Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
+
+            kws.Add ("abstract",    GetTokenCtor (typeof (TokenKwAbstract)));
+            kws.Add ("base",        GetTokenCtor (typeof (TokenKwBase)));
+            kws.Add ("class",       GetTokenCtor (typeof (TokenKwClass)));
+            kws.Add ("constructor", GetTokenCtor (typeof (TokenKwConstructor)));
+            kws.Add ("delegate",    GetTokenCtor (typeof (TokenKwDelegate)));
+            kws.Add ("destructor",  GetTokenCtor (typeof (TokenKwDestructor)));
+            kws.Add ("final",       GetTokenCtor (typeof (TokenKwFinal)));
+            kws.Add ("get",         GetTokenCtor (typeof (TokenKwGet)));
+            kws.Add ("interface",   GetTokenCtor (typeof (TokenKwInterface)));
+            kws.Add ("new",         GetTokenCtor (typeof (TokenKwNew)));
+            kws.Add ("override",    GetTokenCtor (typeof (TokenKwOverride)));
+            kws.Add ("partial",     GetTokenCtor (typeof (TokenKwPartial)));
+            kws.Add ("private",     GetTokenCtor (typeof (TokenKwPrivate)));
+            kws.Add ("protected",   GetTokenCtor (typeof (TokenKwProtected)));
+            kws.Add ("public",      GetTokenCtor (typeof (TokenKwPublic)));
+            kws.Add ("set",         GetTokenCtor (typeof (TokenKwSet)));
+            kws.Add ("static",      GetTokenCtor (typeof (TokenKwStatic)));
+            kws.Add ("this",        GetTokenCtor (typeof (TokenKwThis)));
+            kws.Add ("typedef",     GetTokenCtor (typeof (TokenKwTypedef)));
+            kws.Add ("virtual",     GetTokenCtor (typeof (TokenKwVirtual)));
+
+            return kws;
+        }
+
+        private static Dictionary<string, System.Reflection.ConstructorInfo> BuildCharsKeywords ()
+        {
+            Dictionary<string, System.Reflection.ConstructorInfo> kws = new Dictionary<string, System.Reflection.ConstructorInfo> ();
+
+            kws.Add ("char", GetTokenCtor (typeof (TokenTypeChar)));
+
+            return kws;
+        }
+
+        /**
+         * @brief Create a URL from a base URL and a relative string
+         * @param oldurl = base url string
+         * @param relurl = relative url
+         * @returns new url string
+         */
+        private static string CreateURL (string oldurl, string relurl)
+        {
+            if (relurl.IndexOf ("://") >= 0) return relurl;
+            StringBuilder newurl = new StringBuilder (oldurl.Length + relurl.Length);
+            if (relurl[0] == '/') {
+                // file:///oldname + /newname => file:///newname
+                // http://webserver.com/oldname + /newname => http://webserver.com/newname
+                int i = oldurl.IndexOf ("://") + 3;
+                int j = oldurl.IndexOf ('/', i);
+                if (j < 0) j = oldurl.Length;
+                newurl.Append (oldurl.Substring (0, j));
+                newurl.Append (relurl);
+            } else {
+                // file:///oldname + newname => file:///newname
+                // http://webserver.com/oldname + newname => http://webserver.com/newname
+                int i = oldurl.LastIndexOf ('/') + 1;
+                newurl.Append (oldurl.Substring (0, i));
+                newurl.Append (relurl);
+            }
+            return newurl.ToString ();
+        }
+
+        /**
+         * @brief Read source file from a webserver somewhere out there.
+         */
+        private const int MAX_INCLUDE_SIZE = 100000;
+        private Dictionary<string, string> scriptIncludes = new Dictionary<string, string> ();
+        private string ReadSourceFromURL (string url)
+        {
+            Stream stream = null;
+            StreamReader reader = null;
+
+            try {
+
+                /*
+                 * Get stream to read from webserver.
+                 */
+                stream = MMRWebRequest.MakeRequest ("GET", url, null, 0);
+                reader = new StreamReader (stream);
+
+                /*
+                 * Read file from stream.
+                 */
+                char[] buf = new char[4000];
+                int len = 0;
+                int total = 0;
+                List<char[]> fullBuffs = new List<char[]> ();
+                string signature = null;
+
+                while (true) {
+
+                    /*
+                     * Read a big chunk of characters.
+                     */
+                    len = reader.ReadBlock (buf, 0, buf.Length);
+
+                    /*
+                     * Signature is first line of the first chunk read and must be contained therein.
+                     * If an include file with the same signature has already been seen by this script, 
+                     * this include file is ignored.
+                     */
+                    if (signature == null) {
+                        signature   = new String (buf, 0, len);
+                        int siglen  = signature.IndexOf ('\n');
+                        if (siglen <= 0) {
+                            throw new Exception ("missing signature in first " + len + " characters: " + url);
+                        }
+                        signature   = signature.Substring (0, siglen);
+                        if (scriptIncludes.ContainsKey (signature)) return null;
+                        scriptIncludes.Add (signature, "");
+                    }
+
+                    /*
+                     * Signature is ok, stash full blocks away and keep reading.
+                     * If short read, means we have hit the end of the stream.
+                     */
+                    total += len;
+                    if (total > MAX_INCLUDE_SIZE) {
+                        throw new Exception ("script include exceeds maximum " + MAX_INCLUDE_SIZE + ": " + url);
+                    }
+                    if (len < buf.Length) break;
+                    fullBuffs.Add (buf);
+                    buf = new char[4000];
+                }
+
+                /*
+                 * Return the whole thing as one string.
+                 */
+                StringBuilder sb = new StringBuilder (total + url.Length + 20);
+                sb.Append ("# 1 \"");
+                sb.Append (url);
+                sb.Append ("\"\n");
+                foreach (char[] fullBuff in fullBuffs) {
+                    sb.Append (fullBuff);
+                }
+                sb.Append (buf, 0, len);
+                return sb.ToString ();
+            } finally {
+                     if (reader   != null) reader.Close ();
+                else if (stream   != null) stream.Close ();
+            }
+        }
+    }
+
+    /**
+     * @brief All output token types in addition to TokenBegin.
+     *        They are all sub-types of Token.
+     */
+
+    public class TokenChar : Token {
+        public char val;
+        public TokenChar (TokenErrorMessage emsg, string file, int line, int posn, char val) : base (emsg, file, line, posn)
+        {
+            this.val = val;
+        }
+        public TokenChar (Token original, char val) : base (original)
+        {
+            this.val = val;
+        }
+        public override string ToString ()
+        {
+            switch (val) {
+                case '\'': return "'\\''";
+                case '\\': return "'\\\\'";
+                case '\n': return "'\\n'";
+                case '\t': return "'\\t'";
+                default:   return "'" + val + "'";
+            }
+        }
+    }
+
+    public class TokenFloat : Token {
+        public double val;
+        public TokenFloat (TokenErrorMessage emsg, string file, int line, int posn, double val) : base (emsg, file, line, posn)
+        {
+            this.val = val;
+        }
+        public override string ToString ()
+        {
+            return val.ToString ();
+        }
+    }
+
+    public class TokenInt : Token {
+        public int val;
+        public TokenInt (TokenErrorMessage emsg, string file, int line, int posn, int val) : base (emsg, file, line, posn)
+        {
+            this.val = val;
+        }
+        public TokenInt (Token original, int val) : base (original)
+        {
+            this.val = val;
+        }
+        public override string ToString ()
+        {
+            return val.ToString ();
+        }
+    }
+
+    public class TokenName : Token {
+        public string val;
+        public TokenName (TokenErrorMessage emsg, string file, int line, int posn, string val) : base (emsg, file, line, posn)
+        {
+            this.val = val;
+        }
+        public TokenName (Token original, string val) : base (original)
+        {
+            this.val = val;
+        }
+        public override string ToString ()
+        {
+            return this.val;
+        }
+    }
+
+    public class TokenStr : Token {
+        public string val;
+        public TokenStr (TokenErrorMessage emsg, string file, int line, int posn, string val) : base (emsg, file, line, posn)
+        {
+            this.val = val;
+        }
+        public override string ToString ()
+        {
+            if ((val.IndexOf ('"') < 0) && 
+                (val.IndexOf ('\\') < 0) && 
+                (val.IndexOf ('\n') < 0) && 
+                (val.IndexOf ('\t') < 0)) return "\"" + val + "\"";
+
+            int len = val.Length;
+            StringBuilder sb = new StringBuilder (len * 2 + 2);
+            sb.Append ('"');
+            for (int i = 0; i < len; i ++) {
+                char c = val[i];
+                switch (c) {
+                    case '"': {
+                        sb.Append ('\\');
+                        sb.Append ('"');
+                        break;
+                    }
+                    case '\\': {
+                        sb.Append ('\\');
+                        sb.Append ('\\');
+                        break;
+                    }
+                    case '\n': {
+                        sb.Append ('\\');
+                        sb.Append ('n');
+                        break;
+                    }
+                    case '\t': {
+                        sb.Append ('\\');
+                        sb.Append ('t');
+                        break;
+                    }
+                    default: {
+                        sb.Append (c);
+                        break;
+                    }
+                }
+            }
+            return sb.ToString ();
+        }
+    }
+
+    /*
+     * This one marks the end-of-file.
+     */
+    public class TokenEnd : Token { public TokenEnd (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { } }
+
+    /*
+     * Various keywords and delimeters.
+     */
+    public delegate object TokenRValConstBinOpDelegate (object left, object right);
+    public delegate object TokenRValConstUnOpDelegate  (object right);
+
+    public class TokenKw : Token {
+        public TokenRValConstBinOpDelegate binOpConst;
+        public TokenRValConstUnOpDelegate  unOpConst;
+        public bool sdtClassOp;
+        public TokenKw (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenKw (Token original) : base (original)  { }
+    }
+
+    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 "..."; } }
+    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 "&&&"; } }
+    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 "|||"; } }
+    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 "<<="; } }
+    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 ">>="; } }
+    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 "<="; } }
+    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 ">="; } }
+    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 "=="; } }
+    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 "!="; } }
+    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 "++"; } }
+    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 "--"; } }
+    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 "&&"; } }
+    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 "||"; } }
+    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 "+="; } }
+    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 "&="; } }
+    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 "-="; } }
+    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 "*="; } }
+    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 "/="; } }
+    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 "%="; } }
+    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 "|="; } }
+    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 "^="; } }
+    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 "<<"; } }
+    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 ">>"; } }
+    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 "~"; } }
+    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 "!"; } }
+    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 "@"; } }
+    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 "%"; } }
+    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 "^"; } }
+    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 "&"; } }
+    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 "*"; } }
+    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 "("; } }
+    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 ")"; } }
+    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 "-"; } }
+    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 "+"; } }
+    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 "="; } }
+    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 "{"; } }
+    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 "}"; } }
+    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 "["; } }
+    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 "]"; } }
+    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 ";"; } }
+    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 ":"; } }
+    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 "<"; } }
+    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 ">"; } }
+    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 ","; } }
+    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 "."; } }
+    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 "?"; } }
+    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 "/"; } }
+    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 "|"; } }
+    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 "#"; } }
+
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+    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"; } }
+
+    /**
+     * @brief These static functions attempt to perform arithmetic on two constant
+     *        operands to generate the resultant constant.
+     *        Likewise for unary operators.
+     *
+     * @param left  = left-hand value
+     * @param right = right-hand value
+     * @returns null: not able to perform computation
+     *          else: resultant value object
+     *
+     * Note: it is ok for these to throw any exception (such as overflow or div-by-zero), 
+     *       and it will be treated as the 'not able to perform computation' case.
+     */
+    public class TokenRValConstOps {
+        public static object Null (object left, object right) { return null; }
+        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; }
+        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; }
+        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; }
+        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; }
+        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; }
+        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; }
+        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; }
+        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; }
+        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; }
+        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; }
+        public static object Null (object right) { return null; }
+        public static object Neg (object right) { if (right is int) { return - (int)right; } if (right is double) { return - (double)right; } return null; }
+        public static object Not (object right) { if (right is int) { return ~ (int)right; } return null; }
+    }
+
+    /*
+     * Various datatypes.
+     */
+    public abstract class TokenType : Token {
+
+        public TokenType (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenType (Token original) : base (original) { }
+
+        public static TokenType FromSysType (Token original, System.Type typ)
+        {
+            if (typ == typeof (LSL_List))     return new TokenTypeList      (original);
+            if (typ == typeof (LSL_Rotation)) return new TokenTypeRot       (original);
+            if (typ == typeof (void))         return new TokenTypeVoid      (original);
+            if (typ == typeof (LSL_Vector))   return new TokenTypeVec       (original);
+            if (typ == typeof (float))        return new TokenTypeFloat     (original);
+            if (typ == typeof (int))          return new TokenTypeInt       (original);
+            if (typ == typeof (string))       return new TokenTypeStr       (original);
+            if (typ == typeof (double))       return new TokenTypeFloat     (original);
+            if (typ == typeof (bool))         return new TokenTypeBool      (original);
+            if (typ == typeof (object))       return new TokenTypeObject    (original);
+            if (typ == typeof (XMR_Array))    return new TokenTypeArray     (original);
+            if (typ == typeof (LSL_Integer))  return new TokenTypeLSLInt    (original);
+            if (typ == typeof (LSL_Float))    return new TokenTypeLSLFloat  (original);
+            if (typ == typeof (LSL_String))   return new TokenTypeLSLString (original);
+            if (typ == typeof (char))         return new TokenTypeChar      (original);
+            if (typ == typeof (Exception))    return new TokenTypeExc       (original);
+
+            throw new Exception ("unknown script type " + typ.ToString ());
+        }
+
+        public static TokenType FromLSLType (Token original, string typ)
+        {
+            if (typ == "list")      return new TokenTypeList   (original);
+            if (typ == "rotation")  return new TokenTypeRot    (original);
+            if (typ == "vector")    return new TokenTypeVec    (original);
+            if (typ == "float")     return new TokenTypeFloat  (original);
+            if (typ == "integer")   return new TokenTypeInt    (original);
+            if (typ == "key")       return new TokenTypeKey    (original);
+            if (typ == "string")    return new TokenTypeStr    (original);
+            if (typ == "object")    return new TokenTypeObject (original);
+            if (typ == "array")     return new TokenTypeArray  (original);
+            if (typ == "bool")      return new TokenTypeBool   (original);
+            if (typ == "void")      return new TokenTypeVoid   (original);
+            if (typ == "char")      return new TokenTypeChar   (original);
+            if (typ == "exception") return new TokenTypeExc    (original);
+
+            throw new Exception ("unknown type " + typ);
+        }
+
+        /**
+         * @brief Estimate the number of bytes of memory taken by one of these
+         *        objects.  For objects with widely varying size, return the
+         *        smallest it can be.
+         */
+        public static int StaticSize (System.Type typ)
+        {
+            if (typ == typeof (LSL_List))     return  96;
+            if (typ == typeof (LSL_Rotation)) return  80;
+            if (typ == typeof (void))         return   0;
+            if (typ == typeof (LSL_Vector))   return  72;
+            if (typ == typeof (float))        return   8;
+            if (typ == typeof (int))          return   8;
+            if (typ == typeof (string))       return  40;
+            if (typ == typeof (double))       return   8;
+            if (typ == typeof (bool))         return   8;
+            if (typ == typeof (XMR_Array))    return  96;
+            if (typ == typeof (object))       return  32;
+            if (typ == typeof (char))         return   2;
+
+            if (typ == typeof (LSL_Integer))  return  32;
+            if (typ == typeof (LSL_Float))    return  32;
+            if (typ == typeof (LSL_String))   return  40;
+
+            throw new Exception ("unknown type " + typ.ToString ());
+        }
+
+        /**
+         * @brief Return the corresponding system type.
+         */
+        public abstract Type ToSysType ();
+
+        /**
+         * @brief Return the equivalent LSL wrapping type.
+         *
+         *  null: normal
+         *  else: LSL-style wrapping, ie, LSL_Integer, LSL_Float, LSL_String
+         *        ToSysType()=System.Int32;  lslWrapping=LSL_Integer
+         *        ToSysType()=System.Float;  lslWrapping=LSL_Float
+         *        ToSysType()=System.String; lslWrapping=LSL_String
+         */
+        public virtual Type ToLSLWrapType ()
+        {
+            return null;
+        }
+
+        /**
+         * @brief Assign slots in either the global variable arrays or the script-defined type instance arrays.
+         *        These only need to be implemented for script-visible types, ie, those that a script writer 
+         *        can actually define a variable as.
+         */
+        public virtual void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            throw new Exception ("not implemented for " + ToString () + " (" + GetType () + ")");
+        }
+
+        /**
+         * @brief Get heap tracking type.
+         */
+        public virtual Type ToHeapTrackerType ()
+        {
+            return null;
+        }
+        public virtual ConstructorInfo GetHeapTrackerCtor ()
+        {
+            return null;
+        }
+        public virtual MethodInfo GetHeapTrackerPopMeth ()
+        {
+            return null;
+        }
+        public virtual MethodInfo GetHeapTrackerPushMeth ()
+        {
+            return null;
+        }
+    }
+
+    public class TokenTypeArray : TokenType {
+        private static readonly FieldInfo iarArraysFieldInfo = typeof (XMRInstArrays).GetField ("iarArrays");
+
+        public TokenTypeArray (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeArray (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (XMR_Array); }
+        public override string ToString () { return "array"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarArraysFieldInfo;
+            declVar.vTableIndex = arSizes.iasArrays ++;
+        }
+    }
+    public class TokenTypeBool : TokenType {
+        public TokenTypeBool (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeBool (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (bool); }
+        public override string ToString () { return "bool"; }
+    }
+    public class TokenTypeChar : TokenType {
+        private static readonly FieldInfo iarCharsFieldInfo = typeof (XMRInstArrays).GetField ("iarChars");
+
+        public TokenTypeChar (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeChar (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (char); }
+        public override string ToString () { return "char"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarCharsFieldInfo;
+            declVar.vTableIndex = arSizes.iasChars ++;
+        }
+    }
+    public class TokenTypeExc : TokenType {
+        private static readonly FieldInfo iarObjectsFieldInfo = typeof (XMRInstArrays).GetField ("iarObjects");
+
+        public TokenTypeExc (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeExc (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (Exception); }
+        public override string ToString () { return "exception"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarObjectsFieldInfo;
+            declVar.vTableIndex = arSizes.iasObjects ++;
+        }
+    }
+    public class TokenTypeFloat : TokenType {
+        private static readonly FieldInfo iarFloatsFieldInfo = typeof (XMRInstArrays).GetField ("iarFloats");
+
+        public TokenTypeFloat (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeFloat (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (double); }
+        public override string ToString () { return "float"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarFloatsFieldInfo;
+            declVar.vTableIndex = arSizes.iasFloats ++;
+        }
+    }
+    public class TokenTypeInt : TokenType {
+        private static readonly FieldInfo iarIntegersFieldInfo = typeof (XMRInstArrays).GetField ("iarIntegers");
+
+        public TokenTypeInt (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeInt (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (int); }
+        public override string ToString () { return "integer"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarIntegersFieldInfo;
+            declVar.vTableIndex = arSizes.iasIntegers ++;
+        }
+    }
+    public class TokenTypeKey : TokenType {
+        private static readonly FieldInfo iarStringsFieldInfo = typeof (XMRInstArrays).GetField ("iarStrings");
+
+        public TokenTypeKey (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeKey (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (string); }
+        public override string ToString () { return "key"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarStringsFieldInfo;
+            declVar.vTableIndex = arSizes.iasStrings ++;
+        }
+    }
+    public class TokenTypeList : TokenType {
+        private static readonly FieldInfo iarListsFieldInfo = typeof (XMRInstArrays).GetField ("iarLists");
+        private static readonly ConstructorInfo htListCtor  = typeof (HeapTrackerList).GetConstructor (new Type [] { typeof (XMRInstAbstract) });
+        private static readonly MethodInfo htListPopMeth    = typeof (HeapTrackerList).GetMethod ("Pop", new Type[] { typeof (LSL_List) });
+        private static readonly MethodInfo htListPushMeth   = typeof (HeapTrackerList).GetMethod ("Push", new Type[0]);
+
+        public TokenTypeList (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeList (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (LSL_List); }
+        public override string ToString () { return "list"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarListsFieldInfo;
+            declVar.vTableIndex = arSizes.iasLists ++;
+        }
+        public override Type ToHeapTrackerType () { return typeof (HeapTrackerList); }
+        public override ConstructorInfo GetHeapTrackerCtor () { return htListCtor; }
+        public override MethodInfo GetHeapTrackerPopMeth ()   { return htListPopMeth; }
+        public override MethodInfo GetHeapTrackerPushMeth ()  { return htListPushMeth; }
+    }
+    public class TokenTypeObject : TokenType {
+        private static readonly FieldInfo iarObjectsFieldInfo = typeof (XMRInstArrays).GetField ("iarObjects");
+        private static readonly ConstructorInfo htObjectCtor  = typeof (HeapTrackerObject).GetConstructor (new Type [] { typeof (XMRInstAbstract) });
+        private static readonly MethodInfo htObjectPopMeth    = typeof (HeapTrackerObject).GetMethod ("Pop", new Type[] { typeof (object) });
+        private static readonly MethodInfo htObjectPushMeth   = typeof (HeapTrackerObject).GetMethod ("Push", new Type[0]);
+
+        public TokenTypeObject (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeObject (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (object); }
+        public override string ToString () { return "object"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarObjectsFieldInfo;
+            declVar.vTableIndex = arSizes.iasObjects ++;
+        }
+        public override Type ToHeapTrackerType () { return typeof (HeapTrackerObject); }
+        public override ConstructorInfo GetHeapTrackerCtor () { return htObjectCtor; }
+        public override MethodInfo GetHeapTrackerPopMeth ()   { return htObjectPopMeth; }
+        public override MethodInfo GetHeapTrackerPushMeth ()  { return htObjectPushMeth; }
+    }
+    public class TokenTypeRot : TokenType {
+        private static readonly FieldInfo iarRotationsFieldInfo = typeof (XMRInstArrays).GetField ("iarRotations");
+
+        public TokenTypeRot (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeRot (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (LSL_Rotation); }
+        public override string ToString () { return "rotation"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarRotationsFieldInfo;
+            declVar.vTableIndex = arSizes.iasRotations ++;
+        }
+    }
+    public class TokenTypeStr : TokenType {
+        private static readonly FieldInfo iarStringsFieldInfo = typeof (XMRInstArrays).GetField ("iarStrings");
+        private static readonly ConstructorInfo htStringCtor  = typeof (HeapTrackerString).GetConstructor (new Type [] { typeof (XMRInstAbstract) });
+        private static readonly MethodInfo htStringPopMeth    = typeof (HeapTrackerString).GetMethod ("Pop", new Type[] { typeof (string) });
+        private static readonly MethodInfo htStringPushMeth   = typeof (HeapTrackerString).GetMethod ("Push", new Type[0]);
+
+        public TokenTypeStr (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeStr (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (string); }
+        public override string ToString () { return "string"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarStringsFieldInfo;
+            declVar.vTableIndex = arSizes.iasStrings ++;
+        }
+        public override Type ToHeapTrackerType () { return typeof (HeapTrackerString); }
+        public override ConstructorInfo GetHeapTrackerCtor () { return htStringCtor; }
+        public override MethodInfo GetHeapTrackerPopMeth ()   { return htStringPopMeth; }
+        public override MethodInfo GetHeapTrackerPushMeth ()  { return htStringPushMeth; }
+    }
+    public class TokenTypeUndef : TokenType {  // for the 'undef' constant, ie, null object pointer
+        public TokenTypeUndef (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeUndef (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (object); }
+        public override string ToString () { return "undef"; }
+    }
+    public class TokenTypeVec : TokenType {
+        private static readonly FieldInfo iarVectorsFieldInfo = typeof (XMRInstArrays).GetField ("iarVectors");
+
+        public TokenTypeVec (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { } 
+        public TokenTypeVec (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (LSL_Vector); }
+        public override string ToString () { return "vector"; }
+        public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes arSizes)
+        {
+            declVar.vTableArray = iarVectorsFieldInfo;
+            declVar.vTableIndex = arSizes.iasVectors ++;
+        }
+    }
+    public class TokenTypeVoid : TokenType {  // used only for function/method return types
+        public TokenTypeVoid (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeVoid (Token original) : base (original) { }
+        public override Type ToSysType () { return typeof (void); }
+        public override string ToString () { return "void"; }
+    }
+
+    public class TokenTypeLSLFloat : TokenTypeFloat {
+        public TokenTypeLSLFloat (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeLSLFloat (Token original) : base (original) { }
+        public override Type ToLSLWrapType () { return typeof (LSL_Float); }
+    }
+    public class TokenTypeLSLInt : TokenTypeInt {
+        public TokenTypeLSLInt (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeLSLInt (Token original) : base (original) { }
+        public override Type ToLSLWrapType () { return typeof (LSL_Integer); }
+    }
+    public class TokenTypeLSLKey : TokenTypeKey {
+        public TokenTypeLSLKey (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeLSLKey (Token original) : base (original) { }
+        public override Type ToLSLWrapType () { return typeof (LSL_Key); }
+    }
+    public class TokenTypeLSLString : TokenTypeStr {
+        public TokenTypeLSLString (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
+        public TokenTypeLSLString (Token original) : base (original) { }
+        public override Type ToLSLWrapType () { return typeof (LSL_String); }
+    }
+}

+ 819 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTypeCast.cs

@@ -0,0 +1,819 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+/**
+ * @brief Generate script object code to perform type casting
+ */
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+
+    public class TypeCast {
+        private delegate void CastDelegate (IScriptCodeGen scg, Token errorAt);
+
+        private static ConstructorInfo floatConstructorStringInfo   = typeof (LSL_Float).GetConstructor (new Type[] { typeof (string) });
+        private static ConstructorInfo integerConstructorStringInfo = typeof (LSL_Integer).GetConstructor (new Type[] { typeof (string) });
+        private static ConstructorInfo lslFloatConstructorInfo      = typeof (LSL_Float).GetConstructor (new Type[] { typeof (double) });
+        private static ConstructorInfo lslIntegerConstructorInfo    = typeof (LSL_Integer).GetConstructor (new Type[] { typeof (int) });
+        private static ConstructorInfo lslStringConstructorInfo     = typeof (LSL_String).GetConstructor (new Type[] { typeof (string) });
+        private static ConstructorInfo rotationConstrucorStringInfo = typeof (LSL_Rotation).GetConstructor (new Type[] { typeof (string) });
+        private static ConstructorInfo vectorConstrucorStringInfo   = typeof (LSL_Vector).GetConstructor (new Type[] { typeof (string) });
+        private static FieldInfo  lslFloatValueFieldInfo     = typeof (LSL_Float).GetField ("value");
+        private static FieldInfo  lslIntegerValueFieldInfo   = typeof (LSL_Integer).GetField ("value");
+        private static FieldInfo  lslStringValueFieldInfo    = typeof (LSL_String).GetField ("m_string");
+        private static FieldInfo  sdtcITableFieldInfo        = typeof (XMRSDTypeClObj).GetField ("sdtcITable");
+        private static MethodInfo boolToListMethodInfo       = typeof (TypeCast).GetMethod ("BoolToList",        new Type[] { typeof (bool) });
+        private static MethodInfo boolToStringMethodInfo     = typeof (TypeCast).GetMethod ("BoolToString",      new Type[] { typeof (bool) });
+        private static MethodInfo charToStringMethodInfo     = typeof (TypeCast).GetMethod ("CharToString",      new Type[] { typeof (char) });
+        private static MethodInfo excToStringMethodInfo      = typeof (TypeCast).GetMethod ("ExceptionToString", new Type[] { typeof (Exception), typeof (XMRInstAbstract) });
+        private static MethodInfo floatToStringMethodInfo    = typeof (TypeCast).GetMethod ("FloatToString",     new Type[] { typeof (double) });
+        private static MethodInfo intToStringMethodInfo      = typeof (TypeCast).GetMethod ("IntegerToString",   new Type[] { typeof (int) });
+        private static MethodInfo keyToBoolMethodInfo        = typeof (TypeCast).GetMethod ("KeyToBool",         new Type[] { typeof (string) });
+        private static MethodInfo listToBoolMethodInfo       = typeof (TypeCast).GetMethod ("ListToBool",        new Type[] { typeof (LSL_List) });
+        private static MethodInfo listToStringMethodInfo     = typeof (TypeCast).GetMethod ("ListToString",      new Type[] { typeof (LSL_List) });
+        private static MethodInfo objectToFloatMethodInfo    = typeof (TypeCast).GetMethod ("ObjectToFloat",     new Type[] { typeof (object) });
+        private static MethodInfo objectToIntegerMethodInfo  = typeof (TypeCast).GetMethod ("ObjectToInteger",   new Type[] { typeof (object) });
+        private static MethodInfo objectToListMethodInfo     = typeof (TypeCast).GetMethod ("ObjectToList",      new Type[] { typeof (object) });
+        private static MethodInfo objectToRotationMethodInfo = typeof (TypeCast).GetMethod ("ObjectToRotation",  new Type[] { typeof (object) });
+        private static MethodInfo objectToStringMethodInfo   = typeof (TypeCast).GetMethod ("ObjectToString",    new Type[] { typeof (object) });
+        private static MethodInfo objectToVectorMethodInfo   = typeof (TypeCast).GetMethod ("ObjectToVector",    new Type[] { typeof (object) });
+        private static MethodInfo rotationToBoolMethodInfo   = typeof (TypeCast).GetMethod ("RotationToBool",    new Type[] { typeof (LSL_Rotation) });
+        private static MethodInfo rotationToStringMethodInfo = typeof (TypeCast).GetMethod ("RotationToString",  new Type[] { typeof (LSL_Rotation) });
+        private static MethodInfo stringToBoolMethodInfo     = typeof (TypeCast).GetMethod ("StringToBool",      new Type[] { typeof (string) });
+        private static MethodInfo vectorToBoolMethodInfo     = typeof (TypeCast).GetMethod ("VectorToBool",      new Type[] { typeof (LSL_Vector) });
+        private static MethodInfo vectorToStringMethodInfo   = typeof (TypeCast).GetMethod ("VectorToString",    new Type[] { typeof (LSL_Vector) });
+        private static MethodInfo sdTypeClassCastClass2ClassMethodInfo = typeof (XMRSDTypeClObj).GetMethod ("CastClass2Class", new Type[] { typeof (object),     typeof (int) });
+        private static MethodInfo sdTypeClassCastIFace2ClassMethodInfo = typeof (XMRSDTypeClObj).GetMethod ("CastIFace2Class", new Type[] { typeof (Delegate[]), typeof (int) });
+        private static MethodInfo sdTypeClassCastObj2IFaceMethodInfo   = typeof (XMRSDTypeClObj).GetMethod ("CastObj2IFace",   new Type[] { typeof (object),     typeof (string) });
+        private static MethodInfo charToListMethodInfo       = typeof (TypeCast).GetMethod ("CharToList",     new Type[] { typeof (char) });
+        private static MethodInfo excToListMethodInfo        = typeof (TypeCast).GetMethod ("ExcToList",      new Type[] { typeof (Exception) });
+        private static MethodInfo vectorToListMethodInfo     = typeof (TypeCast).GetMethod ("VectorToList",   new Type[] { typeof (LSL_Vector) });
+        private static MethodInfo floatToListMethodInfo      = typeof (TypeCast).GetMethod ("FloatToList",    new Type[] { typeof (double) });
+        private static MethodInfo integerToListMethodInfo    = typeof (TypeCast).GetMethod ("IntegerToList",  new Type[] { typeof (int) });
+        private static MethodInfo rotationToListMethodInfo   = typeof (TypeCast).GetMethod ("RotationToList", new Type[] { typeof (LSL_Rotation) });
+        private static MethodInfo stringToListMethodInfo     = typeof (TypeCast).GetMethod ("StringToList",   new Type[] { typeof (string) });
+
+        /*
+         * List of all allowed type casts and how to perform the casting.
+         */
+        private static Dictionary<string, CastDelegate> legalTypeCasts = CreateLegalTypeCasts ();
+
+        /**
+         * @brief create a dictionary of legal type casts.
+         * Defines what EXPLICIT type casts are allowed in addition to the IMPLICIT ones.
+         * Key is of the form <oldtype> <newtype> for IMPLICIT casting.
+         * Key is of the form <oldtype>*<newtype> for EXPLICIT casting.
+         * Value is a delegate that generates code to perform the type cast.
+         */
+        private static Dictionary<string, CastDelegate> CreateLegalTypeCasts ()
+        {
+            Dictionary<string, CastDelegate> ltc = new Dictionary<string, CastDelegate> ();
+
+            // IMPLICIT type casts (a space is in middle of the key)
+            // EXPLICIT type casts (an * is in middle of the key)
+            // In general, only mark explicit if it might throw an exception
+            ltc.Add ("array object",     TypeCastArray2Object);
+            ltc.Add ("bool float",       TypeCastBool2Float);
+            ltc.Add ("bool integer",     TypeCastBool2Integer);
+            ltc.Add ("bool list",        TypeCastBool2List);
+            ltc.Add ("bool object",      TypeCastBool2Object);
+            ltc.Add ("bool string",      TypeCastBool2String);
+            ltc.Add ("char integer",     TypeCastChar2Integer);
+            ltc.Add ("char list",        TypeCastChar2List);
+            ltc.Add ("char object",      TypeCastChar2Object);
+            ltc.Add ("char string",      TypeCastChar2String);
+            ltc.Add ("exception list",   TypeCastExc2List);
+            ltc.Add ("exception object", TypeCastExc2Object);
+            ltc.Add ("exception string", TypeCastExc2String);
+            ltc.Add ("float bool",       TypeCastFloat2Bool);
+            ltc.Add ("float integer",    TypeCastFloat2Integer);
+            ltc.Add ("float list",       TypeCastFloat2List);
+            ltc.Add ("float object",     TypeCastFloat2Object);
+            ltc.Add ("float string",     TypeCastFloat2String);
+            ltc.Add ("integer bool",     TypeCastInteger2Bool);
+            ltc.Add ("integer char",     TypeCastInteger2Char);
+            ltc.Add ("integer float",    TypeCastInteger2Float);
+            ltc.Add ("integer list",     TypeCastInteger2List);
+            ltc.Add ("integer object",   TypeCastInteger2Object);
+            ltc.Add ("integer string",   TypeCastInteger2String);
+            ltc.Add ("list bool",        TypeCastList2Bool);
+            ltc.Add ("list object",      TypeCastList2Object);
+            ltc.Add ("list string",      TypeCastList2String);
+            ltc.Add ("object*array",     TypeCastObject2Array);
+            ltc.Add ("object*bool",      TypeCastObject2Bool);
+            ltc.Add ("object*char",      TypeCastObject2Char);
+            ltc.Add ("object*exception", TypeCastObject2Exc);
+            ltc.Add ("object*float",     TypeCastObject2Float);
+            ltc.Add ("object*integer",   TypeCastObject2Integer);
+            ltc.Add ("object*list",      TypeCastObject2List);
+            ltc.Add ("object*rotation",  TypeCastObject2Rotation);
+            ltc.Add ("object string",    TypeCastObject2String);
+            ltc.Add ("object*vector",    TypeCastObject2Vector);
+            ltc.Add ("rotation bool",    TypeCastRotation2Bool);
+            ltc.Add ("rotation list",    TypeCastRotation2List);
+            ltc.Add ("rotation object",  TypeCastRotation2Object);
+            ltc.Add ("rotation string",  TypeCastRotation2String);
+            ltc.Add ("string bool",      TypeCastString2Bool);
+            ltc.Add ("string float",     TypeCastString2Float);
+            ltc.Add ("string integer",   TypeCastString2Integer);
+            ltc.Add ("string list",      TypeCastString2List);
+            ltc.Add ("string object",    TypeCastString2Object);
+            ltc.Add ("string rotation",  TypeCastString2Rotation);
+            ltc.Add ("string vector",    TypeCastString2Vector);
+            ltc.Add ("vector bool",      TypeCastVector2Bool);
+            ltc.Add ("vector list",      TypeCastVector2List);
+            ltc.Add ("vector object",    TypeCastVector2Object);
+            ltc.Add ("vector string",    TypeCastVector2String);
+
+            return ltc;
+        }
+
+        /**
+         * @brief See if the given type can be cast to the other implicitly.
+         * @param dstType = type being cast to
+         * @param srcType = type being cast from
+         * @returns false: implicit cast not allowed
+         *           true: implicit cast allowed
+         */
+        public static bool IsAssignableFrom (TokenType dstType, TokenType srcType)
+        {
+            /*
+             * Do a 'dry run' of the casting operation, discarding any emits and not printing any errors.
+             * But if the casting tries to print error(s), return false.
+             * Otherwise assume the cast is allowed and return true.
+             */
+            SCGIAF scg = new SCGIAF ();
+            scg.ok = true;
+            scg._ilGen = migiaf;
+            CastTopOfStack (scg, null, srcType, dstType, false);
+            return scg.ok;
+        }
+
+        private struct SCGIAF : IScriptCodeGen {
+            public bool ok;
+            public ScriptMyILGen _ilGen;
+
+            // IScriptCodeGen
+            public ScriptMyILGen ilGen { get { return _ilGen; } }
+            public void ErrorMsg (Token token, string message) { ok = false; }
+            public void PushDefaultValue (TokenType type) { }
+            public void PushXMRInst () { }
+        }
+
+        private static readonly MIGIAF migiaf = new MIGIAF ();
+        private struct MIGIAF : ScriptMyILGen {
+            // ScriptMyILGen
+            public string methName { get { return null; } }
+            public ScriptMyLocal DeclareLocal (Type type, string name) { return null; }
+            public ScriptMyLabel DefineLabel (string name) { return null; }
+            public void BeginExceptionBlock () { }
+            public void BeginCatchBlock (Type excType) { }
+            public void BeginFinallyBlock () { }
+            public void EndExceptionBlock () { }
+            public void Emit (Token errorAt, OpCode opcode) { }
+            public void Emit (Token errorAt, OpCode opcode, FieldInfo field) { }
+            public void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal) { }
+            public void Emit (Token errorAt, OpCode opcode, Type type) { }
+            public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel) { }
+            public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels) { }
+            public void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method) { }
+            public void Emit (Token errorAt, OpCode opcode, MethodInfo method) { }
+            public void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor) { }
+            public void Emit (Token errorAt, OpCode opcode, double value) { }
+            public void Emit (Token errorAt, OpCode opcode, float value) { }
+            public void Emit (Token errorAt, OpCode opcode, int value) { }
+            public void Emit (Token errorAt, OpCode opcode, string value) { }
+            public void MarkLabel (ScriptMyLabel myLabel) { }
+        }
+
+        /**
+         * @brief Emit code that converts the top stack item from 'oldType' to 'newType'
+         * @param scg = what script we are compiling
+         * @param errorAt = token used for source location for error messages
+         * @param oldType = type of item currently on the stack
+         * @param newType = type to convert it to
+         * @param explicitAllowed = false: only consider implicit casts
+         *                           true: consider both implicit and explicit casts
+         * @returns with code emitted for conversion (or error message output if not allowed, and stack left unchanged)
+         */
+        public static void CastTopOfStack (IScriptCodeGen scg, Token errorAt, TokenType oldType, TokenType newType, bool explicitAllowed)
+        {
+            CastDelegate castDelegate;
+            string oldString = oldType.ToString ();
+            string newString = newType.ToString ();
+
+            /*
+             * 'key' -> 'bool' is the only time we care about key being different than string.
+             */
+            if ((oldString == "key") && (newString == "bool")) {
+                LSLUnwrap (scg, errorAt, oldType);
+                scg.ilGen.Emit (errorAt, OpCodes.Call, keyToBoolMethodInfo);
+                LSLWrap (scg, errorAt, newType);
+                return;
+            }
+
+            /*
+             * Treat key and string as same type for all other type casts.
+             */
+            if (oldString == "key") oldString = "string";
+            if (newString == "key") newString = "string";
+
+            /*
+             * If the types are the same, there is no conceptual casting needed.
+             * However, there may be wraping/unwraping to/from the LSL wrappers.
+             */
+            if (oldString == newString) {
+                if (oldType.ToLSLWrapType () != newType.ToLSLWrapType ()) {
+                    LSLUnwrap (scg, errorAt, oldType);
+                    LSLWrap (scg, errorAt, newType);
+                }
+                return;
+            }
+
+            /*
+             * Script-defined classes can be cast up and down the tree.
+             */
+            if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeSDTypeClass)) {
+                TokenDeclSDTypeClass oldSDTC = ((TokenTypeSDTypeClass)oldType).decl;
+                TokenDeclSDTypeClass newSDTC = ((TokenTypeSDTypeClass)newType).decl;
+
+                // implicit cast allowed from leaf toward root
+                for (TokenDeclSDTypeClass sdtc = oldSDTC; sdtc != null; sdtc = sdtc.extends) {
+                    if (sdtc == newSDTC) return;
+                }
+
+                // explicit cast allowed from root toward leaf
+                for (TokenDeclSDTypeClass sdtc = newSDTC; sdtc != null; sdtc = sdtc.extends) {
+                    if (sdtc == oldSDTC) {
+                        ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
+                        scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, newSDTC.sdTypeIndex);
+                        scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastClass2ClassMethodInfo);
+                        return;
+                    }
+                }
+
+                // not on same branch
+                goto illcast;
+            }
+
+            /*
+             * One script-defined interface type cannot be cast to another script-defined interface type, 
+             * unless the old interface declares that it implements the new interface.  That proves that 
+             * the underlying object, no matter what type, implements the new interface.
+             */
+            if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeSDTypeInterface)) {
+                TokenDeclSDTypeInterface oldDecl = ((TokenTypeSDTypeInterface)oldType).decl;
+                TokenDeclSDTypeInterface newDecl = ((TokenTypeSDTypeInterface)newType).decl;
+                if (!oldDecl.Implements (newDecl)) goto illcast;
+                scg.ilGen.Emit (errorAt, OpCodes.Ldstr, newType.ToString ());
+                scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastObj2IFaceMethodInfo);
+                return;
+            }
+
+            /*
+             * A script-defined class type can be implicitly cast to a script-defined interface type that it 
+             * implements.  The result is an array of delegates that give the class's implementation of the 
+             * various methods defined by the interface.
+             */
+            if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeSDTypeInterface)) {
+                TokenDeclSDTypeClass oldSDTC = ((TokenTypeSDTypeClass)oldType).decl;
+                int intfIndex;
+                if (!oldSDTC.intfIndices.TryGetValue (newType.ToString (), out intfIndex)) goto illcast;
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, sdtcITableFieldInfo);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, intfIndex);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldelem, typeof (Delegate[]));
+                return;
+            }
+
+            /*
+             * A script-defined interface type can be explicitly cast to a script-defined class type by 
+             * extracting the Target property from element 0 of the delegate array that is the interface
+             * object and making sure it casts to the correct script-defined class type.
+             *
+             * But then only if the class type implements the interface type.
+             */
+            if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeSDTypeClass)) {
+                TokenTypeSDTypeInterface oldSDTI = (TokenTypeSDTypeInterface)oldType;
+                TokenTypeSDTypeClass     newSDTC = (TokenTypeSDTypeClass)    newType;
+
+                if (!newSDTC.decl.CanCastToIntf (oldSDTI.decl)) goto illcast;
+
+                ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, newSDTC.decl.sdTypeIndex);
+                scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastIFace2ClassMethodInfo);
+                return;
+            }
+
+            /*
+             * A script-defined interface type can be implicitly cast to object.
+             */
+            if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeObject)) {
+                return;
+            }
+
+            /*
+             * An object can be explicitly cast to a script-defined interface.
+             */
+            if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeInterface)) {
+                ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldstr, newString);
+                scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastObj2IFaceMethodInfo);
+                return;
+            }
+
+            /*
+             * Cast to void is always allowed, such as discarding value from 'i++' or function return value.
+             */
+            if (newType is TokenTypeVoid) {
+                scg.ilGen.Emit (errorAt, OpCodes.Pop);
+                return;
+            }
+
+            /*
+             * Cast from undef to object or script-defined type is always allowed.
+             */
+            if ((oldType is TokenTypeUndef) && 
+                ((newType is TokenTypeObject) || 
+                 (newType is TokenTypeSDTypeClass) || 
+                 (newType is TokenTypeSDTypeInterface))) {
+                return;
+            }
+
+            /*
+             * Script-defined classes can be implicitly cast to objects.
+             */
+            if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeObject)) {
+                return;
+            }
+
+            /*
+             * Script-defined classes can be explicitly cast from objects and other script-defined classes.
+             * Note that we must manually check that it is the correct SDTypeClass however because as far as 
+             * mono is concerned, all SDTypeClass's are the same.
+             */
+            if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeClass)) {
+                ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, ((TokenTypeSDTypeClass)newType).decl.sdTypeIndex);
+                scg.ilGen.Emit (errorAt, OpCodes.Call, sdTypeClassCastClass2ClassMethodInfo);
+                return;
+            }
+
+            /*
+             * Delegates can be implicitly cast to/from objects.
+             */
+            if ((oldType is TokenTypeSDTypeDelegate) && (newType is TokenTypeObject)) {
+                return;
+            }
+            if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeDelegate)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Castclass, newType.ToSysType ());
+                return;
+            }
+
+            /*
+             * Some actual conversion is needed, see if it is in table of legal casts.
+             */
+            string key = oldString + " " + newString;
+            if (!legalTypeCasts.TryGetValue (key, out castDelegate)) {
+                key = oldString + "*" + newString;
+                if (!legalTypeCasts.TryGetValue (key, out castDelegate)) goto illcast;
+                ExplCheck (scg, errorAt, explicitAllowed, oldString, newString);
+            }
+
+            /*
+             * Ok, output cast.  But make sure it is in native form without any LSL wrapping
+             * before passing to our casting routine.  Then if caller is expecting an LSL-
+             * wrapped value on the stack upon return, wrap it up after our casting.
+             */
+            LSLUnwrap (scg, errorAt, oldType);
+            castDelegate (scg, errorAt);
+            LSLWrap (scg, errorAt, newType);
+            return;
+
+        illcast:
+            scg.ErrorMsg (errorAt, "illegal to cast from " + oldString + " to " + newString);
+            if (!(oldType is TokenTypeVoid)) scg.ilGen.Emit (errorAt, OpCodes.Pop);
+            scg.PushDefaultValue (newType);
+        }
+        private static void ExplCheck (IScriptCodeGen scg, Token errorAt, bool explicitAllowed, string oldString, string newString)
+        {
+            if (!explicitAllowed) {
+                scg.ErrorMsg (errorAt, "must explicitly cast from " + oldString + " to " + newString);
+            }
+        }
+
+        /**
+         * @brief If value on the stack is an LSL-style wrapped value, unwrap it.
+         */
+        public static void LSLUnwrap (IScriptCodeGen scg, Token errorAt, TokenType type)
+        {
+            if (type.ToLSLWrapType () == typeof (LSL_Float)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslFloatValueFieldInfo);
+            }
+            if (type.ToLSLWrapType () == typeof (LSL_Integer)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslIntegerValueFieldInfo);
+            }
+            if (type.ToLSLWrapType () == typeof (LSL_String)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslStringValueFieldInfo);
+            }
+        }
+
+        /**
+         * @brief If caller wants the unwrapped value on stack wrapped LSL-style, wrap it.
+         */
+        private static void LSLWrap (IScriptCodeGen scg, Token errorAt, TokenType type)
+        {
+            if (type.ToLSLWrapType () == typeof (LSL_Float)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslFloatConstructorInfo);
+            }
+            if (type.ToLSLWrapType () == typeof (LSL_Integer)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslIntegerConstructorInfo);
+            }
+            if (type.ToLSLWrapType () == typeof (LSL_String)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Newobj, lslStringConstructorInfo);
+            }
+        }
+
+        /**
+         * @brief These routines output code to perform casting.
+         *        They can assume there are no LSL wrapped values on input
+         *        and they should not output an LSL wrapped value.
+         */
+        private static void TypeCastArray2Object (IScriptCodeGen scg, Token errorAt)
+        {
+        }
+        private static void TypeCastBool2Float (IScriptCodeGen scg, Token errorAt)
+        {
+            if (typeof (double) == typeof (float)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Conv_R4);
+            } else if (typeof (double) == typeof (double)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Conv_R8);
+            } else {
+                throw new Exception ("unknown type");
+            }
+        }
+        private static void TypeCastBool2Integer (IScriptCodeGen scg, Token errorAt)
+        {
+        }
+        private static void TypeCastBool2Object (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (bool));
+        }
+        private static void TypeCastChar2Integer (IScriptCodeGen scg, Token errorAt)
+        {
+        }
+        private static void TypeCastChar2List (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, charToListMethodInfo);
+        }
+        private static void TypeCastChar2Object (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (char));
+        }
+        private static void TypeCastChar2String (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, charToStringMethodInfo);
+        }
+        private static void TypeCastExc2List (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, excToListMethodInfo);
+        }
+        private static void TypeCastExc2Object (IScriptCodeGen scg, Token errorAt)
+        {
+        }
+        private static void TypeCastExc2String (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.PushXMRInst ();
+            scg.ilGen.Emit (errorAt, OpCodes.Call, excToStringMethodInfo);
+        }
+        private static void TypeCastFloat2Bool (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_R4, 0.0f);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+        }
+        private static void TypeCastFloat2Integer (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Conv_I4);
+        }
+        private static void TypeCastFloat2Object (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (double));
+        }
+        private static void TypeCastInteger2Bool (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
+            scg.ilGen.Emit (errorAt, OpCodes.Ceq);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_1);
+            scg.ilGen.Emit (errorAt, OpCodes.Xor);
+        }
+        private static void TypeCastInteger2Char (IScriptCodeGen scg, Token errorAt)
+        {
+        }
+        private static void TypeCastInteger2Float (IScriptCodeGen scg, Token errorAt)
+        {
+            if (typeof (double) == typeof (float)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Conv_R4);
+            } else if (typeof (double) == typeof (double)) {
+                scg.ilGen.Emit (errorAt, OpCodes.Conv_R8);
+            } else {
+                throw new Exception ("unknown type");
+            }
+        }
+        private static void TypeCastInteger2Object (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (int));
+        }
+        private static void TypeCastList2Bool (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, listToBoolMethodInfo);
+        }
+        private static void TypeCastList2Object (IScriptCodeGen scg, Token errorAt)
+        {
+            if (typeof (LSL_List).IsValueType) {
+                scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (LSL_List));
+            }
+        }
+        private static void TypeCastObject2Array (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Castclass, typeof (XMR_Array));
+        }
+        private static void TypeCastObject2Bool (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Unbox_Any, typeof (bool));
+        }
+        private static void TypeCastObject2Char (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Unbox_Any, typeof (char));
+        }
+        private static void TypeCastObject2Exc (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Castclass, typeof (Exception));
+        }
+        private static void TypeCastObject2Float (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, objectToFloatMethodInfo);
+        }
+        private static void TypeCastObject2Integer (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, objectToIntegerMethodInfo);
+        }
+        private static void TypeCastObject2List (IScriptCodeGen scg, Token errorAt)
+        {
+            if (typeof (LSL_List).IsValueType) {
+                scg.ilGen.Emit (errorAt, OpCodes.Call, objectToListMethodInfo);
+            } else {
+                scg.ilGen.Emit (errorAt, OpCodes.Castclass, typeof (LSL_List));
+            }
+        }
+        private static void TypeCastObject2Rotation (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, objectToRotationMethodInfo);
+        }
+        private static void TypeCastObject2Vector (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, objectToVectorMethodInfo);
+        }
+        private static void TypeCastRotation2Bool (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, rotationToBoolMethodInfo);
+        }
+        private static void TypeCastRotation2Object (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (LSL_Rotation));
+        }
+        private static void TypeCastString2Bool (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringToBoolMethodInfo);
+        }
+        private static void TypeCastString2Object (IScriptCodeGen scg, Token errorAt)
+        {
+        }
+        private static void TypeCastString2Rotation (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Newobj, rotationConstrucorStringInfo);
+        }
+        private static void TypeCastString2Vector (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Newobj, vectorConstrucorStringInfo);
+        }
+        private static void TypeCastVector2Bool (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, vectorToBoolMethodInfo);
+        }
+        private static void TypeCastVector2List (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, vectorToListMethodInfo);
+        }
+        private static void TypeCastVector2Object (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Box, typeof (LSL_Vector));
+        }
+        private static void TypeCastBool2List (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, boolToListMethodInfo);
+        }
+        private static void TypeCastBool2String (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, boolToStringMethodInfo);
+        }
+        private static void TypeCastFloat2List (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, floatToListMethodInfo);
+        }
+        private static void TypeCastFloat2String (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, floatToStringMethodInfo);
+        }
+        private static void TypeCastInteger2List (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, integerToListMethodInfo);
+        }
+        private static void TypeCastInteger2String (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, intToStringMethodInfo);
+        }
+        private static void TypeCastList2String (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, listToStringMethodInfo);
+        }
+        private static void TypeCastObject2String (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, objectToStringMethodInfo);
+        }
+        private static void TypeCastRotation2List (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, rotationToListMethodInfo);
+        }
+        private static void TypeCastRotation2String (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, rotationToStringMethodInfo);
+        }
+        private static void TypeCastString2Float (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Newobj, floatConstructorStringInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslFloatValueFieldInfo);
+        }
+        private static void TypeCastString2Integer (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Newobj, integerConstructorStringInfo);
+            scg.ilGen.Emit (errorAt, OpCodes.Ldfld, lslIntegerValueFieldInfo);
+        }
+        private static void TypeCastString2List (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, stringToListMethodInfo);
+        }
+        private static void TypeCastVector2String (IScriptCodeGen scg, Token errorAt)
+        {
+            scg.ilGen.Emit (errorAt, OpCodes.Call, vectorToStringMethodInfo);
+        }
+
+        /*
+         * Because the calls are funky, let the compiler handle them.
+         */
+        public static bool     RotationToBool    (LSL_Rotation x) { return !x.Equals (ScriptBaseClass.ZERO_ROTATION); }
+        public static bool     StringToBool      (string x)       { return x.Length > 0; }
+        public static bool     VectorToBool      (LSL_Vector x)   { return !x.Equals (ScriptBaseClass.ZERO_VECTOR); }
+        public static string   BoolToString      (bool x)         { return x ? "1" : "0"; }
+        public static string   CharToString      (char x)         { return x.ToString (); }
+        public static string   FloatToString     (double x)  { return x.ToString ("0.000000"); }
+        public static string   IntegerToString   (int x)          { return x.ToString (); }
+        public static bool     KeyToBool         (string x)       { return (x != "") && (x != ScriptBaseClass.NULL_KEY); }
+        public static bool     ListToBool        (LSL_List x)     { return x.Length != 0; }
+        public static string   ListToString      (LSL_List x)     { return x.ToString (); }
+        public static string   ObjectToString    (object x)       { return (x == null) ? null : x.ToString (); }
+        public static string   RotationToString  (LSL_Rotation x) { return x.ToString (); }
+        public static string   VectorToString    (LSL_Vector x)   { return x.ToString (); }
+        public static LSL_List BoolToList        (bool b)         { return new LSL_List (new object[] { new LSL_Integer (b ? 1 : 0) }); }
+        public static LSL_List CharToList        (char c)         { return new LSL_List (new object[] { new LSL_Integer (c) }); }
+        public static LSL_List ExcToList         (Exception e)    { return new LSL_List (new object[] { e }); }
+        public static LSL_List VectorToList      (LSL_Vector v)   { return new LSL_List (new object[] { v }); }
+        public static LSL_List FloatToList       (double f)  { return new LSL_List (new object[] { new LSL_Float   (f) }); }
+        public static LSL_List IntegerToList     (int i)          { return new LSL_List (new object[] { new LSL_Integer (i) }); }
+        public static LSL_List RotationToList    (LSL_Rotation r) { return new LSL_List (new object[] { r }); }
+        public static LSL_List StringToList      (string s)       { return new LSL_List (new object[] { new LSL_String  (s) }); }
+
+        public static double ObjectToFloat (object x)
+        {
+            if (x is LSL_String)  return double.Parse (((LSL_String)x).m_string);
+            if (x is string)      return double.Parse ((string)x);
+            if (x is LSL_Float)   return (double)(LSL_Float)x;
+            if (x is LSL_Integer) return (double)(int)(LSL_Integer)x;
+            if (x is int)         return (double)(int)x;
+            return (double)x;
+        }
+
+        public static int ObjectToInteger (object x)
+        {
+            if (x is LSL_String)  return int.Parse (((LSL_String)x).m_string);
+            if (x is string)      return int.Parse ((string)x);
+            if (x is LSL_Integer) return (int)(LSL_Integer)x;
+            return (int)x;
+        }
+
+        public static LSL_List ObjectToList (object x)
+        {
+            return (LSL_List)x;
+        }
+
+        public static LSL_Rotation ObjectToRotation (object x)
+        {
+            if (x is LSL_String) return new LSL_Rotation (((LSL_String)x).m_string);
+            if (x is string)     return new LSL_Rotation ((string)x);
+            return (LSL_Rotation)x;
+        }
+
+        public static LSL_Vector ObjectToVector (object x)
+        {
+            if (x is LSL_String) return new LSL_Vector (((LSL_String)x).m_string);
+            if (x is string)     return new LSL_Vector ((string)x);
+            return (LSL_Vector)x;
+        }
+
+        public static string ExceptionToString (Exception x, XMRInstAbstract inst)
+        {
+            return XMRInstAbstract.xmrExceptionTypeName (x) + ": " + XMRInstAbstract.xmrExceptionMessage (x) +
+                "\n" + inst.xmrExceptionStackTrace (x);
+        }
+
+        /*
+         * These are used by event handler entrypoints to remove any LSL wrapping
+         * from the argument list and return the unboxed/unwrapped value.
+         */
+        public static double EHArgUnwrapFloat (object x)
+        {
+            if (x is LSL_Float) return (double)(LSL_Float)x;
+            return (double)x;
+        }
+
+        public static int EHArgUnwrapInteger (object x)
+        {
+            if (x is LSL_Integer) return (int)(LSL_Integer)x;
+            return (int)x;
+        }
+ 
+        public static LSL_Rotation EHArgUnwrapRotation (object x)
+        {
+            if (x is OpenMetaverse.Quaternion) {
+                OpenMetaverse.Quaternion q = (OpenMetaverse.Quaternion)x;
+                return new LSL_Rotation(q.X, q.Y, q.Z, q.W);
+            }
+            return (LSL_Rotation)x;
+        }
+
+        public static string EHArgUnwrapString (object x)
+        {
+            if (x is LSL_Key)    return (string)(LSL_Key)x;
+            if (x is LSL_String) return (string)(LSL_String)x;
+            return (string)x;
+        }
+ 
+        public static LSL_Vector EHArgUnwrapVector (object x)
+        {
+            if (x is OpenMetaverse.Vector3) {
+                OpenMetaverse.Vector3 v = (OpenMetaverse.Vector3)x;
+                return new LSL_Vector(v.X, v.Y, v.Z);
+            }
+            return (LSL_Vector)x;
+        }
+    }
+}

+ 371 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRScriptVarDict.cs

@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+/**
+ * @brief Collection of variable/function/method definitions
+ */
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public class VarDict : IEnumerable {
+        public VarDict outerVarDict;            // next outer VarDict to search
+        public TokenDeclSDTypeClass thisClass;  // this VarDict is for members of thisClass
+
+        private struct ArgTypes {
+            public TokenType[] argTypes;
+
+            public bool CanBeCalledBy (TokenType[] calledBy)
+            {
+                if ((argTypes == null) && (calledBy == null)) return true;
+                if ((argTypes == null) || (calledBy == null)) return false;
+                if (argTypes.Length != calledBy.Length) return false;
+                for (int i = argTypes.Length; -- i >= 0;) {
+                    if (!TypeCast.IsAssignableFrom (argTypes[i], calledBy[i])) return false;
+                }
+                return true;
+            }
+
+            public override bool Equals (Object that)
+            {
+                if (that == null) return false;
+                if (that.GetType () != typeof (ArgTypes)) return false;
+                TokenType[] at = this.argTypes;
+                TokenType[] bt = ((ArgTypes)that).argTypes;
+                if ((at == null) && (bt == null)) return true;
+                if ((at == null) || (bt == null)) return false;
+                if (at.Length != bt.Length) return false;
+                for (int i = at.Length; -- i >= 0;) {
+                    if (at[i].ToString () != bt[i].ToString ()) return false;
+                }
+                return true;
+            }
+
+            public override int GetHashCode ()
+            {
+                TokenType[] at = this.argTypes;
+                if (at == null) return -1;
+                int hc = 0;
+                for (int i = at.Length; -- i >= 0;) {
+                    int c = (hc < 0) ? 1 : 0;
+                    hc  = hc * 2 + c;
+                    hc ^= at[i].ToString ().GetHashCode ();
+                }
+                return hc;
+            }
+        }
+
+        private struct TDVEntry {
+            public int count;
+            public TokenDeclVar var;
+        }
+
+        private bool isFrozen = false;
+        private bool locals;
+        private Dictionary<string, Dictionary<ArgTypes, TDVEntry>> master = new Dictionary<string, Dictionary<ArgTypes, TDVEntry>> ();
+        private int count = 0;
+        private VarDict frozenLocals = null;
+
+        /**
+         * @brief Constructor.
+         * @param locals = false: cannot be frozen, allows forward references
+         *                  true: can be frozen, thus forbidding forward references
+         */
+        public VarDict (bool locals)
+        {
+            this.locals = locals;
+        }
+
+        /**
+         * @brief Add new variable to the dictionary.
+         */
+        public bool AddEntry (TokenDeclVar var)
+        {
+            if (isFrozen) {
+                throw new Exception ("var dict is frozen");
+            }
+
+            /*
+             * Make sure we have a sub-dictionary based on the bare name (ie, no signature)
+             */
+            Dictionary<ArgTypes, TDVEntry> typedic;
+            if (!master.TryGetValue (var.name.val, out typedic)) {
+                typedic = new Dictionary<ArgTypes, TDVEntry> ();
+                master.Add (var.name.val, typedic);
+            }
+
+            /*
+             * See if there is an entry in the sub-dictionary that matches the argument signature.
+             * Note that fields have null argument lists.
+             * Methods always have a non-null argument list, even if only 0 entries long.
+             */
+            ArgTypes types;
+            types.argTypes = (var.argDecl == null) ? null : KeyTypesToStringTypes (var.argDecl.types);
+            if (typedic.ContainsKey (types)) return false;
+
+            /*
+             * It is unique, add to its name-specific sub-dictionary.
+             */
+            TDVEntry entry;
+            entry.count = ++ count;
+            entry.var   = var;
+            typedic.Add (types, entry);
+            return true;
+        }
+
+        public int Count { get { return count; } }
+
+        /**
+         * @brief If this is not a local variable frame, just return the frame as is.
+         *        If this is a local variable frame, return a version that is frozen,
+         *        ie, one that does not contain any future additions.
+         */
+        public VarDict FreezeLocals ()
+        {
+            /*
+             * If not local var frame, return original frame as is.
+             * This will allow forward references as the future additions
+             * will be seen by lookups done in this dictionary.
+             */
+            if (!locals) return this;
+
+            /*
+             * If local var frame, return a copy frozen at this point.
+             * This disallows forward referenes as those future additions
+             * will not be seen by lookups done in the frozen dictionary.
+             */
+            if ((frozenLocals == null) || (frozenLocals.count != this.count)) {
+
+                /*
+                 * Make a copy of the current var dictionary frame.
+                 * We copy a reference to the dictionary, and though it may
+                 * contain additions made after this point, those additions
+                 * will have a count .gt. frozen count and will be ignored.
+                 */
+                frozenLocals = new VarDict (true);
+
+                frozenLocals.outerVarDict = this.outerVarDict;
+                frozenLocals.thisClass    = this.thisClass;
+                frozenLocals.master       = this.master;
+                frozenLocals.count        = this.count;
+                frozenLocals.frozenLocals = frozenLocals;
+
+                /*
+                 * Mark it as being frozen.
+                 * - assert fail if any attempt is made to add to it
+                 * - ignore any additions to the dictionary with greater count
+                 */
+                frozenLocals.isFrozen = true;
+            }
+            return frozenLocals;
+        }
+
+        /**
+         * @brief Find all functions/variables that are callable
+         * @param name = name of function/variable to look for
+         * @param argTypes = the argument types the function is being called with
+         *                   null to look for a variable
+         * @returns null: no matching function/variable found
+         *          else: list of matching functions/variables
+         *                for variables, always of length 1
+         */
+        private List<TokenDeclVar> found = new List<TokenDeclVar> ();
+        public TokenDeclVar[] FindCallables (string name, TokenType[] argTypes)
+        {
+            argTypes = KeyTypesToStringTypes (argTypes);
+            TokenDeclVar var = FindExact (name, argTypes);
+            if (var != null) return new TokenDeclVar[] { var };
+
+            Dictionary<ArgTypes, TDVEntry> typedic;
+            if (!master.TryGetValue (name, out typedic)) return null;
+
+            found.Clear ();
+            foreach (KeyValuePair<ArgTypes, TDVEntry> kvp in typedic) {
+                if ((kvp.Value.count <= this.count) && kvp.Key.CanBeCalledBy (argTypes)) {
+                    found.Add (kvp.Value.var);
+                }
+            }
+            return (found.Count > 0) ? found.ToArray () : null;
+        }
+
+        /**
+         * @brief Find exact matching function/variable
+         * @param name = name of function to look for
+         * @param argTypes = argument types the function was declared with
+         *                   null to look for a variable
+         * @returns null: no matching function/variable found
+         *          else: the matching function/variable
+         */
+        public TokenDeclVar FindExact (string name, TokenType[] argTypes)
+        {
+            /*
+             * Look for list of stuff that matches the given name.
+             */
+            Dictionary<ArgTypes, TDVEntry> typedic;
+            if (!master.TryGetValue (name, out typedic)) return null;
+
+            /*
+             * Loop through all fields/methods declared by that name, regardless of arg signature.
+             */
+            foreach (TDVEntry entry in typedic.Values) {
+                if (entry.count > this.count) continue;
+                TokenDeclVar var = entry.var;
+
+                /*
+                 * Get argument types of declaration.
+                 *   fields are always null
+                 *   methods are always non-null, though may be zero-length
+                 */
+                TokenType[] declArgs = (var.argDecl == null) ? null : var.argDecl.types;
+
+                /*
+                 * Convert any key args to string args.
+                 */
+                declArgs = KeyTypesToStringTypes (declArgs);
+
+                /*
+                 * If both are null, they are signature-less (ie, both are fields), and so match.
+                 */
+                if ((declArgs == null) && (argTypes == null)) return var;
+
+                /*
+                 * If calling a delegate, it is a match, regardless of delegate arg types.
+                 * If it turns out the arg types do not match, the compiler will give an error
+                 * trying to cast the arguments to the delegate arg types.
+                 * We don't allow overloading same field name with different delegate types.
+                 */
+                if ((declArgs == null) && (argTypes != null)) {
+                    TokenType fieldType = var.type;
+                    if (fieldType is TokenTypeSDTypeDelegate) return var;
+                }
+
+                /*
+                 * If not both null, no match, keep looking.
+                 */
+                if ((declArgs == null) || (argTypes == null)) continue;
+
+                /*
+                 * Both not null, match argument types to make sure we have correct overload.
+                 */
+                int i = declArgs.Length;
+                if (i != argTypes.Length) continue;
+                while (-- i >= 0) {
+                    string da = declArgs[i].ToString ();
+                    string ga = argTypes[i].ToString ();
+                    if (da == "key") da = "string";
+                    if (ga == "key") ga = "string";
+                    if (da != ga) break;
+                }
+                if (i < 0) return var;
+            }
+
+            /*
+             * No match.
+             */
+            return null;
+        }
+
+        /**
+         * @brief Replace any TokenTypeKey elements with TokenTypeStr so that
+         *        it doesn't matter if functions are declared with key or string,
+         *        they will accept either.
+         * @param argTypes = argument types as declared in source code
+         * @returns argTypes with any key replaced by string
+         */
+        private static TokenType[] KeyTypesToStringTypes (TokenType[] argTypes)
+        {
+            if (argTypes != null) {
+                int i;
+                int nats = argTypes.Length;
+                for (i = nats; -- i >= 0;) {
+                    if (argTypes[i] is TokenTypeKey) break;
+                }
+                if (i >= 0) {
+                    TokenType[] at = new TokenType[nats];
+                    for (i = nats; -- i >= 0;) {
+                        at[i] = argTypes[i];
+                        if (argTypes[i] is TokenTypeKey) {
+                            at[i] = new TokenTypeStr (argTypes[i]);
+                        }
+                    }
+                    return at;
+                }
+            }
+            return argTypes;
+        }
+
+        // foreach goes through all the TokenDeclVars that were added
+
+        // IEnumerable
+        public IEnumerator GetEnumerator ()
+        {
+            return new VarDictEnumerator (this.master, this.count);
+        }
+
+        private class VarDictEnumerator : IEnumerator {
+            private IEnumerator masterEnum;
+            private IEnumerator typedicEnum;
+            private int count;
+
+            public VarDictEnumerator (Dictionary<string, Dictionary<ArgTypes, TDVEntry>> master, int count)
+            {
+                masterEnum = master.Values.GetEnumerator ();
+                this.count = count;
+            }
+
+            // IEnumerator
+            public void Reset ()
+            {
+                masterEnum.Reset ();
+                typedicEnum = null;
+            }
+
+            // IEnumerator
+            public bool MoveNext ()
+            {
+                while (true) {
+                    if (typedicEnum != null) {
+                        while (typedicEnum.MoveNext ()) {
+                            if (((TDVEntry)typedicEnum.Current).count <= this.count) return true;
+                        }
+                        typedicEnum = null;
+                    }
+                    if (!masterEnum.MoveNext ()) return false;
+                    Dictionary<ArgTypes, TDVEntry> ctd;
+                    ctd = (Dictionary<ArgTypes, TDVEntry>)masterEnum.Current;
+                    typedicEnum = ctd.Values.GetEnumerator ();
+                }
+            }
+
+            // IEnumerator
+            public object Current { get { return ((TDVEntry)typedicEnum.Current).var; } }
+        }
+    }
+}

+ 269 - 0
OpenSim/Region/ScriptEngine/XMREngine/MMRWebRequest.cs

@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @brief Perform web request
+ */
+
+using System;
+using System.IO;
+using System.Net;
+using System.Text;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+    public class MMRWebRequest {
+        public static bool allowFileURL = false;
+
+        public static Stream MakeRequest (string verb, string requestUrl, string obj, int timeoutms)
+        {
+            /*
+             * Pick apart the given URL and make sure we support it.
+             * For file:// URLs, just return a read-only stream of the file.
+             */
+            Uri uri = new Uri (requestUrl);
+            string supported = "http and https";
+            if (allowFileURL && (verb == "GET")) {
+                supported = "file, http and https";
+                if (uri.Scheme == "file") {
+                    return File.OpenRead (requestUrl.Substring (7));
+                }
+            }
+            bool https = uri.Scheme == "https";
+            if (!https && (uri.Scheme != "http")) {
+                throw new WebException ("only support " + supported + ", not " + uri.Scheme);
+            }
+            string host = uri.Host;
+            int port = uri.Port;
+            if (port < 0) port = https ? 443 : 80;
+            string path = uri.AbsolutePath;
+
+            /*
+             * Connect to the web server.
+             */
+            System.Net.Sockets.TcpClient tcpconnection = new System.Net.Sockets.TcpClient (host, port);
+            if (timeoutms > 0) {
+                tcpconnection.SendTimeout = timeoutms;
+                tcpconnection.ReceiveTimeout = timeoutms;
+            }
+
+            try {
+
+                /*
+                 * Get TCP stream to/from web server.
+                 * If HTTPS, wrap stream with SSL encryption.
+                 */
+                Stream tcpstream = tcpconnection.GetStream ();
+                if (https) {
+                    System.Net.Security.SslStream sslstream = new System.Net.Security.SslStream (tcpstream, false);
+                    sslstream.AuthenticateAsClient (host);
+                    tcpstream = sslstream;
+                }
+
+                /*
+                 * Write request header to the web server.
+                 * There might be some POST data as well to write to web server.
+                 */
+                WriteStream (tcpstream, verb + " " + path + " HTTP/1.1\r\n");
+                WriteStream (tcpstream, "Host: " + host + "\r\n");
+                if (obj != null) {
+                    byte[] bytes = Encoding.UTF8.GetBytes (obj);
+
+                    WriteStream (tcpstream, "Content-Length: " + bytes.Length + "\r\n");
+                    WriteStream (tcpstream, "Content-Type: application/x-www-form-urlencoded\r\n");
+                    WriteStream (tcpstream, "\r\n");
+                    tcpstream.Write (bytes, 0, bytes.Length);
+                } else {
+                    WriteStream (tcpstream, "\r\n");
+                }
+                tcpstream.Flush ();
+
+                /*
+                 * Check for successful reply status line.
+                 */
+                string headerline = ReadStreamLine (tcpstream).Trim ();
+                if (headerline != "HTTP/1.1 200 OK") throw new WebException ("status line " + headerline);
+
+                /*
+                 * Scan through header lines.
+                 * The only ones we care about are Content-Length and Transfer-Encoding.
+                 */
+                bool chunked = false;
+                int contentlength = -1;
+                while ((headerline = ReadStreamLine (tcpstream).Trim ().ToLowerInvariant ()) != "") {
+                    if (headerline.StartsWith ("content-length:")) {
+                        contentlength = int.Parse (headerline.Substring (15));
+                    }
+                    if (headerline.StartsWith ("transfer-encoding:") && (headerline.Substring (18).Trim () == "chunked")) {
+                        chunked = true;
+                    }
+                }
+
+                /*
+                 * Read response byte array as a series of chunks.
+                 */
+                if (chunked) {
+                    return new ChunkedStreamReader (tcpstream);
+                }
+
+                /*
+                 * Read response byte array with the exact length given by Content-Length.
+                 */
+                if (contentlength >= 0) {
+                    return new LengthStreamReader (tcpstream, contentlength);
+                }
+
+                /*
+                 * Don't know how it is being transferred.
+                 */
+                throw new WebException ("header missing content-length or transfer-encoding: chunked");
+            } catch {
+                tcpconnection.Close ();
+                throw;
+            }
+        }
+
+        /**
+         * @brief Write the string out as ASCII bytes.
+         */
+        private static void WriteStream (Stream stream, string line)
+        {
+            byte[] bytes = Encoding.ASCII.GetBytes (line);
+            stream.Write (bytes, 0, bytes.Length);
+        }
+
+        /**
+         * @brief Read the next text line from a stream.
+         * @returns string with \r\n trimmed off
+         */
+        private static string ReadStreamLine (Stream stream)
+        {
+            StringBuilder sb = new StringBuilder ();
+            while (true) {
+                int b = stream.ReadByte ();
+                if (b < 0) break;
+                if (b == '\n') break;
+                if (b == '\r') continue;
+                sb.Append ((char)b);
+            }
+            return sb.ToString ();
+        }
+
+        private class ChunkedStreamReader : Stream {
+            private int chunklen;
+            private Stream tcpstream;
+
+            public ChunkedStreamReader (Stream tcpstream)
+            {
+                this.tcpstream = tcpstream;
+            }
+
+            public override bool CanRead    { get { return true;  } }
+            public override bool CanSeek    { get { return false; } }
+            public override bool CanTimeout { get { return false; } }
+            public override bool CanWrite   { get { return false; } }
+            public override long Length     { get { return 0; } }
+            public override long Position   { get { return 0; } set { } }
+            public override void Flush ()   { }
+            public override long Seek (long offset, SeekOrigin origin) { return 0; }
+            public override void SetLength (long length) { }
+            public override void Write (byte[] buffer, int offset, int length) { }
+
+            public override int Read (byte[] buffer, int offset, int length)
+            {
+                if (length <= 0) return 0;
+
+                if (chunklen == 0) {
+                    chunklen = int.Parse (ReadStreamLine (tcpstream), System.Globalization.NumberStyles.HexNumber);
+                    if (chunklen < 0) throw new WebException ("negative chunk length");
+                    if (chunklen == 0) chunklen = -1;
+                }
+                if (chunklen < 0) return 0;
+
+                int maxread = (length < chunklen) ? length : chunklen;
+                int lenread = tcpstream.Read (buffer, offset, maxread);
+                chunklen -= lenread;
+                if (chunklen == 0) {
+                    int b = tcpstream.ReadByte ();
+                    if (b == '\r') b = tcpstream.ReadByte ();
+                    if (b != '\n') throw new WebException ("chunk not followed by \\r\\n");
+                }
+                return lenread;
+            }
+
+            public override void Close ()
+            {
+                chunklen = -1;
+                if (tcpstream != null) {
+                    tcpstream.Close ();
+                    tcpstream = null;
+                }
+            }
+        }
+
+        private class LengthStreamReader : Stream {
+            private int contentlength;
+            private Stream tcpstream;
+
+            public LengthStreamReader (Stream tcpstream, int contentlength)
+            {
+                this.tcpstream = tcpstream;
+                this.contentlength = contentlength;
+            }
+
+            public override bool CanRead    { get { return true;  } }
+            public override bool CanSeek    { get { return false; } }
+            public override bool CanTimeout { get { return false; } }
+            public override bool CanWrite   { get { return false; } }
+            public override long Length     { get { return 0; } }
+            public override long Position   { get { return 0; } set { } }
+            public override void Flush ()   { }
+            public override long Seek (long offset, SeekOrigin origin) { return 0; }
+            public override void SetLength (long length) { }
+            public override void Write (byte[] buffer, int offset, int length) { }
+
+            public override int Read (byte[] buffer, int offset, int length)
+            {
+                if (length <= 0) return 0;
+                if (contentlength <= 0) return 0;
+
+                int maxread = (length < contentlength) ? length : contentlength;
+                int lenread = tcpstream.Read (buffer, offset, maxread);
+                contentlength -= lenread;
+                return lenread;
+            }
+
+            public override void Close ()
+            {
+                contentlength = -1;
+                if (tcpstream != null) {
+                    tcpstream.Close ();
+                    tcpstream = null;
+                }
+            }
+        }
+    }
+}

+ 55 - 0
OpenSim/Region/ScriptEngine/XMREngine/MonoTaskletsDummy.cs

@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Used to build a dummy Mono.Tasklets.dll file when running on Windows
+// Will also work if running with mono, it will just not allow use of
+// the "con" and "mmr" thread models, only "sys" will work.
+
+using System;
+
+namespace Mono.Tasklets {
+    public class Continuation : IDisposable
+    {
+        public Continuation ()
+        {
+            throw new NotSupportedException ("'con' thread model requires mono");
+        }
+        public void Dispose ()
+        { }
+
+        public void Mark ()
+        { }
+
+        public int Store (int state)
+        {
+            return 0;
+        }
+
+        public void Restore (int state)
+        { }
+    }
+}

+ 534 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs

@@ -0,0 +1,534 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+// This class exists in the main app domain
+//
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    /**
+     * @brief Array objects.
+     */
+    public class XMR_Array {
+        private const int EMPTYHEAP = 64;
+        private const int ENTRYHEAP = 24;
+
+        private bool enumrValid;                              // true: enumr set to return array[arrayValid]
+                                                              // false: array[0..arrayValid-1] is all there is
+        private SortedDictionary<object, object> dnary;
+        private SortedDictionary<object, object>.Enumerator enumr;
+                                                              // enumerator used to fill 'array' past arrayValid to end of dictionary
+        private int arrayValid;                               // number of elements in 'array' that have been filled in
+        private KeyValuePair<object, object>[] array;         // list of kvp's that have been returned by ForEach() since last modification
+        private XMRInstAbstract inst;                         // script instance debited with heap use
+        private int heapUse;                                  // current heap use debit amount
+
+        public static TokenTypeSDTypeDelegate countDelegate = new TokenTypeSDTypeDelegate (new TokenTypeInt    (null), new TokenType[0]);
+        public static TokenTypeSDTypeDelegate clearDelegate = new TokenTypeSDTypeDelegate (new TokenTypeVoid   (null), new TokenType[0]);
+        public static TokenTypeSDTypeDelegate indexDelegate = new TokenTypeSDTypeDelegate (new TokenTypeObject (null), new TokenType[] { new TokenTypeInt (null) });
+        public static TokenTypeSDTypeDelegate valueDelegate = new TokenTypeSDTypeDelegate (new TokenTypeObject (null), new TokenType[] { new TokenTypeInt (null) });
+
+        public XMR_Array (XMRInstAbstract inst)
+        {
+            this.inst = inst;
+            dnary = new SortedDictionary<object, object> (XMRArrayKeyComparer.singleton);
+            heapUse = inst.UpdateHeapUse (0, EMPTYHEAP);
+        }
+
+        ~XMR_Array ()
+        {
+            heapUse = inst.UpdateHeapUse (heapUse, 0);
+        }
+
+        public static TokenType GetRValType (TokenName name)
+        {
+            if (name.val == "count") return new TokenTypeInt (name);
+            if (name.val == "clear") return clearDelegate;
+            if (name.val == "index") return indexDelegate;
+            if (name.val == "value") return valueDelegate;
+            return new TokenTypeVoid (name);
+        }
+
+        /**
+         * @brief Handle 'array[index]' syntax to get or set an element of the dictionary.
+         * Get returns null if element not defined, script sees type 'undef'.
+         * Setting an element to null removes it.
+         */
+        public object GetByKey(object key)
+        {
+            object val;
+            key = FixKey (key);
+            if (!dnary.TryGetValue (key, out val)) val = null;
+            return val;
+        }
+
+        public void SetByKey(object key, object value)
+        {
+            key = FixKey (key);
+
+            /*
+             * Update heap use throwing an exception on failure
+             * before making any changes to the array.
+             */
+            int keysize = HeapTrackerObject.Size (key);
+            int newheapuse = heapUse;
+            object oldval;
+            if (dnary.TryGetValue (key, out oldval)) {
+                newheapuse -= keysize + HeapTrackerObject.Size (oldval);
+            }
+            if (value != null) {
+                newheapuse += keysize + HeapTrackerObject.Size (value);
+            }
+            heapUse = inst.UpdateHeapUse (heapUse, newheapuse);
+
+            /*
+             * Save new value in array, replacing one of same key if there.
+             * null means remove the value, ie, script did array[key] = undef.
+             */
+            if (value != null) {
+                dnary[key] = value;
+            } else {
+                dnary.Remove (key);
+
+                /*
+                 * Shrink the enumeration array, but always leave at least one element.
+                 */
+                if ((array != null) && (dnary.Count < array.Length / 2)) {
+                    Array.Resize<KeyValuePair<object, object>> (ref array, array.Length / 2);
+                }
+            }
+
+            /*
+             * The enumeration array is invalid because the dictionary has been modified.
+             * Next time a ForEach() call happens, it will repopulate 'array' as elements are retrieved.
+             */
+            arrayValid = 0;
+        }
+
+        /**
+         * @brief Converts an 'object' type to array, key, list, string, but disallows null,
+         *        as our language doesn't allow types other than 'object' to be null.
+         *        Value types (float, rotation, etc) don't need explicit check for null as
+         *        the C# runtime can't convert a null to a value type, and throws an exception.
+         *        But for any reference type (array, key, etc) we must manually check for null.
+         */
+        public static XMR_Array Obj2Array (object obj)
+        {
+            if (obj == null) throw new NullReferenceException ();
+            return (XMR_Array)obj;
+        }
+        public static LSL_Key Obj2Key (object obj)
+        {
+            if (obj == null) throw new NullReferenceException ();
+            return (LSL_Key)obj;
+        }
+        public static LSL_List Obj2List (object obj)
+        {
+            if (obj == null) throw new NullReferenceException ();
+            return (LSL_List)obj;
+        }
+        public static LSL_String Obj2String (object obj)
+        {
+            if (obj == null) throw new NullReferenceException ();
+            return obj.ToString ();
+        }
+
+        /**
+         * @brief remove all elements from the array.
+         *        sets everything to its 'just constructed' state.
+         */
+        public void __pub_clear ()
+        {
+            heapUse = inst.UpdateHeapUse (heapUse, EMPTYHEAP);
+            dnary.Clear ();
+            enumrValid = false;
+            arrayValid = 0;
+            array      = null;
+        }
+
+        /**
+         * @brief return number of elements in the array.
+         */
+        public int __pub_count ()
+        {
+            return dnary.Count;
+        }
+
+        /**
+         * @brief Retrieve index (key) of an arbitrary element.
+         * @param number = number of the element (0 based)
+         * @returns null: array doesn't have that many elements
+         *          else: index (key) for that element
+         */
+        public object __pub_index (int number)
+        {
+            return ForEach (number) ? UnfixKey (array[number].Key) : null;
+        }
+
+        /**
+         * @brief Retrieve value of an arbitrary element.
+         * @param number = number of the element (0 based)
+         * @returns null: array doesn't have that many elements
+         *          else: value for that element
+         */
+        public object __pub_value (int number)
+        {
+            return ForEach (number) ? array[number].Value : null;
+        }
+
+        /**
+         * @brief Called in each iteration of a 'foreach' statement.
+         * @param number = index of element to retrieve (0 = first one)
+         * @returns false: element does not exist
+         *           true: element exists
+         */
+        private bool ForEach (int number)
+        {
+            /*
+             * If we don't have any array, we can't have ever done
+             * any calls here before, so allocate an array big enough
+             * and set everything else to the beginning.
+             */
+            if (array == null) {
+                array = new KeyValuePair<object, object>[dnary.Count];
+                arrayValid = 0;
+            }
+
+            /*
+             * If dictionary modified since last enumeration, get a new enumerator.
+             */
+            if (arrayValid == 0) {
+                enumr = dnary.GetEnumerator ();
+                enumrValid = true;
+            }
+
+            /*
+             * Make sure we have filled the array up enough for requested element.
+             */
+            while ((arrayValid <= number) && enumrValid && enumr.MoveNext ()) {
+                if (arrayValid >= array.Length) {
+                    Array.Resize<KeyValuePair<object, object>> (ref array, dnary.Count);
+                }
+                array[arrayValid++] = enumr.Current;
+            }
+
+            /*
+             * If we don't have that many elements, return end-of-array status.
+             */
+            return number < arrayValid;
+        }
+
+        /**
+         * @brief Transmit array out in such a way that it can be reconstructed,
+         *        including any in-progress ForEach() enumerations.
+         */
+        public delegate void SendArrayObjDelegate (object graph);
+        public void SendArrayObj (SendArrayObjDelegate sendObj)
+        {
+            /*
+             * Set the count then the elements themselves.
+             * UnfixKey() because sendObj doesn't handle XMRArrayListKeys.
+             */
+            sendObj (dnary.Count);
+            foreach (KeyValuePair<object, object> kvp in dnary) {
+                sendObj (UnfixKey (kvp.Key));
+                sendObj (kvp.Value);
+            }
+        }
+
+        /**
+         * @brief Receive array in.  Any previous contents are erased.
+         *        Set up such that any enumeration in progress will resume
+         *        at the exact spot and in the exact same order as they
+         *        were in on the sending side.
+         */
+        public delegate object RecvArrayObjDelegate ();
+        public void RecvArrayObj (RecvArrayObjDelegate recvObj)
+        {
+            heapUse = inst.UpdateHeapUse (heapUse, EMPTYHEAP);
+
+            /*
+             * Cause any enumeration to refill the array from the sorted dictionary.
+             * Since it is a sorted dictionary, any enumerations will be in the same 
+             * order as on the sending side.
+             */
+            arrayValid = 0;
+            enumrValid = false;
+
+            /*
+             * Fill dictionary.
+             */
+            dnary.Clear ();
+            int count = (int)recvObj ();
+            while (-- count >= 0) {
+                object key = FixKey (recvObj ());
+                object val = recvObj ();
+                int htuse = HeapTrackerObject.Size (key) + HeapTrackerObject.Size (val);
+                heapUse = inst.UpdateHeapUse (heapUse, heapUse + htuse);
+                dnary.Add (key, val);
+            }
+        }
+
+        /**
+         * We want our index values to be of consistent type, otherwise we get things like (LSL_Integer)1 != (int)1.
+         * So strip off any LSL-ness from the types.
+         * We also deep-strip any given lists used as keys (multi-dimensional arrays).
+         */
+        public static object FixKey (object key)
+        {
+            if (key is LSL_Integer) return (int)(LSL_Integer)key;
+            if (key is LSL_Float)   return (double)(LSL_Float)key;
+            if (key is LSL_Key)     return (string)(LSL_Key)key;
+            if (key is LSL_String)  return (string)(LSL_String)key;
+            if (key is LSL_List) {
+                object[] data = ((LSL_List)key).Data;
+                if (data.Length == 1) return FixKey (data[0]);
+                return new XMRArrayListKey ((LSL_List)key);
+            }
+            return key;  // int, double, string, LSL_Vector, LSL_Rotation, etc are ok as is
+        }
+
+        /**
+         * @brief When returning a key, such as for array.index(), we want to return the original
+         *        LSL_List, not the sanitized one, as the script compiler expects an LSL_List.
+         *        Any other sanitized types can remain as is (int, string, etc).
+         */
+        private static object UnfixKey (object key)
+        {
+            if (key is XMRArrayListKey) key = ((XMRArrayListKey)key).GetOriginal ();
+            return key;
+        }
+    }
+
+    public class XMRArrayKeyComparer : IComparer<object> {
+
+        public static XMRArrayKeyComparer singleton = new XMRArrayKeyComparer ();
+
+        /**
+         * @brief Compare two keys
+         */
+        public int Compare (object x, object y)  // IComparer<object>
+        {
+            /*
+             * Use short type name (eg, String, Int32, XMRArrayListKey) as most significant part of key.
+             */
+            string xtn = x.GetType ().Name;
+            string ytn = y.GetType ().Name;
+            int ctn = String.CompareOrdinal (xtn, ytn);
+            if (ctn != 0) return ctn;
+
+            ComparerDelegate cd;
+            if (!comparers.TryGetValue (xtn, out cd)) {
+                throw new Exception ("unsupported key type " + xtn);
+            }
+            return cd (x, y);
+        }
+
+        private delegate int ComparerDelegate (object a, object b);
+
+        private static Dictionary<string, ComparerDelegate> comparers = BuildComparers ();
+
+        private static Dictionary<string, ComparerDelegate> BuildComparers ()
+        {
+            Dictionary<string, ComparerDelegate> cmps = new Dictionary<string, ComparerDelegate> ();
+            cmps.Add (typeof (double).Name,     MyFloatComparer);
+            cmps.Add (typeof (int).Name,             MyIntComparer);
+            cmps.Add (typeof (XMRArrayListKey).Name, MyListKeyComparer);
+            cmps.Add (typeof (LSL_Rotation).Name,    MyRotationComparer);
+            cmps.Add (typeof (string).Name,          MyStringComparer);
+            cmps.Add (typeof (LSL_Vector).Name,      MyVectorComparer);
+            return cmps;
+        }
+
+        private static int MyFloatComparer (object a, object b)
+        {
+            double af = (double)a;
+            double bf = (double)b;
+            if (af < bf) return -1;
+            if (af > bf) return  1;
+            return 0;
+        }
+        private static int MyIntComparer (object a, object b)
+        {
+            return (int)a - (int)b;
+        }
+        private static int MyListKeyComparer (object a, object b)
+        {
+            XMRArrayListKey alk = (XMRArrayListKey)a;
+            XMRArrayListKey blk = (XMRArrayListKey)b;
+            return XMRArrayListKey.Compare (alk, blk);
+        }
+        private static int MyRotationComparer (object a, object b)
+        {
+            LSL_Rotation ar = (LSL_Rotation)a;
+            LSL_Rotation br = (LSL_Rotation)b;
+            if (ar.x < br.x) return -1;
+            if (ar.x > br.x) return  1;
+            if (ar.y < br.y) return -1;
+            if (ar.y > br.y) return  1;
+            if (ar.z < br.z) return -1;
+            if (ar.z > br.z) return  1;
+            if (ar.s < br.s) return -1;
+            if (ar.s > br.s) return  1;
+            return 0;
+        }
+        private static int MyStringComparer (object a, object b)
+        {
+            return String.CompareOrdinal ((string)a, (string)b);
+        }
+        private static int MyVectorComparer (object a, object b)
+        {
+            LSL_Vector av = (LSL_Vector)a;
+            LSL_Vector bv = (LSL_Vector)b;
+            if (av.x < bv.x) return -1;
+            if (av.x > bv.x) return  1;
+            if (av.y < bv.y) return -1;
+            if (av.y > bv.y) return  1;
+            if (av.z < bv.z) return -1;
+            if (av.z > bv.z) return  1;
+            return 0;
+        }
+    }
+
+    /**
+     * @brief Lists used as keys must be sanitized first.
+     *        List gets converted to an object[] and each element is converted from LSL_ types to system types where possible.
+     *        And we also need an equality operator that compares the values of all elements of the list, not just the lengths.
+     *        Note that just like LSL_Lists, we consider these objects to be immutable, so they can be directly used as keys in
+     *        the dictionary as they don't ever change.
+     */
+    public class XMRArrayListKey {
+        private LSL_List original;
+        private object[] cleaned;
+        private int length;
+        private int hashCode;
+
+        /**
+         * @brief Construct a sanitized object[] from a list.
+         *        Also save the original list in case we need it later.
+         */
+        public XMRArrayListKey (LSL_List key)
+        {
+            original = key;
+            object[] given = key.Data;
+            int len  = given.Length;
+            length   = len;
+            cleaned  = new object[len];
+            int hc   = len;
+            for (int i = 0; i < len; i ++) {
+                object v   = XMR_Array.FixKey (given[i]);
+                hc        += hc + ((hc < 0) ? 1 : 0);
+                hc        ^= v.GetHashCode ();
+                cleaned[i] = v;
+            }
+            hashCode = hc;
+        }
+
+        /**
+         * @brief Get heap tracking size.
+         */
+        public int Size {
+            get {
+                return original.Size;
+            }
+        }
+
+        /**
+         * @brief See if the given object is an XMRArrayListKey and every value is equal to our own.
+         */
+        public override bool Equals (object o)
+        {
+            if (!(o is XMRArrayListKey)) return false;
+            XMRArrayListKey a = (XMRArrayListKey)o;
+            int len = a.length;
+            if (len != length) return false;
+            if (a.hashCode != hashCode) return false;
+            for (int i = 0; i < len; i ++) {
+                if (!cleaned[i].Equals (a.cleaned[i])) return false;
+            }
+            return true;
+        }
+
+        /**
+         * @brief Get an hash code.
+         */
+        public override int GetHashCode ()
+        {
+            return hashCode;
+        }
+
+        /**
+         * @brief Compare for key sorting.
+         */
+        public static int Compare (XMRArrayListKey x, XMRArrayListKey y)
+        {
+            int j = x.length - y.length;
+            if (j == 0) {
+                for (int i = 0; i < x.length; i ++) {
+                    object xo = x.cleaned[i];
+                    object yo = y.cleaned[i];
+                    j = XMRArrayKeyComparer.singleton.Compare (xo, yo);
+                    if (j != 0) break;
+                }
+            }
+            return j;
+        }
+
+        /**
+         * @brief Get the original LSL_List we were built from.
+         */
+        public LSL_List GetOriginal ()
+        {
+            return original;
+        }
+
+        /**
+         * @brief Debugging
+         */
+        public override string ToString ()
+        {
+            StringBuilder sb = new StringBuilder ();
+            for (int i = 0; i < length; i ++) {
+                if (i > 0) sb.Append (',');
+                sb.Append (cleaned[i].ToString ());
+            }
+            return sb.ToString ();
+        }
+    }
+}

+ 491 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMREngXmrTestLs.cs

@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using log4net;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public partial class XMREngine {
+
+        private void XmrTestLs (string[] args, int indx)
+        {
+            bool flagFull   = false;
+            bool flagQueues = false;
+            bool flagTopCPU = false;
+            int maxScripts  = 0x7FFFFFFF;
+            int numScripts  = 0;
+            string outName  = null;
+            XMRInstance[] instances;
+
+            /*
+             * Decode command line options.
+             */
+            for (int i = indx; i < args.Length; i ++) {
+                if (args[i] == "-full") {
+                    flagFull = true;
+                    continue;
+                }
+                if (args[i] == "-help") {
+                    m_log.Info ("[XMREngine]: xmr ls -full -max=<number> -out=<filename> -queues -topcpu");
+                    return;
+                }
+                if (args[i].StartsWith("-max=")) {
+                    try {
+                        maxScripts = Convert.ToInt32(args[i].Substring(5));
+                    } catch (Exception e) {
+                        m_log.Error("[XMREngine]: bad max " + args[i].Substring(5) + ": " + e.Message);
+                        return;
+                    }
+                    continue;
+                }
+                if (args[i].StartsWith("-out=")) {
+                    outName = args[i].Substring(5);
+                    continue;
+                }
+                if (args[i] == "-queues") {
+                    flagQueues = true;
+                    continue;
+                }
+                if (args[i] == "-topcpu") {
+                    flagTopCPU = true;
+                    continue;
+                }
+                if (args[i][0] == '-') {
+                    m_log.Error("[XMREngine]: unknown option " + args[i] + ", try 'xmr ls -help'");
+                    return;
+                }
+            }
+
+            TextWriter outFile = null;
+            if (outName != null) {
+                try {
+                    outFile = File.CreateText(outName);
+                } catch (Exception e) {
+                    m_log.Error("[XMREngine]: error creating " + outName + ": " + e.Message);
+                    return;
+                }
+            } else {
+                outFile = new LogInfoTextWriter(m_log);
+            }
+
+            try {
+                for (int i = 0; i < numThreadScriptWorkers; i ++) {
+                    XMRScriptThread th = m_ScriptThreads[i];
+                    outFile.WriteLine("Script thread ID: " + th.m_ScriptThreadTID);
+                    long execTime = th.m_ScriptExecTime;
+                    if (execTime < 0) {
+                        execTime += (long)(DateTime.UtcNow - DateTime.MinValue).TotalMilliseconds;
+                    }
+                    outFile.WriteLine("  execution time: " + execTime + " mS");
+                    outFile.WriteLine("     last ran at: " + th.m_LastRanAt.ToString());
+                    XMRInstance rins = th.m_RunInstance;
+                    if (rins != null) {
+                        outFile.WriteLine("         running: " + rins.ItemID.ToString() + " " + rins.m_DescName);
+                        if (flagFull) {
+                            outFile.WriteLine (rins.RunTestLs (true));
+                        }
+                    }
+                }
+
+                /*
+                 * Scan instance list to find those that match selection criteria.
+                 */
+                if (!Monitor.TryEnter(m_InstancesDict, 100)) {
+                    m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
+                    return;
+                }
+                try
+                {
+                    instances = new XMRInstance[m_InstancesDict.Count];
+                    foreach (XMRInstance ins in m_InstancesDict.Values)
+                    {
+                        if (InstanceMatchesArgs(ins, args, indx)) {
+                            instances[numScripts++] = ins;
+                        }
+                    }
+                } finally {
+                    Monitor.Exit(m_InstancesDict);
+                }
+    
+                /*
+                 * Maybe sort by descending CPU time.
+                 */
+                if (flagTopCPU) {
+                    Array.Sort<XMRInstance>(instances, CompareInstancesByCPUTime);
+                }
+    
+                /*
+                 * Print the entries.
+                 */
+                if (!flagFull) {
+                    outFile.WriteLine("                              ItemID" + 
+                                      "   CPU(ms)" +
+                                      " NumEvents" +
+                                      " Status    " +
+                                      " World Position                  " +
+                                      " <Part>:<Item>");
+                }
+                for (int i = 0; (i < numScripts) && (i < maxScripts); i ++) {
+                    outFile.WriteLine(instances[i].RunTestLs(flagFull));
+                }
+
+                /*
+                 * Print number of scripts that match selection criteria,
+                 * even if we were told to print fewer.
+                 */
+                outFile.WriteLine("total of {0} script(s)", numScripts);
+
+                /*
+                 * If -queues given, print out queue contents too.
+                 */
+                if (flagQueues) {
+                    LsQueue(outFile, "start", m_StartQueue, args, indx);
+                    LsQueue(outFile, "sleep", m_SleepQueue, args, indx);
+                    LsQueue(outFile, "yield", m_YieldQueue, args, indx);
+                }
+            } finally {
+                outFile.Close();
+            }
+        }
+
+        private void XmrTestPev (string[] args, int indx)
+        {
+            bool flagAll = false;
+            int numScripts = 0;
+            XMRInstance[] instances;
+
+            /*
+             * Decode command line options.
+             */
+            int i, j;
+            List<string> selargs = new List<string> (args.Length);
+            MethodInfo[] eventmethods = typeof (IEventHandlers).GetMethods ();
+            MethodInfo eventmethod;
+            for (i = indx; i < args.Length; i ++) {
+                string arg = args[i];
+                if (arg == "-all") {
+                    flagAll = true;
+                    continue;
+                }
+                if (arg == "-help") {
+                    m_log.Info ("[XMREngine]: xmr pev -all | <part-of-script-name> <event-name> <params...>");
+                    return;
+                }
+                if (arg[0] == '-') {
+                    m_log.Error ("[XMREngine]: unknown option " + arg + ", try 'xmr pev -help'");
+                    return;
+                }
+                for (j = 0; j < eventmethods.Length; j ++) {
+                    eventmethod = eventmethods[j];
+                    if (eventmethod.Name == arg) goto gotevent;
+                }
+                selargs.Add (arg);
+            }
+            m_log.Error ("[XMREngine]: missing <event-name> <params...>, try 'xmr pev -help'");
+            return;
+        gotevent:
+            string eventname = eventmethod.Name;
+            StringBuilder sourcesb = new StringBuilder ();
+            while (++ i < args.Length) {
+                sourcesb.Append (' ');
+                sourcesb.Append (args[i]);
+            }
+            string sourcest = sourcesb.ToString ();
+            string sourcehash;
+            youveanerror = false;
+            Token t = TokenBegin.Construct ("", null, ErrorMsg, sourcest, out sourcehash);
+            if (youveanerror) return;
+            ParameterInfo[] paraminfos = eventmethod.GetParameters ();
+            object[] paramvalues = new object[paraminfos.Length];
+            i = 0;
+            while (!((t = t.nextToken) is TokenEnd)) {
+                if (i >= paramvalues.Length) {
+                    ErrorMsg (t, "extra parameter(s)");
+                    return;
+                }
+                paramvalues[i] = ParseParamValue (ref t);
+                if (paramvalues[i] == null) return;
+                i ++;
+            }
+            OpenSim.Region.ScriptEngine.Shared.EventParams eps = 
+                    new OpenSim.Region.ScriptEngine.Shared.EventParams (eventname, paramvalues, zeroDetectParams);
+
+            /*
+             * Scan instance list to find those that match selection criteria.
+             */
+            if (!Monitor.TryEnter(m_InstancesDict, 100)) {
+                m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
+                return;
+            }
+
+            try {
+                instances = new XMRInstance[m_InstancesDict.Count];
+                foreach (XMRInstance ins in m_InstancesDict.Values) {
+                    if (flagAll || InstanceMatchesArgs (ins, selargs.ToArray (), 0)) {
+                        instances[numScripts++] = ins;
+                    }
+                }
+            } finally {
+                Monitor.Exit(m_InstancesDict);
+            }
+    
+            /*
+             * Post event to the matching instances.
+             */
+            for (i = 0; i < numScripts; i ++) {
+                XMRInstance inst = instances[i];
+                m_log.Info ("[XMREngine]: post " + eventname + " to " + inst.m_DescName);
+                inst.PostEvent (eps);
+            }
+        }
+
+        private object ParseParamValue (ref Token token)
+        {
+            if (token is TokenFloat) {
+                return new LSL_Float (((TokenFloat)token).val);
+            }
+            if (token is TokenInt) {
+                return new LSL_Integer (((TokenInt)token).val);
+            }
+            if (token is TokenStr) {
+                return new LSL_String (((TokenStr)token).val);
+            }
+            if (token is TokenKwCmpLT) {
+                List<double> valuelist = new List<double> ();
+                while (!((token = token.nextToken) is TokenKwCmpGT)) {
+                    if (!(token is TokenKwComma)) {
+                        object value = ParseParamValue (ref token);
+                        if (value == null) return null;
+                        if (value is int) value = (double)(int)value;
+                        if (!(value is double)) {
+                            ErrorMsg (token, "must be float or integer constant");
+                            return null;
+                        }
+                        valuelist.Add ((double)value);
+                    } else if (token.prevToken is TokenKwComma) {
+                        ErrorMsg (token, "missing constant");
+                        return null;
+                    }
+                }
+                double[] values = valuelist.ToArray ();
+                switch (values.Length) {
+                    case 3: {
+                        return new LSL_Vector (values[0], values[1], values[2]);
+                    }
+                    case 4: {
+                        return new LSL_Rotation (values[0], values[1], values[2], values[3]);
+                    }
+                    default: {
+                        ErrorMsg (token, "not rotation or vector");
+                        return null;
+                    }
+                }
+            }
+            if (token is TokenKwBrkOpen) {
+                List<object> valuelist = new List<object> ();
+                while (!((token = token.nextToken) is TokenKwBrkClose)) {
+                    if (!(token is TokenKwComma)) {
+                        object value = ParseParamValue (ref token);
+                        if (value == null) return null;
+                        valuelist.Add (value);
+                    } else if (token.prevToken is TokenKwComma) {
+                        ErrorMsg (token, "missing constant");
+                        return null;
+                    }
+                }
+                return new LSL_List (valuelist.ToArray ());
+            }
+            if (token is TokenName) {
+                FieldInfo field = typeof (OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass).GetField (((TokenName)token).val);
+                if ((field != null) && field.IsPublic && (field.IsLiteral || (field.IsStatic && field.IsInitOnly))) {
+                    return field.GetValue (null);
+                }
+            }
+            ErrorMsg (token, "invalid constant");
+            return null;
+        }
+
+        private bool youveanerror;
+        private void ErrorMsg (Token token, string message)
+        {
+            youveanerror = true;
+            m_log.Info ("[XMREngine]: " + token.posn + " " + message);
+        }
+
+        private void XmrTestReset (string[] args, int indx)
+        {
+            bool flagAll = false;
+            int numScripts = 0;
+            XMRInstance[] instances;
+
+            if (args.Length <= indx) {
+                m_log.Error("[XMREngine]: must specify part of script name or -all for all scripts");
+                return;
+            }
+
+            /*
+             * Decode command line options.
+             */
+            for (int i = indx; i < args.Length; i ++) {
+                if (args[i] == "-all") {
+                    flagAll = true;
+                    continue;
+                }
+                if (args[i] == "-help") {
+                    m_log.Info ("[XMREngine]: xmr reset -all | <part-of-script-name>");
+                    return;
+                }
+                if (args[i][0] == '-') {
+                    m_log.Error ("[XMREngine]: unknown option " + args[i] + ", try 'xmr reset -help'");
+                    return;
+                }
+            }
+
+            /*
+             * Scan instance list to find those that match selection criteria.
+             */
+            if (!Monitor.TryEnter(m_InstancesDict, 100)) {
+                m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
+                return;
+            }
+
+            try {
+                instances = new XMRInstance[m_InstancesDict.Count];
+                foreach (XMRInstance ins in m_InstancesDict.Values) {
+                    if (flagAll || InstanceMatchesArgs (ins, args, indx)) {
+                        instances[numScripts++] = ins;
+                    }
+                }
+            } finally {
+                Monitor.Exit(m_InstancesDict);
+            }
+    
+            /*
+             * Reset the instances as if someone clicked their "Reset" button.
+             */
+            for (int i = 0; i < numScripts; i ++) {
+                XMRInstance inst = instances[i];
+                m_log.Info ("[XMREngine]: resetting " + inst.m_DescName);
+                inst.Reset();
+            }
+        }
+
+        private static int CompareInstancesByCPUTime(XMRInstance a, XMRInstance b)
+        {
+            if (a == null) {
+                return (b == null) ? 0 : 1;
+            }
+            if (b == null) {
+                return -1;
+            }
+            if (b.m_CPUTime < a.m_CPUTime) return -1;
+            if (b.m_CPUTime > a.m_CPUTime) return  1;
+            return 0;
+        }
+
+        private void LsQueue(TextWriter outFile, string name, XMRInstQueue queue, string[] args, int indx)
+        {
+            outFile.WriteLine("Queue " + name + ":");
+            lock (queue) {
+                for (XMRInstance inst = queue.PeekHead(); inst != null; inst = inst.m_NextInst) {
+                    try {
+
+                        /*
+                         * Try to print instance name.
+                         */
+                        if (InstanceMatchesArgs(inst, args, indx)) {
+                            outFile.WriteLine("   " + inst.ItemID.ToString() + " " + inst.m_DescName);
+                        }
+                    } catch (Exception e) {
+
+                        /*
+                         * Sometimes there are instances in the queue that are disposed.
+                         */
+                        outFile.WriteLine("   " + inst.ItemID.ToString() + " " + inst.m_DescName + ": " + e.Message);
+                    }
+                }
+            }
+        }
+
+        private bool InstanceMatchesArgs(XMRInstance ins, string[] args, int indx)
+        {
+            bool hadSomethingToCompare = false;
+
+            for (int i = indx; i < args.Length; i ++)
+            {
+                if (args[i][0] != '-') {
+                    hadSomethingToCompare = true;
+                    if (ins.m_DescName.Contains(args[i])) return true;
+                    if (ins.ItemID.ToString().Contains(args[i])) return true;
+                    if (ins.AssetID.ToString().Contains(args[i])) return true;
+                }
+            }
+            return !hadSomethingToCompare;
+        }
+    }
+
+    /**
+     * @brief Make m_log.Info look like a text writer.
+     */
+    public class LogInfoTextWriter : TextWriter {
+        private StringBuilder sb = new StringBuilder();
+        private ILog m_log;
+        public LogInfoTextWriter (ILog m_log)
+        {
+            this.m_log = m_log;
+        }
+        public override void Write (char c)
+        {
+            if (c == '\n') {
+                m_log.Info("[XMREngine]: " + sb.ToString());
+                sb.Remove(0, sb.Length);
+            } else {
+                sb.Append(c);
+            }
+        }
+        public override void Close () { }
+        public override Encoding Encoding {
+            get {
+                return Encoding.UTF8;
+            }
+        }
+    }
+}

+ 1979 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMREngine.cs

@@ -0,0 +1,1979 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using log4net;
+using Mono.Addins;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Framework.Console;
+using OpenSim.Framework.Monitoring;
+using OpenSim.Region.ClientStack.Linden;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.ScriptEngine.Interfaces;
+using OpenSim.Region.ScriptEngine.Shared;
+using OpenSim.Region.ScriptEngine.Shared.Api;
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenMetaverse;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using System.Threading;
+using System.Timers;
+using System.Xml;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+[assembly: Addin("XMREngine", OpenSim.VersionInfo.VersionNumber)]
+[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "XMREngine")]
+    public partial class XMREngine : INonSharedRegionModule, IScriptEngine,
+            IScriptModule
+    {
+        public  static readonly DetectParams[] zeroDetectParams = new DetectParams[0];
+        private static ArrayList noScriptErrors = new ArrayList();
+        public  static readonly ILog m_log =
+            LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+        private static readonly string[] scriptReferencedAssemblies = new string[0];
+
+        private bool m_LateInit;
+        private bool m_TraceCalls;
+        public  bool m_Verbose;
+        public  bool m_ScriptDebug;
+        public  Scene m_Scene;
+        private IConfigSource m_ConfigSource;
+        private IConfig m_Config;
+        private string m_ScriptBasePath;
+        private bool m_Enabled = false;
+        public  bool m_StartProcessing = false;
+        public  bool m_UseSourceHashCode = false;
+        public  ConstructorInfo uThreadCtor;
+        private Dictionary<UUID, ArrayList> m_ScriptErrors =
+                new Dictionary<UUID, ArrayList>();
+        private Dictionary<UUID, List<UUID>> m_ObjectItemList =
+                new Dictionary<UUID, List<UUID>>();
+        private Dictionary<UUID, XMRInstance[]> m_ObjectInstArray =
+                new Dictionary<UUID, XMRInstance[]>();
+        public  Dictionary<string,FieldInfo> m_XMRInstanceApiCtxFieldInfos =
+                new Dictionary<string,FieldInfo> ();
+        private int m_StackSize;
+        private int m_HeapSize;
+        private XMRScriptThread[] m_ScriptThreads;
+        private Thread m_SleepThread = null;
+        private Thread m_SliceThread = null;
+        private bool m_Exiting = false;
+
+        private int m_MaintenanceInterval = 10;
+        private System.Timers.Timer m_MaintenanceTimer;
+        public  int numThreadScriptWorkers;
+
+        private object m_FrameUpdateLock = new object ();
+        private event ThreadStart m_FrameUpdateList = null;
+
+        /*
+         * Various instance lists:
+         *   m_InstancesDict = all known instances
+         *                     find an instance given its itemID
+         *   m_StartQueue = instances that have just had event queued to them
+         *   m_YieldQueue = instances that are ready to run right now
+         *   m_SleepQueue = instances that have m_SleepUntil valid
+         *                  sorted by ascending m_SleepUntil
+         */
+        private Dictionary<UUID, XMRInstance> m_InstancesDict =
+                new Dictionary<UUID, XMRInstance>();
+        public  Queue<ThreadStart> m_ThunkQueue = new Queue<ThreadStart> ();
+        public  XMRInstQueue m_StartQueue = new XMRInstQueue();
+        public  XMRInstQueue m_YieldQueue = new XMRInstQueue();
+        public  XMRInstQueue m_SleepQueue = new XMRInstQueue();
+        private string m_LockedDict = "nobody";
+
+        public XMREngine()
+        {
+            string envar;
+
+            envar = Environment.GetEnvironmentVariable ("XMREngineTraceCalls");
+            m_TraceCalls  = (envar != null) && ((envar[0] & 1) != 0);
+            m_log.Info ("[XMREngine]: m_TraceCalls=" + m_TraceCalls);
+
+            envar = Environment.GetEnvironmentVariable ("XMREngineVerbose");
+            m_Verbose     = (envar != null) && ((envar[0] & 1) != 0);
+            m_log.Info ("[XMREngine]: m_Verbose=" + m_Verbose);
+
+            envar = Environment.GetEnvironmentVariable ("XMREngineScriptDebug");
+            m_ScriptDebug = (envar != null) && ((envar[0] & 1) != 0);
+            m_log.Info ("[XMREngine]: m_ScriptDebug=" + m_ScriptDebug);
+        }
+
+        public string Name
+        {
+            get { return "XMREngine"; }
+        }
+
+        public Type ReplaceableInterface
+        {
+            get { return null; }
+        }
+
+        public string ScriptEnginePath
+        {
+            get { return m_ScriptBasePath; }
+        }
+
+        public string ScriptClassName
+        {
+            get { return "XMREngineScript"; }
+        }
+
+        public string ScriptBaseClassName
+        {
+            get { return typeof (XMRInstance).FullName; }
+        }
+
+        public ParameterInfo[] ScriptBaseClassParameters
+        {
+            get { return typeof(XMRInstance).GetConstructor (new Type[] { typeof (WaitHandle) }).GetParameters (); }
+        }
+
+        public string[] ScriptReferencedAssemblies
+        {
+            get { return scriptReferencedAssemblies; }
+        }
+
+        public void Initialise(IConfigSource config)
+        {
+            TraceCalls("[XMREngine]: Initialize entry");
+            m_ConfigSource = config;
+
+            ////foreach (IConfig icfg in config.Configs) {
+            ////    m_log.Debug("[XMREngine]: Initialise: configs[" + icfg.Name + "]");
+            ////    foreach (string key in icfg.GetKeys ()) {
+            ////        m_log.Debug("[XMREngine]: Initialise:     " + key + "=" + icfg.GetExpanded (key));
+            ////    }
+            ////}
+
+            m_Enabled = false;
+            m_Config = config.Configs["XMREngine"];
+            if (m_Config == null) {
+                m_log.Info("[XMREngine]: no config, assuming disabled");
+                return;
+            }
+            m_Enabled = m_Config.GetBoolean("Enabled", false);
+            m_log.InfoFormat("[XMREngine]: config enabled={0}", m_Enabled);
+            if (!m_Enabled) {
+                return;
+            }
+
+            string uThreadModel = "sys";  // will work anywhere
+            uThreadModel = m_Config.GetString ("UThreadModel", uThreadModel);
+
+            Type uThreadType = null;
+            switch (uThreadModel.ToLower ()) {
+
+                // mono continuations - memcpy()s the stack
+                case "con": {
+                    uThreadType = typeof (ScriptUThread_Con);
+                    break;
+                }
+
+                // patched mono microthreads - switches stack pointer
+                case "mmr": {
+                    Exception e = ScriptUThread_MMR.LoadMono ();
+                    if (e != null) {
+                        m_log.Error ("[XMREngine]: mmr thread model not available\n", e);
+                        m_Enabled = false;
+                        return;
+                    }
+                    uThreadType = typeof (ScriptUThread_MMR);
+                    break;
+                }
+
+                // system threads - works on mono and windows
+                case "sys": {
+                    uThreadType = typeof (ScriptUThread_Sys);
+                    break;
+                }
+
+                // who knows what
+                default: {
+                    m_log.Error ("[XMREngine]: unknown thread model " + uThreadModel);
+                    m_Enabled = false;
+                    return;
+                }
+            }
+
+            uThreadCtor = uThreadType.GetConstructor (new Type[] { typeof (XMRInstance) });
+            m_log.Info ("[XMREngine]: using thread model " + uThreadModel);
+
+            m_UseSourceHashCode    = m_Config.GetBoolean ("UseSourceHashCode", false);
+            numThreadScriptWorkers = m_Config.GetInt ("NumThreadScriptWorkers", 1);
+            m_ScriptThreads        = new XMRScriptThread[numThreadScriptWorkers];
+
+            for (int i = 0; i < numThreadScriptWorkers; i ++) {
+                m_ScriptThreads[i] = new XMRScriptThread(this);
+            }
+
+            m_SleepThread = StartMyThread (RunSleepThread, "xmrengine sleep", ThreadPriority.Normal);
+            m_SliceThread = StartMyThread (RunSliceThread, "xmrengine slice", ThreadPriority.Normal);
+
+            /*
+             * Verify that our ScriptEventCode's match OpenSim's scriptEvent's.
+             */
+            bool err = false;
+            for (int i = 0; i < 32; i ++) {
+                string mycode = "undefined";
+                string oscode = "undefined";
+                try {
+                    mycode = ((ScriptEventCode)i).ToString();
+                    Convert.ToInt32(mycode);
+                    mycode = "undefined";
+                } catch {
+                }
+                try {
+                    oscode = ((OpenSim.Region.Framework.Scenes.scriptEvents)(1 << i)).ToString();
+                    Convert.ToInt32(oscode);
+                    oscode = "undefined";
+                } catch {
+                }
+                if (mycode != oscode) {
+                    m_log.ErrorFormat("[XMREngine]: {0} mycode={1}, oscode={2}", i, mycode, oscode);
+                    err = true;
+                }
+            }
+            if (err) {
+                m_Enabled = false;
+                return;
+            }
+
+            m_StackSize = m_Config.GetInt("ScriptStackSize", 2048) << 10;
+            m_HeapSize  = m_Config.GetInt("ScriptHeapSize",  1024) << 10;
+
+            m_log.InfoFormat("[XMREngine]: Enabled, {0}.{1} Meg (0x{2}) stacks",
+                    (m_StackSize >> 20).ToString (),
+                    (((m_StackSize % 0x100000) * 1000) 
+                            >> 20).ToString ("D3"),
+                    m_StackSize.ToString ("X"));
+
+            m_log.InfoFormat("[XMREngine]:  ... {0}.{1} Meg (0x{2}) heaps",
+                    (m_HeapSize >> 20).ToString (),
+                    (((m_HeapSize % 0x100000) * 1000) 
+                            >> 20).ToString ("D3"),
+                    m_HeapSize.ToString ("X"));
+
+            m_MaintenanceInterval = m_Config.GetInt("MaintenanceInterval", 10);
+
+            if (m_MaintenanceInterval > 0)
+            {
+                m_MaintenanceTimer = new System.Timers.Timer(m_MaintenanceInterval * 60000);
+                m_MaintenanceTimer.Elapsed += DoMaintenance;
+                m_MaintenanceTimer.Start();
+            }
+
+            MainConsole.Instance.Commands.AddCommand("xmr", false,
+                    "xmr",
+                    "xmr [...|help|...] ...",
+                    "Run xmr script engine commands",
+                    RunTest);
+
+            TraceCalls("[XMREngine]: Initialize successful");
+        }
+
+        public void AddRegion(Scene scene)
+        {
+            if (!m_Enabled)
+                return;
+
+            TraceCalls("[XMREngine]: XMREngine.AddRegion({0})", scene.RegionInfo.RegionName);
+
+            m_Scene = scene;
+
+            m_Scene.RegisterModuleInterface<IScriptModule>(this);
+
+            m_ScriptBasePath = m_Config.GetString ("ScriptBasePath", "ScriptData");
+            m_ScriptBasePath = Path.Combine (m_ScriptBasePath, scene.RegionInfo.RegionID.ToString());
+
+            Directory.CreateDirectory(m_ScriptBasePath);
+
+            m_Scene.EventManager.OnRezScript += OnRezScript;
+
+            m_Scene.StackModuleInterface<IScriptModule>(this);
+        }
+
+        private void OneTimeLateInitialization ()
+        {
+            /*
+             * Build list of defined APIs and their 'this' types and define a field in XMRInstanceSuperType.
+             */
+            ApiManager am = new ApiManager ();
+            Dictionary<string,Type> apiCtxTypes = new Dictionary<string,Type> ();
+            foreach (string api in am.GetApis ()) {
+                m_log.Debug ("[XMREngine]: adding api " + api);
+                IScriptApi scriptApi = am.CreateApi (api);
+                Type apiCtxType = scriptApi.GetType ();
+                if (api == "LSL") apiCtxType = typeof (XMRLSL_Api);
+                apiCtxTypes[api] = apiCtxType;
+            }
+
+            if (ScriptCodeGen.xmrInstSuperType == null) // Only create type once!
+            {
+                /*
+                 * Start creating type XMRInstanceSuperType that contains a field
+                 * m_ApiManager_<APINAME> that points to the per-instance context
+                 * struct for that API, ie, the 'this' value passed to all methods
+                 * in that API.  It is in essence:
+                 *
+                 *  public class XMRInstanceSuperType : XMRInstance {
+                 *      public XMRLSL_Api m_ApiManager_LSL;   // 'this' value for all ll...() functions
+                 *      public MOD_Api    m_ApiManager_MOD;   // 'this' value for all mod...() functions
+                 *      public OSSL_Api   m_ApiManager_OSSL;  // 'this' value for all os...() functions
+                 *              ....
+                 *  }
+                 */
+                AssemblyName assemblyName = new AssemblyName ();
+                assemblyName.Name = "XMRInstanceSuperAssembly";
+                AssemblyBuilder assemblyBuilder = Thread.GetDomain ().DefineDynamicAssembly (assemblyName, AssemblyBuilderAccess.Run);
+                ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule ("XMRInstanceSuperModule");
+                TypeBuilder typeBuilder = moduleBuilder.DefineType ("XMRInstanceSuperType", TypeAttributes.Public | TypeAttributes.Class);
+                typeBuilder.SetParent (typeof (XMRInstance));
+
+                foreach (string apiname in apiCtxTypes.Keys)
+                {
+                    string fieldName = "m_ApiManager_" + apiname;
+                    typeBuilder.DefineField (fieldName, apiCtxTypes[apiname], FieldAttributes.Public);
+                }
+
+                /*
+                 * Finalize definition of XMRInstanceSuperType.
+                 * Give the compiler a short name to reference it by,
+                 * otherwise it will try to use the AssemblyQualifiedName
+                 * and fail miserably.
+                 */
+                ScriptCodeGen.xmrInstSuperType = typeBuilder.CreateType ();
+                ScriptObjWriter.DefineInternalType ("xmrsuper", ScriptCodeGen.xmrInstSuperType);
+            }
+
+            /*
+             * Tell the compiler about all the constants and methods for each API.
+             * We also tell the compiler how to get the per-instance context for each API
+             * by reading the corresponding m_ApiManager_<APINAME> field of XMRInstanceSuperType.
+             */
+            foreach (KeyValuePair<string,Type> kvp in apiCtxTypes) {
+
+                // get API name and the corresponding per-instance context type
+                string api = kvp.Key;
+                Type apiCtxType = kvp.Value;
+
+                // give script compiler an abbreviated name for the API context type
+                ScriptObjWriter.DefineInternalType ("apimanager_" + api, apiCtxType);
+
+                // this field tells the compiled code where the per-instance API context object is
+                // eg, for the OSSL API, it is in ((XMRInstanceSuperType)inst).m_ApiManager_OSSL
+                string fieldName = "m_ApiManager_" + api;
+                FieldInfo fieldInfo = ScriptCodeGen.xmrInstSuperType.GetField (fieldName);
+                m_XMRInstanceApiCtxFieldInfos[api] = fieldInfo;
+
+                // now tell the compiler about the constants and methods for the API
+                ScriptConst.AddInterfaceConstants   (null, apiCtxType.GetFields ());
+                TokenDeclInline.AddInterfaceMethods (null, apiCtxType.GetMethods (), fieldInfo);
+            }
+
+            /*
+             * Add sim-specific APIs to the compiler.
+             */
+            IScriptModuleComms comms = m_Scene.RequestModuleInterface<IScriptModuleComms> ();
+            if (comms != null) {
+
+                /*
+                 * Add methods to list of built-in functions.
+                 */
+                Delegate[] methods = comms.GetScriptInvocationList ();
+                foreach (Delegate m in methods) {
+                    MethodInfo mi = m.Method;
+                    try {
+                        CommsCallCodeGen cccg = new CommsCallCodeGen (mi, comms, m_XMRInstanceApiCtxFieldInfos["MOD"]);
+                        Verbose ("[XMREngine]: added comms function " + cccg.fullName);
+                    } catch (Exception e) {
+                        m_log.Error ("[XMREngine]: failed to add comms function " + mi.Name);
+                        m_log.Error ("[XMREngine]: - " + e.ToString ());
+                    }
+                }
+
+                /*
+                 * Add constants to list of built-in constants.
+                 */
+                Dictionary<string,object> consts = comms.GetConstants ();
+                foreach (KeyValuePair<string,object> kvp in consts) {
+                    try {
+                        ScriptConst sc = ScriptConst.AddConstant (kvp.Key, kvp.Value);
+                        Verbose ("[XMREngine]: added comms constant " + sc.name);
+                    } catch (Exception e) {
+                        m_log.Error ("[XMREngine]: failed to add comms constant " + kvp.Key);
+                        m_log.Error ("[XMREngine]: - " + e.Message);
+                    }
+                }
+            } else {
+                Verbose ("[XMREngine]: comms not enabled");
+            }
+        }
+
+        /**
+         * @brief Generate code for the calls to the comms functions.
+         *        It is a tRUlY EvIL interface.
+         *        To call the function we must call an XMRInstanceSuperType.m_ApiManager_MOD.modInvoker?()
+         *        method passing it the name of the function as a string and the script
+         *        argument list wrapped up in an object[] array.  The modInvoker?() methods 
+         *        do some sick type conversions (with corresponding mallocs) so we can't 
+         *        call the methods directly.
+         */
+        private class CommsCallCodeGen : TokenDeclInline {
+            private static Type[] modInvokerArgTypes = new Type[] { typeof (string), typeof (object[]) };
+            public  static FieldInfo xmrInstModApiCtxField;
+
+            private MethodInfo modInvokerMeth;
+            private string methName;
+
+            /**
+             * @brief Constructor
+             * @param mi = method to make available to scripts
+             *        mi.Name = name that is used by scripts
+             *        mi.GetParameters() = parameter list as defined by module
+             *                             includes the 'UUID host','UUID script' parameters that script does not see
+             *                             allowed types for script-visible parameters are as follows:
+             *                                    Single -> float
+             *                                     Int32 -> integer
+             *                        OpenMetaverse.UUID -> key
+             *                                  Object[] -> list
+             *                  OpenMetaverse.Quaternion -> rotation
+             *                                    String -> string
+             *                     OpenMetaverse.Vector3 -> vector
+             *        mi.ReturnType = return type as defined by module
+             *                        types are same as allowed for parameters
+             * @param comms = comms module the method came from
+             * @param apictxfi = what field in XMRInstanceSuperType the 'this' value is for this method
+             */
+            public CommsCallCodeGen (MethodInfo mi, IScriptModuleComms comms, FieldInfo apictxfi)
+                    : base (null, false, NameArgSig (mi), RetType (mi))
+            {
+                methName = mi.Name;
+                string modInvokerName = comms.LookupModInvocation (methName);
+                if (modInvokerName == null) throw new Exception ("cannot find comms method " + methName);
+                modInvokerMeth = typeof (MOD_Api).GetMethod (modInvokerName, modInvokerArgTypes);
+                xmrInstModApiCtxField = apictxfi;
+            }
+
+            // script-visible name(argtype,...) signature string
+            private static string NameArgSig (MethodInfo mi)
+            {
+                StringBuilder sb = new StringBuilder ();
+                sb.Append (mi.Name);
+                sb.Append ('(');
+                ParameterInfo[] mps = mi.GetParameters ();
+                for (int i = 2; i < mps.Length; i ++) {
+                    ParameterInfo pi = mps[i];
+                    if (i > 2) sb.Append (',');
+                    sb.Append (ParamType (pi.ParameterType));
+                }
+                sb.Append (')');
+                return sb.ToString ();
+            }
+
+            // script-visible return type
+            // note that although we support void, the comms stuff does not
+            private static TokenType RetType (MethodInfo mi)
+            {
+                Type rt = mi.ReturnType;
+                if (rt == typeof (float))                    return new TokenTypeFloat (null);
+                if (rt == typeof (int))                      return new TokenTypeInt   (null);
+                if (rt == typeof (object[]))                 return new TokenTypeList  (null);
+                if (rt == typeof (OpenMetaverse.UUID))       return new TokenTypeKey   (null);
+                if (rt == typeof (OpenMetaverse.Quaternion)) return new TokenTypeRot   (null);
+                if (rt == typeof (string))                   return new TokenTypeStr   (null);
+                if (rt == typeof (OpenMetaverse.Vector3))    return new TokenTypeVec   (null);
+                if (rt == null || rt == typeof (void))       return new TokenTypeVoid  (null);
+                throw new Exception ("unsupported return type " + rt.Name);
+            }
+
+            // script-visible parameter type
+            private static string ParamType (Type t)
+            {
+                if (t == typeof (float))                    return "float";
+                if (t == typeof (int))                      return "integer";
+                if (t == typeof (OpenMetaverse.UUID))       return "key";
+                if (t == typeof (object[]))                 return "list";
+                if (t == typeof (OpenMetaverse.Quaternion)) return "rotation";
+                if (t == typeof (string))                   return "string";
+                if (t == typeof (OpenMetaverse.Vector3))    return "vector";
+                throw new Exception ("unsupported parameter type " + t.Name);
+            }
+
+            /**
+             * @brief Called by the compiler to generate a call to the comms function.
+             * @param scg = which script is being compiled
+             * @param errorAt = where in the source code the call is being made (for error messages)
+             * @param result = a temp location to put the return value in if any
+             * @param args = array of script-visible arguments being passed to the function
+             */
+            public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
+            {
+                /*
+                 * Set up 'this' pointer for modInvoker?() = value from ApiManager.CreateApi("MOD").
+                 */
+                scg.PushXMRInst ();
+                scg.ilGen.Emit (errorAt, OpCodes.Castclass, xmrInstModApiCtxField.DeclaringType);
+                scg.ilGen.Emit (errorAt, OpCodes.Ldfld, xmrInstModApiCtxField);
+
+                /*
+                 * Set up 'fname' argument to modInvoker?() = name of the function to be called.
+                 */
+                scg.ilGen.Emit (errorAt, OpCodes.Ldstr, methName);
+
+                /*
+                 * Set up 'parms' argument to modInvoker?() = object[] of the script-visible parameters,
+                 * in their LSL-wrapped form.  Of course, the modInvoker?() method will malloc yet another 
+                 * object[] and type-convert these parameters one-by-one with another round of unwrapping 
+                 * and wrapping.
+                 * Types allowed in this object[]:
+                 *    LSL_Float, LSL_Integer, LSL_Key, LSL_List, LSL_Rotation, LSL_String, LSL_Vector
+                 */
+                int nargs = args.Length;
+                scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, nargs);
+                scg.ilGen.Emit (errorAt, OpCodes.Newarr, typeof (object));
+
+                for (int i = 0; i < nargs; i ++) {
+                    scg.ilGen.Emit (errorAt, OpCodes.Dup);
+                    scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, i);
+
+                    // get location and type of argument
+
+                    CompValu arg = args[i];
+                    TokenType argtype = arg.type;
+
+                    // if already in a form acceptable to modInvoker?(),
+                    // just push it to the stack and convert to object
+                    // by boxing it if necessary
+
+                    // but if something like a double, int, string, etc
+                    // push to stack converting to the LSL-wrapped type
+                    // then convert to object by boxing if necessary
+
+                    Type boxit = null;
+                    if (argtype is TokenTypeLSLFloat) {
+                        args[i].PushVal (scg, errorAt);
+                        boxit = typeof (LSL_Float);
+                    } else if (argtype is TokenTypeLSLInt) {
+                        args[i].PushVal (scg, errorAt);
+                        boxit = typeof (LSL_Integer);
+                    } else if (argtype is TokenTypeLSLKey) {
+                        args[i].PushVal (scg, errorAt);
+                        boxit = typeof (LSL_Key);
+                    } else if (argtype is TokenTypeList) {
+                        args[i].PushVal (scg, errorAt);
+                        boxit = typeof (LSL_List);
+                    } else if (argtype is TokenTypeRot) {
+                        args[i].PushVal (scg, errorAt);
+                        boxit = typeof (LSL_Rotation);
+                    } else if (argtype is TokenTypeLSLString) {
+                        args[i].PushVal (scg, errorAt);
+                        boxit = typeof (LSL_String);
+                    } else if (argtype is TokenTypeVec) {
+                        args[i].PushVal (scg, errorAt);
+                        boxit = typeof (LSL_Vector);
+                    } else if (argtype is TokenTypeFloat) {
+                        args[i].PushVal (scg, errorAt, new TokenTypeLSLFloat (argtype));
+                        boxit = typeof (LSL_Float);
+                    } else if (argtype is TokenTypeInt) {
+                        args[i].PushVal (scg, errorAt, new TokenTypeLSLInt (argtype));
+                        boxit = typeof (LSL_Integer);
+                    } else if (argtype is TokenTypeKey) {
+                        args[i].PushVal (scg, errorAt, new TokenTypeLSLKey (argtype));
+                        boxit = typeof (LSL_Key);
+                    } else if (argtype is TokenTypeStr) {
+                        args[i].PushVal (scg, errorAt, new TokenTypeLSLString (argtype));
+                        boxit = typeof (LSL_String);
+                    } else {
+                        throw new Exception ("unsupported arg type " + argtype.GetType ().Name);
+                    }
+                    if (boxit.IsValueType) {
+                        scg.ilGen.Emit (errorAt, OpCodes.Box, boxit);
+                    }
+
+                    // pop the object into the object[]
+
+                    scg.ilGen.Emit (errorAt, OpCodes.Stelem, typeof (object));
+                }
+
+                /*
+                 * Call the modInvoker?() method.
+                 * It leaves an LSL-wrapped type on the stack.
+                 */
+                if (modInvokerMeth.IsVirtual) {
+                    scg.ilGen.Emit (errorAt, OpCodes.Callvirt, modInvokerMeth);
+                } else {
+                    scg.ilGen.Emit (errorAt, OpCodes.Call, modInvokerMeth);
+                }
+
+                /*
+                 * The 3rd arg to Pop() is the type on the stack, 
+                 * ie, what modInvoker?() actually returns.
+                 * The Pop() method will wrap/unwrap as needed.
+                 */
+                Type retSysType = modInvokerMeth.ReturnType;
+                if (retSysType == null) retSysType = typeof (void);
+                TokenType retTokType = TokenType.FromSysType (errorAt, retSysType);
+                result.Pop (scg, errorAt, retTokType);
+            }
+        }
+
+        /**
+         * @brief Called late in shutdown procedure,
+         *        after the 'Shutting down..." message.
+         */
+        public void RemoveRegion(Scene scene)
+        {
+            if (!m_Enabled)
+                return;
+
+            TraceCalls("[XMREngine]: XMREngine.RemoveRegion({0})", scene.RegionInfo.RegionName);
+
+            /*
+             * Write script states out to .state files so it will be
+             * available when the region is restarted.
+             */
+            DoMaintenance(null, null);
+
+            /*
+             * Stop executing script threads and wait for final
+             * one to finish (ie, script gets to CheckRun() call).
+             */
+            m_Exiting = true;
+            for (int i = 0; i < numThreadScriptWorkers; i ++) {
+                XMRScriptThread scriptThread = m_ScriptThreads[i];
+                if (scriptThread != null) {
+                    scriptThread.Terminate();
+                    m_ScriptThreads[i] = null;
+                }
+            }
+            if (m_SleepThread != null) {
+                lock (m_SleepQueue) {
+                    Monitor.PulseAll (m_SleepQueue);
+                }
+                m_SleepThread.Join();
+                m_SleepThread = null;
+            }
+            if (m_SliceThread != null) {
+                m_SliceThread.Join();
+                m_SliceThread = null;
+            }
+
+            m_Scene.EventManager.OnFrame -= OnFrame;
+            m_Scene.EventManager.OnRezScript -= OnRezScript;
+            m_Scene.EventManager.OnRemoveScript -= OnRemoveScript;
+            m_Scene.EventManager.OnScriptReset -= OnScriptReset;
+            m_Scene.EventManager.OnStartScript -= OnStartScript;
+            m_Scene.EventManager.OnStopScript -= OnStopScript;
+            m_Scene.EventManager.OnGetScriptRunning -= OnGetScriptRunning;
+            m_Scene.EventManager.OnShutdown -= OnShutdown;
+
+            m_Enabled = false;
+            m_Scene = null;
+        }
+
+        public void RegionLoaded(Scene scene)
+        {
+            if (!m_Enabled)
+                return;
+
+            TraceCalls("[XMREngine]: XMREngine.RegionLoaded({0})", scene.RegionInfo.RegionName);
+
+            m_Scene.EventManager.OnFrame += OnFrame;
+            m_Scene.EventManager.OnRemoveScript += OnRemoveScript;
+            m_Scene.EventManager.OnScriptReset += OnScriptReset;
+            m_Scene.EventManager.OnStartScript += OnStartScript;
+            m_Scene.EventManager.OnStopScript += OnStopScript;
+            m_Scene.EventManager.OnGetScriptRunning += OnGetScriptRunning;
+            m_Scene.EventManager.OnShutdown += OnShutdown;
+
+            InitEvents();
+        }
+
+        public void StartProcessing()
+        {
+            m_log.Debug ("[XMREngine]: StartProcessing entry");
+            m_Scene.EventManager.TriggerEmptyScriptCompileQueue (0, "");
+            m_StartProcessing = true;
+            for (int i = 0; i < numThreadScriptWorkers; i ++) {
+                XMRScriptThread.WakeUpOne();
+            }
+            m_log.Debug ("[XMREngine]: StartProcessing return");
+        }
+
+        public void Close()
+        {
+            TraceCalls("[XMREngine]: XMREngine.Close()");
+        }
+
+        private void RunTest (string module, string[] args)
+        {
+            if (args.Length < 2) {
+                m_log.Info ("[XMREngine]: missing command, try 'xmr help'");
+                return;
+            }
+
+            switch (args[1]) {
+                case "cvv": {
+                    switch (args.Length) {
+                        case 2: {
+                            m_log.InfoFormat ("[XMREngine]: compiled version value = {0}", 
+                                    ScriptCodeGen.COMPILED_VERSION_VALUE);
+                            break;
+                        }
+                        case 3: {
+                            try {
+                                ScriptCodeGen.COMPILED_VERSION_VALUE = Convert.ToInt32 (args[2]);
+                            } catch {
+                                m_log.Error ("[XMREngine]: bad/missing version number");
+                            }
+                            break;
+                        }
+                        default: {
+                            m_log.Error ("[XMREngine]: xmr cvv [<new_compiled_version_value>]");
+                            break;
+                        }
+                    }
+                    break;
+                }
+                case "echo": {
+                    for (int i = 0; i < args.Length; i ++) {
+                        m_log.Info ("[XMREngine]: echo[" + i + "]=<" + args[i] + ">");
+                    }
+                    break;
+                }
+                case "gc": {
+                    GC.Collect();
+                    break;
+                }
+                case "help":
+                case "?": {
+                    m_log.Info ("[XMREngine]: xmr cvv [<newvalue>] - show/set compiled version value");
+                    m_log.Info ("[XMREngine]: xmr gc");
+                    m_log.Info ("[XMREngine]: xmr ls [-help ...]");
+                    m_log.Info ("[XMREngine]: xmr mvv [<newvalue>] - show/set migration version value");
+                    m_log.Info ("[XMREngine]: xmr pev [-help ...] - post event");
+                    m_log.Info ("[XMREngine]: xmr reset [-help ...]");
+                    m_log.Info ("[XMREngine]: xmr resume - resume script processing");
+                    m_log.Info ("[XMREngine]: xmr suspend - suspend script processing");
+                    m_log.Info ("[XMREngine]: xmr tracecalls [yes | no]");
+                    m_log.Info ("[XMREngine]: xmr verbose [yes | no]");
+                    break;
+                }
+                case "ls": {
+                    XmrTestLs (args, 2);
+                    break;
+                }
+                case "mvv": {
+                    switch (args.Length) {
+                        case 2: {
+                            m_log.InfoFormat ("[XMREngine]: migration version value = {0}", 
+                                    XMRInstance.migrationVersion);
+                            break;
+                        }
+                        case 3: {
+                            try {
+                                int mvv = Convert.ToInt32 (args[2]);
+                                if ((mvv < 0) || (mvv > 255)) throw new Exception ("out of range");
+                                XMRInstance.migrationVersion = (byte) mvv;
+                            } catch (Exception e) {
+                                m_log.Error ("[XMREngine]: bad/missing version number (" + e.Message + ")");
+                            }
+                            break;
+                        }
+                        default: {
+                            m_log.Error ("[XMREngine]: xmr mvv [<new_migration_version_value>]");
+                            break;
+                        }
+                    }
+                    break;
+                }
+                case "pev": {
+                    XmrTestPev (args, 2);
+                    break;
+                }
+                case "reset": {
+                    XmrTestReset (args, 2);
+                    break;
+                }
+                case "resume": {
+                    m_log.Info ("[XMREngine]: resuming scripts");
+                    for (int i = 0; i < numThreadScriptWorkers; i ++) {
+                        m_ScriptThreads[i].ResumeThread();
+                    }
+                    break;
+                }
+                case "suspend": {
+                    m_log.Info ("[XMREngine]: suspending scripts");
+                    for (int i = 0; i < numThreadScriptWorkers; i ++) {
+                        m_ScriptThreads[i].SuspendThread();
+                    }
+                    break;
+                }
+                case "tracecalls": {
+                    if (args.Length > 2) {
+                        m_TraceCalls = (args[2][0] & 1) != 0;
+                    }
+                    m_log.Info ("[XMREngine]: tracecalls " + (m_TraceCalls ? "yes" : "no"));
+                    break;
+                }
+                case "verbose": {
+                    if (args.Length > 2) {
+                        m_Verbose = (args[2][0] & 1) != 0;
+                    }
+                    m_log.Info ("[XMREngine]: verbose " + (m_Verbose ? "yes" : "no"));
+                    break;
+                }
+                default: {
+                    m_log.Error ("[XMREngine]: unknown command " + args[1] + ", try 'xmr help'");
+                    break;
+                }
+            }
+        }
+
+        // Not required when not using IScriptInstance
+        //
+        public IScriptWorkItem QueueEventHandler(object parms)
+        {
+            return null;
+        }
+
+        public Scene World
+        {
+            get { return m_Scene; }
+        }
+
+        public IScriptModule ScriptModule
+        {
+            get { return this; }
+        }
+
+        public void SaveAllState()
+        {
+            m_log.Error("[XMREngine]: XMREngine.SaveAllState() called!!");
+        }
+
+        public event ScriptRemoved OnScriptRemoved;
+        public event ObjectRemoved OnObjectRemoved;
+
+        // Events targeted at a specific script
+        // ... like listen() for an llListen() call
+        //
+        public bool PostScriptEvent(UUID itemID, EventParams parms)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance == null) return false;
+
+            TraceCalls("[XMREngine]: XMREngine.PostScriptEvent({0},{1})", itemID.ToString(), parms.EventName);
+
+            instance.PostEvent(parms);
+            return true;
+        }
+
+        // Events targeted at all scripts in the given prim.
+        //   localID = which prim
+        //   parms   = event to post
+        //
+        public bool PostObjectEvent (uint localID, EventParams parms)
+        {
+            SceneObjectPart part = m_Scene.GetSceneObjectPart(localID);
+
+            if (part == null)
+                return false;
+
+            TraceCalls("[XMREngine]: XMREngine.PostObjectEvent({0},{1})", localID.ToString(), parms.EventName);
+
+            /*
+             * In SecondLife, attach events go to all scripts of all prims
+             * in a linked object.  So here we duplicate that functionality,
+             * as all we ever get is a single attach event for the whole
+             * object.
+             */
+            if (parms.EventName == "attach") {
+                bool posted = false;
+                foreach (SceneObjectPart primpart in part.ParentGroup.Parts) {
+                    posted |= PostPrimEvent (primpart, parms);
+                }
+                return posted;
+            }
+
+            /*
+             * Other events go to just the scripts in that prim.
+             */
+            return PostPrimEvent (part, parms);
+        }
+
+        private bool PostPrimEvent (SceneObjectPart part, EventParams parms)
+        {
+            UUID partUUID = part.UUID;
+
+            /*
+             * Get list of script instances running in the object.
+             */
+            XMRInstance[] objInstArray;
+            lock (m_InstancesDict) {
+                if (!m_ObjectInstArray.TryGetValue (partUUID, out objInstArray)) {
+                    return false;
+                }
+                if (objInstArray == null) {
+                    objInstArray = RebuildObjectInstArray (partUUID);
+                    m_ObjectInstArray[partUUID] = objInstArray;
+                }
+            }
+
+            /*
+             * Post event to all script instances in the object.
+             */
+            if (objInstArray.Length <= 0) return false;
+            foreach (XMRInstance inst in objInstArray) {
+                inst.PostEvent (parms);
+            }
+            return true;
+        }
+
+        public DetectParams GetDetectParams(UUID itemID, int number)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance == null) return null;
+            return instance.GetDetectParams(number);
+        }
+
+        public void SetMinEventDelay(UUID itemID, double delay)
+        {
+        }
+
+        public int GetStartParameter(UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance == null) return 0;
+            return instance.StartParam;
+        }
+
+        // This is the "set running" method
+        //
+        public void SetScriptState(UUID itemID, bool state, bool self)
+        {
+            SetScriptState (itemID, state);
+        }
+        public void SetScriptState(UUID itemID, bool state)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                instance.Running = state;
+            }
+        }
+
+        // Control display of the "running" checkbox
+        //
+        public bool GetScriptState(UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance == null) return false;
+            return instance.Running;
+        }
+
+        public void SetState(UUID itemID, string newState)
+        {
+            TraceCalls("[XMREngine]: XMREngine.SetState({0},{1})", itemID.ToString(), newState);
+        }
+
+        public void ApiResetScript(UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                instance.ApiReset();
+            }
+        }
+
+        public void ResetScript(UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                IUrlModule urlModule = m_Scene.RequestModuleInterface<IUrlModule>();
+                if (urlModule != null)
+                    urlModule.ScriptRemoved(itemID);
+
+                instance.Reset();
+            }
+        }
+
+        public IConfig Config
+        {
+            get { return m_Config; }
+        }
+
+        public IConfigSource ConfigSource
+        {
+            get { return m_ConfigSource; }
+        }
+
+        public string ScriptEngineName
+        {
+            get { return "XMREngine"; }
+        }
+
+        public IScriptApi GetApi(UUID itemID, string name)
+        {
+            FieldInfo fi;
+            if (!m_XMRInstanceApiCtxFieldInfos.TryGetValue (name, out fi)) return null;
+            XMRInstance inst = GetInstance (itemID);
+            if (inst == null) return null;
+            return (IScriptApi)fi.GetValue (inst);
+        }
+
+        /**
+         * @brief Get script's current state as an XML string
+         *        - called by "Take", "Take Copy" and when object deleted (ie, moved to Trash)
+         *        This includes the .state file
+         */
+        public string GetXMLState(UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance == null) return String.Empty;
+
+            TraceCalls("[XMREngine]: XMREngine.GetXMLState({0})", itemID.ToString());
+
+            if (!instance.m_HasRun) return String.Empty;
+
+            XmlDocument doc = new XmlDocument();
+
+            /*
+             * Set up <State Engine="XMREngine" UUID="itemID" Asset="assetID"> tag.
+             */
+            XmlElement stateN = doc.CreateElement("", "State", "");
+            doc.AppendChild(stateN);
+
+            XmlAttribute engineA = doc.CreateAttribute("", "Engine", "");
+            engineA.Value = ScriptEngineName;
+            stateN.Attributes.Append(engineA);
+
+            XmlAttribute uuidA = doc.CreateAttribute("", "UUID", "");
+            uuidA.Value = itemID.ToString();
+            stateN.Attributes.Append(uuidA);
+
+            XmlAttribute assetA = doc.CreateAttribute("", "Asset", "");
+            string assetID = instance.AssetID.ToString();
+            assetA.Value = assetID;
+            stateN.Attributes.Append(assetA);
+
+            /*
+             * Get <ScriptState>...</ScriptState> item that hold's script's state.
+             * This suspends the script if necessary then takes a snapshot.
+             */
+            XmlElement scriptStateN = instance.GetExecutionState(doc);
+            stateN.AppendChild(scriptStateN);
+
+            return doc.OuterXml;
+        }
+
+        // Set script's current state from an XML string
+        // - called just before a script is instantiated
+        // So we write the .state file so the .state file will be seen when 
+        // the script is instantiated.
+        public bool SetXMLState(UUID itemID, string xml)
+        {
+            XmlDocument doc = new XmlDocument();
+
+            try
+            {
+                doc.LoadXml(xml);
+            }
+            catch
+            {
+                return false;
+            }
+            TraceCalls("[XMREngine]: XMREngine.SetXMLState({0})", itemID.ToString());
+
+            // Make sure <State Engine="XMREngine"> so we know it is in our
+            // format.
+            XmlElement stateN = (XmlElement)doc.SelectSingleNode("State");
+            if (stateN == null)
+                return false;
+
+            if (stateN.GetAttribute("Engine") != ScriptEngineName)
+                return false;
+
+            // <ScriptState>...</ScriptState> contains contents of .state file.
+            XmlElement scriptStateN = (XmlElement)stateN.SelectSingleNode("ScriptState");
+            if (scriptStateN == null) {
+                return false;
+            }
+            string sen = stateN.GetAttribute("Engine");
+            if ((sen == null) || (sen != ScriptEngineName)) {
+                return false;
+            }
+
+            XmlAttribute assetA = doc.CreateAttribute("", "Asset", "");
+            assetA.Value = stateN.GetAttribute("Asset");
+            scriptStateN.Attributes.Append(assetA);
+
+            // Write out the .state file with the <ScriptState ...>...</ScriptState> XML text
+            string statePath = XMRInstance.GetStateFileName(m_ScriptBasePath, itemID);
+            FileStream ss = File.Create(statePath);
+            StreamWriter sw = new StreamWriter(ss);
+            sw.Write(scriptStateN.OuterXml);
+            sw.Close();
+            ss.Close();
+
+            return true;
+        }
+
+        public bool PostScriptEvent(UUID itemID, string name, Object[] p)
+        {
+            if (!m_Enabled)
+                return false;
+
+            TraceCalls("[XMREngine]: XMREngine.PostScriptEvent({0},{1})", itemID.ToString(), name);
+
+            return PostScriptEvent(itemID, new EventParams(name, p, zeroDetectParams));
+        }
+
+        public bool PostObjectEvent(UUID itemID, string name, Object[] p)
+        {
+            if (!m_Enabled)
+                return false;
+
+            TraceCalls("[XMREngine]: XMREngine.PostObjectEvent({0},{1})", itemID.ToString(), name);
+
+            SceneObjectPart part = m_Scene.GetSceneObjectPart(itemID);
+            if (part == null)
+                return false;
+
+            return PostObjectEvent(part.LocalId, new EventParams(name, p, zeroDetectParams));
+        }
+
+        // about the 3523rd entrypoint for a script to put itself to sleep
+        public void SleepScript(UUID itemID, int delay)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                instance.Sleep (delay);
+            }
+        }
+
+        // Get a script instance loaded, compiling it if necessary
+        //
+        //  localID     = the object as a whole, may contain many scripts
+        //  itemID      = this instance of the script in this object
+        //  script      = script source code
+        //  startParam  = value passed to 'on_rez' event handler
+        //  postOnRez   = true to post an 'on_rez' event to script on load
+        //  defEngine   = default script engine
+        //  stateSource = post this event to script on load
+
+        public void OnRezScript(uint localID, UUID itemID, string script,
+                int startParam, bool postOnRez, string defEngine, int stateSource)
+        {
+            SceneObjectPart part = m_Scene.GetSceneObjectPart(localID);
+            TaskInventoryItem item = part.Inventory.GetInventoryItem(itemID);
+
+            if (!m_LateInit) {
+                m_LateInit = true;
+                OneTimeLateInitialization ();
+            }
+
+            TraceCalls("[XMREngine]: OnRezScript(...,{0},...)", itemID.ToString());
+
+            /*
+             * Assume script uses the default engine, whatever that is.
+             */
+            string engineName = defEngine;
+
+            /*
+             * Very first line might contain "//" scriptengine ":".
+             */
+            string firstline = "";
+            if (script.StartsWith("//")) {
+                int lineEnd = script.IndexOf('\n');
+                if (lineEnd > 1) firstline = script.Substring(0, lineEnd).Trim();
+                int colon = firstline.IndexOf(':');
+                if (colon >= 2) {
+                    engineName = firstline.Substring(2, colon - 2).Trim();
+                    if (engineName == "") engineName = defEngine;
+                }
+            }
+
+            /*
+             * Make sure the default or requested engine is us.
+             */
+            if (engineName != ScriptEngineName) {
+
+                /*
+                 * Not us, if requested engine exists, silently ignore script and let
+                 * requested engine handle it.
+                 */
+                IScriptModule[] engines = m_Scene.RequestModuleInterfaces<IScriptModule> ();
+                foreach (IScriptModule eng in engines) {
+                    if (eng.ScriptEngineName == engineName) {
+                        return;
+                    }
+                }
+
+                /*
+                 * Requested engine not defined, warn on console.
+                 * Then we try to handle it if we're the default engine, else we ignore it.
+                 */
+                m_log.Warn ("[XMREngine]: " + itemID.ToString() + " requests undefined/disabled engine " + engineName);
+                m_log.Info ("[XMREngine]: - " + part.GetWorldPosition ());
+                m_log.Info ("[XMREngine]: first line: " + firstline);
+                if (defEngine != ScriptEngineName) {
+                    m_log.Info ("[XMREngine]: leaving it to the default script engine (" + defEngine + ") to process it");
+                    return;
+                }
+                m_log.Info ("[XMREngine]: will attempt to processing it anyway as default script engine");
+            }
+
+            /*
+             * Put on object/instance lists.
+             */
+            XMRInstance instance   = (XMRInstance)Activator.CreateInstance (ScriptCodeGen.xmrInstSuperType);
+            instance.m_LocalID     = localID;
+            instance.m_ItemID      = itemID;
+            instance.m_SourceCode  = script;
+            instance.m_StartParam  = startParam;
+            instance.m_PostOnRez   = postOnRez;
+            instance.m_StateSource = (StateSource)stateSource;
+            instance.m_Part        = part;
+            instance.m_PartUUID    = part.UUID;
+            instance.m_Item        = item;
+            instance.m_DescName    = part.Name + ":" + item.Name;
+            instance.m_IState      = XMRInstState.CONSTRUCT;
+
+            lock (m_InstancesDict) {
+                m_LockedDict = "RegisterInstance";
+
+                // Insert on internal list of all scripts being handled by this engine instance.
+                m_InstancesDict[instance.m_ItemID] = instance;
+
+                // Insert on internal list of all scripts being handled by this engine instance
+                // that are part of the object.
+                List<UUID> itemIDList;
+                if (!m_ObjectItemList.TryGetValue(instance.m_PartUUID, out itemIDList)) {
+                    itemIDList = new List<UUID>();
+                    m_ObjectItemList[instance.m_PartUUID] = itemIDList;
+                }
+                if (!itemIDList.Contains(instance.m_ItemID)) {
+                    itemIDList.Add(instance.m_ItemID);
+                    m_ObjectInstArray[instance.m_PartUUID] = null;
+                }
+
+                m_LockedDict = "~RegisterInstance";
+            }
+
+            /*
+             * Compile and load it.
+             */
+            lock (m_ScriptErrors) {
+                m_ScriptErrors.Remove (instance.m_ItemID);
+            }
+            LoadThreadWork (instance);
+        }
+
+        /**
+         * @brief This routine instantiates one script.
+         */
+        private void LoadThreadWork (XMRInstance instance)
+        {
+            /*
+             * Compile and load the script in memory.
+             */
+            ArrayList errors = new ArrayList();
+            Exception initerr = null;
+            try {
+                instance.Initialize(this, m_ScriptBasePath, m_StackSize, m_HeapSize, errors);
+            } catch (Exception e1) {
+                initerr = e1;
+            }
+            if ((initerr != null) && !instance.m_ForceRecomp) {
+                UUID itemID = instance.m_ItemID;
+                Verbose ("[XMREngine]: {0}/{2} first load failed ({1}), retrying after recompile", 
+                        itemID.ToString(), initerr.Message, instance.m_Item.AssetID.ToString());
+                Verbose ("[XMREngine]:\n{0}", initerr.ToString ());
+                initerr = null;
+                errors = new ArrayList();
+                instance.m_ForceRecomp = true;
+                try {
+                    instance.Initialize(this, m_ScriptBasePath, m_StackSize, m_HeapSize, errors);
+                } catch (Exception e2) {
+                    initerr = e2;
+                }
+            }
+            if (initerr != null) {
+                UUID itemID = instance.m_ItemID;
+                Verbose ("[XMREngine]: Error starting script {0}/{2}: {1}",
+                                  itemID.ToString(), initerr.Message, instance.m_Item.AssetID.ToString());
+                if (initerr.Message != "compilation errors") {
+                    Verbose ("[XMREngine]: - " + instance.m_Part.GetWorldPosition () + " " + instance.m_DescName);
+                    Verbose ("[XMREngine]:   exception:\n{0}", initerr.ToString());
+                }
+
+                OnRemoveScript (0, itemID);
+
+                /*
+                 * Post errors where GetScriptErrors() can see them.
+                 */
+                if (errors.Count == 0) {
+                    errors.Add(initerr.Message);
+                } else {
+                    foreach (Object err in errors) {
+                        if (m_ScriptDebug)
+                            m_log.DebugFormat ("[XMREngine]:   {0}", err.ToString());
+                    }
+                }
+                lock (m_ScriptErrors) {
+                    m_ScriptErrors[instance.m_ItemID] = errors;
+                }
+
+                return;
+            }
+
+            /*
+             * Tell GetScriptErrors() that we have finished compiling/loading
+             * successfully (by posting a 0 element array).
+             */
+            lock (m_ScriptErrors) {
+                if (instance.m_IState != XMRInstState.CONSTRUCT) throw new Exception("bad state");
+                m_ScriptErrors[instance.m_ItemID] = noScriptErrors;
+            }
+
+            /*
+             * Transition from CONSTRUCT->ONSTARTQ and give to RunScriptThread().
+             * Put it on the start queue so it will run any queued event handlers,
+             * such as state_entry() or on_rez().  If there aren't any queued, it
+             * will just go to idle state when RunOne() tries to dequeue an event.
+             */
+            lock (instance.m_QueueLock) {
+                if (instance.m_IState != XMRInstState.CONSTRUCT) throw new Exception("bad state");
+                instance.m_IState = XMRInstState.ONSTARTQ;
+                if (!instance.m_Running) {
+                    instance.EmptyEventQueues ();
+                }
+            }
+            QueueToStart(instance);
+        }
+
+        public void OnRemoveScript(uint localID, UUID itemID)
+        {
+            TraceCalls("[XMREngine]: OnRemoveScript(...,{0})", itemID.ToString());
+
+            /*
+             * Remove from our list of known scripts.
+             * After this, no more events can queue because we won't be
+             * able to translate the itemID to an XMRInstance pointer.
+             */
+            XMRInstance instance = null;
+            lock (m_InstancesDict)
+            {
+                m_LockedDict = "OnRemoveScript:" + itemID.ToString();
+
+                /*
+                 * Tell the instance to free off everything it can.
+                 */
+                if (!m_InstancesDict.TryGetValue(itemID, out instance))
+                {
+                    m_LockedDict = "~OnRemoveScript";
+                    return;
+                }
+
+                /*
+                 * Tell it to stop executing anything.
+                 */
+                instance.suspendOnCheckRunHold = true;
+
+                /*
+                 * Remove it from our list of known script instances
+                 * mostly so no more events can queue to it.
+                 */
+                m_InstancesDict.Remove(itemID);
+
+                List<UUID> itemIDList;
+                if (m_ObjectItemList.TryGetValue (instance.m_PartUUID, out itemIDList)) {
+                    itemIDList.Remove(itemID);
+                    if (itemIDList.Count == 0) {
+                        m_ObjectItemList.Remove(instance.m_PartUUID);
+                        m_ObjectInstArray.Remove(instance.m_PartUUID);
+                    } else {
+                        m_ObjectInstArray[instance.m_PartUUID] = null;
+                    }
+                }
+
+                /*
+                 * Delete the .state file as any needed contents were fetched with GetXMLState()
+                 * and stored on the database server.
+                 */
+                string stateFileName = XMRInstance.GetStateFileName(m_ScriptBasePath, itemID);
+                File.Delete(stateFileName);
+
+                ScriptRemoved handlerScriptRemoved = OnScriptRemoved;
+                if (handlerScriptRemoved != null) {
+                    handlerScriptRemoved(itemID);
+                }
+
+                m_LockedDict = "~~OnRemoveScript";
+            }
+
+            /*
+             * Free off its stack and fun things like that.
+             * If it is running, abort it.
+             */
+            instance.Dispose ();
+        }
+
+        public void OnScriptReset(uint localID, UUID itemID)
+        {
+            TraceCalls("[XMREngine]: XMREngine.OnScriptReset({0},{1})", localID.ToString(), itemID.ToString());
+            ResetScript(itemID);
+        }
+
+        public void OnStartScript(uint localID, UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                instance.Running = true;
+            }
+        }
+
+        public void OnStopScript(uint localID, UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                instance.Running = false;
+            }
+        }
+
+        public void OnGetScriptRunning(IClientAPI controllingClient,
+                UUID objectID, UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                TraceCalls("[XMREngine]: XMREngine.OnGetScriptRunning({0},{1})", objectID.ToString(), itemID.ToString());
+
+                IEventQueue eq = World.RequestModuleInterface<IEventQueue>();
+                if (eq == null) {
+                    controllingClient.SendScriptRunningReply(objectID, itemID,
+                            instance.Running);
+                } else {
+                    eq.Enqueue(EventQueueHelper.ScriptRunningReplyEvent(objectID,
+                            itemID, instance.Running, true),
+                            controllingClient.AgentId);
+                }
+            }
+        }
+
+        public bool HasScript(UUID itemID, out bool running)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance == null) {
+                running = true;
+                return false;
+            }
+            running = instance.Running;
+            return true;
+        }
+
+        /**
+         * @brief Called once per frame update to see if scripts have
+         *        any such work to do.
+         */
+        private void OnFrame ()
+        {
+            if (m_FrameUpdateList != null) {
+                ThreadStart frameupdates;
+                lock (m_FrameUpdateLock) {
+                    frameupdates = m_FrameUpdateList;
+                    m_FrameUpdateList = null;
+                }
+                frameupdates ();
+            }
+        }
+
+        /**
+         * @brief Add a one-shot delegate to list of things to do
+         *        synchronized with frame updates.
+         */
+        public void AddOnFrameUpdate (ThreadStart thunk)
+        {
+            lock (m_FrameUpdateLock) {
+                m_FrameUpdateList += thunk;
+            }
+        }
+
+        /**
+         * @brief Gets called early as part of shutdown,
+         *        right after "Persisting changed objects" message.
+         */
+        public void OnShutdown()
+        {
+            TraceCalls("[XMREngine]: XMREngine.OnShutdown()");
+        }
+
+        /**
+         * @brief Queue an instance to the StartQueue so it will run.
+         *        This queue is used for instances that have just had
+         *        an event queued to them when they were previously
+         *        idle.  It must only be called by the thread that
+         *        transitioned the thread to XMRInstState.ONSTARTQ so
+         *        we don't get two threads trying to queue the same
+         *        instance to the m_StartQueue at the same time.
+         */
+        public void QueueToStart(XMRInstance inst)
+        {
+            lock (m_StartQueue) {
+                if (inst.m_IState != XMRInstState.ONSTARTQ) throw new Exception("bad state");
+                m_StartQueue.InsertTail(inst);
+            }
+            XMRScriptThread.WakeUpOne();
+        }
+
+        /**
+         * @brief A script may be sleeping, in which case we wake it.
+         */
+        public void WakeFromSleep(XMRInstance inst)
+        {
+            /*
+             * Remove from sleep queue unless someone else already woke it.
+             */
+            lock (m_SleepQueue) {
+                if (inst.m_IState != XMRInstState.ONSLEEPQ) {
+                    return;
+                }
+                m_SleepQueue.Remove(inst);
+                inst.m_IState = XMRInstState.REMDFROMSLPQ;
+            }
+
+            /*
+             * Put on end of list of scripts that are ready to run.
+             */
+            lock (m_YieldQueue) {
+                inst.m_IState = XMRInstState.ONYIELDQ;
+                m_YieldQueue.InsertTail(inst);
+            }
+
+            /*
+             * Make sure the OS thread is running so it will see the script.
+             */
+            XMRScriptThread.WakeUpOne();
+        }
+
+        /**
+         * @brief An instance has just finished running for now,
+         *        figure out what to do with it next.
+         * @param inst = instance in question, not on any queue at the moment
+         * @param newIState = its new state
+         * @returns with instance inserted onto proper queue (if any)
+         */
+        public void HandleNewIState(XMRInstance inst, XMRInstState newIState)
+        {
+            /*
+             * RunOne() should have left the instance in RUNNING state.
+             */
+            if (inst.m_IState != XMRInstState.RUNNING) throw new Exception("bad state");
+
+            /*
+             * Now see what RunOne() wants us to do with the instance next.
+             */
+            switch (newIState) {
+
+                /*
+                 * Instance has set m_SleepUntil to when it wants to sleep until.
+                 * So insert instance in sleep queue by ascending wake time.
+                 * Then wake the timer thread if this is the new first entry
+                 * so it will reset its timer.
+                 */
+                case XMRInstState.ONSLEEPQ: {
+                    lock (m_SleepQueue) {
+                        XMRInstance after;
+
+                        inst.m_IState = XMRInstState.ONSLEEPQ;
+                        for (after = m_SleepQueue.PeekHead(); after != null; after = after.m_NextInst) {
+                            if (after.m_SleepUntil > inst.m_SleepUntil) break;
+                        }
+                        m_SleepQueue.InsertBefore(inst, after);
+                        if (m_SleepQueue.PeekHead() == inst) {
+                            Monitor.Pulse (m_SleepQueue);
+                        }
+                    }
+                    break;
+                }
+
+                /*
+                 * Instance just took a long time to run and got wacked by the
+                 * slicer.  So put on end of yield queue to let someone else
+                 * run.  If there is no one else, it will run again right away.
+                 */
+                case XMRInstState.ONYIELDQ: {
+                    lock (m_YieldQueue) {
+                        inst.m_IState = XMRInstState.ONYIELDQ;
+                        m_YieldQueue.InsertTail(inst);
+                    }
+                    break;
+                }
+
+                /*
+                 * Instance finished executing an event handler.  So if there is
+                 * another event queued for it, put it on the start queue so it
+                 * will process the new event.  Otherwise, mark it idle and the
+                 * next event to queue to it will start it up.
+                 */
+                case XMRInstState.FINISHED: {
+                    Monitor.Enter(inst.m_QueueLock);
+                    if (!inst.m_Suspended && (inst.m_EventQueue.Count > 0)) {
+                        Monitor.Exit(inst.m_QueueLock);
+                        lock (m_StartQueue) {
+                            inst.m_IState = XMRInstState.ONSTARTQ;
+                            m_StartQueue.InsertTail (inst);
+                        }
+                    } else {
+                        inst.m_IState = XMRInstState.IDLE;
+                        Monitor.Exit(inst.m_QueueLock);
+                    }
+                    break;
+                }
+
+                /*
+                 * Its m_SuspendCount > 0.
+                 * Don't put it on any queue and it won't run.
+                 * Since it's not IDLE, even queuing an event won't start it.
+                 */
+                case XMRInstState.SUSPENDED: {
+                    inst.m_IState = XMRInstState.SUSPENDED;
+                    break;
+                }
+
+                /*
+                 * It has been disposed of.
+                 * Just set the new state and all refs should theoretically drop off
+                 * as the instance is no longer in any list.
+                 */
+                case XMRInstState.DISPOSED: {
+                    inst.m_IState = XMRInstState.DISPOSED;
+                    break;
+                }
+
+                /*
+                 * RunOne returned something bad.
+                 */
+                default: throw new Exception("bad new state");
+            }
+        }
+
+        /**
+         * @brief Thread that moves instances from the Sleep queue to the Yield queue.
+         */
+        private void RunSleepThread()
+        {
+            double deltaTS;
+            int deltaMS;
+            XMRInstance inst;
+
+            while (true) {
+                lock (m_SleepQueue) {
+
+                    /*
+                     * Wait here until there is a script on the timer queue that has expired.
+                     */
+                    while (true) {
+                        UpdateMyThread ();
+                        if (m_Exiting) {
+                            MyThreadExiting ();
+                            return;
+                        }
+                        inst = m_SleepQueue.PeekHead();
+                        if (inst == null) {
+                            Monitor.Wait (m_SleepQueue, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
+                            continue;
+                        }
+                        if (inst.m_IState != XMRInstState.ONSLEEPQ) throw new Exception("bad state");
+                        deltaTS = (inst.m_SleepUntil - DateTime.UtcNow).TotalMilliseconds;
+                        if (deltaTS <= 0.0) break;
+                        deltaMS = Int32.MaxValue;
+                        if (deltaTS < Int32.MaxValue) deltaMS = (int)deltaTS;
+                        if (deltaMS > Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2) {
+                            deltaMS = Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2;
+                        }
+                        Monitor.Wait (m_SleepQueue, deltaMS);
+                    }
+
+                    /*
+                     * Remove the expired entry from the timer queue.
+                     */
+                    m_SleepQueue.RemoveHead();
+                    inst.m_IState = XMRInstState.REMDFROMSLPQ;
+                }
+
+                /*
+                 * Post the script to the yield queue so it will run and wake a script thread to run it.
+                 */
+                lock (m_YieldQueue) {
+                    inst.m_IState = XMRInstState.ONYIELDQ;
+                    m_YieldQueue.InsertTail(inst);
+                }
+                XMRScriptThread.WakeUpOne ();
+            }
+        }
+
+        /**
+         * @brief Thread that runs a time slicer.
+         */
+        private void RunSliceThread()
+        {
+            int ms = m_Config.GetInt ("TimeSlice", 50);
+            while (!m_Exiting) {
+                UpdateMyThread ();
+
+                /*
+                 * Let script run for a little bit.
+                 */
+                System.Threading.Thread.Sleep (ms);
+
+                /*
+                 * If some script is running, flag it to suspend
+                 * next time it calls CheckRun().
+                 */
+                for (int i = 0; i < numThreadScriptWorkers; i ++) {
+                    XMRScriptThread st = m_ScriptThreads[i];
+                    if (st != null) st.TimeSlice();
+                }
+            }
+            MyThreadExiting ();
+        }
+
+        public void Suspend(UUID itemID, int ms)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                instance.Sleep(ms);
+            }
+        }
+
+        public void Die(UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                TraceCalls("[XMREngine]: XMREngine.Die({0})", itemID.ToString());
+                instance.Die();
+            }
+        }
+
+        /**
+         * @brief Get specific script instance for which OnRezScript()
+         *        has been called for an XMREngine script, and that
+         *        OnRemoveScript() has not been called since.
+         * @param itemID = as passed to OnRezScript() identifying a specific script instance
+         * @returns null: not one of our scripts (maybe XEngine etc)
+         *          else: points to the script instance
+         */
+        public XMRInstance GetInstance(UUID itemID)
+        {
+            XMRInstance instance;
+            lock (m_InstancesDict) {
+                if (!m_InstancesDict.TryGetValue(itemID, out instance)) {
+                    instance = null;
+                }
+            }
+            return instance;
+        }
+
+        // Called occasionally to write script state to .state file so the
+        // script will restart from its last known state if the region crashes
+        // and gets restarted.
+        private void DoMaintenance(object source, ElapsedEventArgs e)
+        {
+            XMRInstance[] instanceArray;
+
+            lock (m_InstancesDict) {
+                instanceArray = System.Linq.Enumerable.ToArray(m_InstancesDict.Values);
+            }
+            foreach (XMRInstance ins in instanceArray)
+            {
+                // Don't save attachments
+                if (ins.m_Part.ParentGroup.IsAttachment)
+                    continue;
+                ins.GetExecutionState(new XmlDocument());
+            }
+        }
+
+        /**
+         * @brief Retrieve errors generated by a previous call to OnRezScript().
+         *        We are guaranteed this routine will not be called before the
+         *        corresponding OnRezScript() has returned.  It blocks until the
+         *        compile has completed.
+         */
+        public ArrayList GetScriptErrors(UUID itemID)
+        {
+            ArrayList errors;
+
+            lock (m_ScriptErrors) {
+                while (!m_ScriptErrors.TryGetValue (itemID, out errors)) {
+                    Monitor.Wait (m_ScriptErrors);
+                }
+                m_ScriptErrors.Remove (itemID);
+            }
+            return errors;
+        }
+
+        /**
+         * @brief Return a list of all script execution times.
+         */
+        public Dictionary<uint, float> GetObjectScriptsExecutionTimes ()
+        {
+            Dictionary<uint, float> topScripts = new Dictionary<uint, float> ();
+            lock (m_InstancesDict) {
+                foreach (XMRInstance instance in m_InstancesDict.Values) {
+                    uint rootLocalID = instance.m_Part.ParentGroup.LocalId;
+                    float oldTotal;
+                    if (!topScripts.TryGetValue (rootLocalID, out oldTotal)) {
+                        oldTotal = 0;
+                    }
+                    topScripts[rootLocalID] = (float)instance.m_CPUTime + oldTotal;
+                }
+            }
+            return topScripts;
+        }
+
+        /**
+         * @brief A float the value is a representative execution time in
+         *        milliseconds of all scripts in the link set.
+         * @param itemIDs = list of scripts in the link set
+         * @returns milliseconds for all those scripts
+         */
+        public float GetScriptExecutionTime (List<UUID> itemIDs)
+        {
+            if ((itemIDs == null) || (itemIDs.Count == 0)) {
+                return 0;
+            }
+            float time = 0;
+            foreach (UUID itemID in itemIDs) {
+                XMRInstance instance = GetInstance (itemID);
+                if ((instance != null) && instance.Running) {
+                    time += (float) instance.m_CPUTime;
+                }
+            }
+            return time;
+        }
+
+        /**
+         * @brief Block script from dequeuing events.
+         */
+        public void SuspendScript(UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                TraceCalls("[XMREngine]: XMREngine.SuspendScript({0})", itemID.ToString());
+                instance.SuspendIt();
+            }
+        }
+
+        /**
+         * @brief Allow script to dequeue events.
+         */
+        public void ResumeScript(UUID itemID)
+        {
+            XMRInstance instance = GetInstance (itemID);
+            if (instance != null) {
+                TraceCalls("[XMREngine]: XMREngine.ResumeScript({0})", itemID.ToString());
+                instance.ResumeIt();
+            } else {
+                // probably an XEngine script
+            }
+        }
+
+        /**
+         * @brief Rebuild m_ObjectInstArray[partUUID] from m_ObjectItemList[partUUID]
+         * @param partUUID = which object in scene to rebuild for
+         */
+        private XMRInstance[] RebuildObjectInstArray (UUID partUUID)
+        {
+            List<UUID> itemIDList = m_ObjectItemList[partUUID];
+            int n = 0;
+            foreach (UUID itemID in itemIDList) {
+                if (m_InstancesDict.ContainsKey (itemID)) n ++;
+            }
+            XMRInstance[] a = new XMRInstance[n];
+            n = 0;
+            foreach (UUID itemID in itemIDList) {
+                if (m_InstancesDict.TryGetValue (itemID, out a[n])) n ++;
+            }
+            m_ObjectInstArray[partUUID] = a;
+            return a;
+        }
+
+        public void TraceCalls (string format, params object[] args)
+        {
+            if (m_TraceCalls) m_log.DebugFormat (format, args);
+        }
+        public void Verbose (string format, params object[] args)
+        {
+            if (m_Verbose) m_log.DebugFormat (format, args);
+        }
+
+        /**
+         * @brief Manage our threads.
+         */
+        public static Thread StartMyThread (ThreadStart start, string name, ThreadPriority priority)
+        {
+            m_log.Debug ("[XMREngine]: starting thread " + name);
+            Thread thread   = new Thread (start);
+            thread.Name     = name;
+            thread.Priority = priority;
+            thread.Start ();
+
+            Watchdog.ThreadWatchdogInfo info = new Watchdog.ThreadWatchdogInfo (thread, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS, name);
+            Watchdog.AddThread (info, name, true);
+
+            return thread;
+        }
+
+        public static void UpdateMyThread ()
+        {
+            Watchdog.UpdateThread ();
+        }
+
+        public static void MyThreadExiting ()
+        {
+            Watchdog.RemoveThread (true);
+        }
+    }
+}

+ 369 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMREvents.cs

@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.ScriptEngine.Shared;
+using OpenSim.Region.ScriptEngine.Interfaces;
+using log4net;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    /// <summary>
+    /// Prepares events so they can be directly executed upon a script by EventQueueManager, then queues it.
+    /// </summary>
+    public partial class XMREngine
+    {
+        public static readonly object[] zeroObjectArray = new object[0];
+        public static readonly object[] oneObjectArrayOne = new object[1] { 1 };
+
+        private void InitEvents()
+        {
+            m_log.Info("[XMREngine] Hooking up to server events");
+            this.World.EventManager.OnAttach                    += attach;
+            this.World.EventManager.OnObjectGrab                += touch_start;
+            this.World.EventManager.OnObjectGrabbing            += touch;
+            this.World.EventManager.OnObjectDeGrab              += touch_end;
+            this.World.EventManager.OnScriptChangedEvent        += changed;
+            this.World.EventManager.OnScriptAtTargetEvent       += at_target;
+            this.World.EventManager.OnScriptNotAtTargetEvent    += not_at_target;
+            this.World.EventManager.OnScriptAtRotTargetEvent    += at_rot_target;
+            this.World.EventManager.OnScriptNotAtRotTargetEvent += not_at_rot_target;
+            this.World.EventManager.OnScriptMovingStartEvent    += moving_start;
+            this.World.EventManager.OnScriptMovingEndEvent      += moving_end;
+            this.World.EventManager.OnScriptControlEvent        += control;
+            this.World.EventManager.OnScriptColliderStart       += collision_start;
+            this.World.EventManager.OnScriptColliding           += collision;
+            this.World.EventManager.OnScriptCollidingEnd        += collision_end;
+            this.World.EventManager.OnScriptLandColliderStart   += land_collision_start;
+            this.World.EventManager.OnScriptLandColliding       += land_collision;
+            this.World.EventManager.OnScriptLandColliderEnd     += land_collision_end;
+            IMoneyModule money=this.World.RequestModuleInterface<IMoneyModule>();
+            if (money != null)
+            {
+                money.OnObjectPaid+=HandleObjectPaid;
+            }
+        }
+
+        /// <summary>
+        /// When an object gets paid by an avatar and generates the paid event, 
+        /// this will pipe it to the script engine
+        /// </summary>
+        /// <param name="objectID">Object ID that got paid</param>
+        /// <param name="agentID">Agent Id that did the paying</param>
+        /// <param name="amount">Amount paid</param>
+        private void HandleObjectPaid(UUID objectID, UUID agentID,
+                int amount)
+        {
+            // Add to queue for all scripts in ObjectID object
+            DetectParams[] det = new DetectParams[1];
+            det[0] = new DetectParams();
+            det[0].Key = agentID;
+            det[0].Populate(this.World);
+
+            // Since this is an event from a shared module, all scenes will
+            // get it. But only one has the object in question. The others
+            // just ignore it.
+            //
+            SceneObjectPart part =
+                    this.World.GetSceneObjectPart(objectID);
+
+            if (part == null)
+                return;
+
+            if ((part.ScriptEvents & scriptEvents.money) == 0)
+                part = part.ParentGroup.RootPart;
+
+            Verbose ("Paid: " + objectID + " from " + agentID + ", amount " + amount);
+
+            if (part != null)
+            {
+                money(part.LocalId, agentID, amount, det);
+            }
+        }
+
+        /// <summary>
+        /// Handles piping the proper stuff to The script engine for touching
+        /// Including DetectedParams
+        /// </summary>
+        /// <param name="localID"></param>
+        /// <param name="originalID"></param>
+        /// <param name="offsetPos"></param>
+        /// <param name="remoteClient"></param>
+        /// <param name="surfaceArgs"></param>
+        public void touch_start(uint localID, uint originalID, Vector3 offsetPos,
+                IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs)
+        {
+            touches(localID, originalID, offsetPos, remoteClient, surfaceArgs, "touch_start");
+        }
+
+        public void touch(uint localID, uint originalID, Vector3 offsetPos,
+                IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs)
+        {
+            touches(localID, originalID, offsetPos, remoteClient, surfaceArgs, "touch");
+        }
+
+        private static Vector3 zeroVec3 = new Vector3(0,0,0);
+        public void touch_end(uint localID, uint originalID, IClientAPI remoteClient,
+                              SurfaceTouchEventArgs surfaceArgs)
+        {
+            touches(localID, originalID, zeroVec3, remoteClient, surfaceArgs, "touch_end");
+        }
+
+        private void touches(uint localID, uint originalID, Vector3 offsetPos,
+                IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs, string eventname)
+        {
+            SceneObjectPart part;
+            if (originalID == 0) {
+                part = this.World.GetSceneObjectPart(localID);
+                if (part == null) return;
+            } else {
+                part = this.World.GetSceneObjectPart(originalID);
+            }
+
+            DetectParams det = new DetectParams();
+            det.Key = remoteClient.AgentId;
+            det.Populate(this.World);
+            det.OffsetPos = new LSL_Vector(offsetPos.X,
+                                           offsetPos.Y,
+                                           offsetPos.Z);
+            det.LinkNum = part.LinkNum;
+
+            if (surfaceArgs != null) {
+                det.SurfaceTouchArgs = surfaceArgs;
+            }
+
+            // Add to queue for all scripts in ObjectID object
+            this.PostObjectEvent(localID, new EventParams(
+                    eventname, oneObjectArrayOne,
+                    new DetectParams[] { det }));
+        }
+
+        public void changed(uint localID, uint change)
+        {
+            int ch = (int)change;
+            // Add to queue for all scripts in localID, Object pass change.
+            this.PostObjectEvent(localID, new EventParams(
+                    "changed",new object[] { ch },
+                    zeroDetectParams));
+        }
+
+        // state_entry: not processed here
+        // state_exit: not processed here
+
+        public void money(uint localID, UUID agentID, int amount, DetectParams[] det)
+        {
+            this.PostObjectEvent(localID, new EventParams(
+                    "money", new object[] {
+                    agentID.ToString(),
+                    amount },
+                    det));
+        }
+
+        public void collision_start(uint localID, ColliderArgs col)
+        {
+            collisions(localID, col, "collision_start");
+        }
+
+        public void collision(uint localID, ColliderArgs col)
+        {
+            collisions(localID, col, "collision");
+        }
+
+        public void collision_end(uint localID, ColliderArgs col)
+        {
+            collisions(localID, col, "collision_end");
+        }
+
+        private void collisions(uint localID, ColliderArgs col, string eventname)
+        {
+            int dc = col.Colliders.Count;
+            if (dc > 0) {
+                DetectParams[] det = new DetectParams[dc];
+                int i = 0;
+                foreach (DetectedObject detobj in col.Colliders) {
+                    DetectParams d = new DetectParams();
+                    det[i++] = d;
+
+                    d.Key = detobj.keyUUID;
+                    d.Populate (this.World);
+
+                    /* not done by XEngine...
+                    d.Position = detobj.posVector;
+                    d.Rotation = detobj.rotQuat;
+                    d.Velocity = detobj.velVector;
+                    ... */
+                }
+
+                this.PostObjectEvent(localID, new EventParams(
+                        eventname,
+                        new Object[] { dc },
+                        det));
+            }
+        }
+
+        public void land_collision_start(uint localID, ColliderArgs col)
+        {
+            land_collisions(localID, col, "land_collision_start");
+        }
+
+        public void land_collision(uint localID, ColliderArgs col)
+        {
+            land_collisions(localID, col, "land_collision");
+        }
+
+        public void land_collision_end(uint localID, ColliderArgs col)
+        {
+            land_collisions(localID, col, "land_collision_end");
+        }
+
+        private void land_collisions(uint localID, ColliderArgs col, string eventname)
+        {
+            foreach (DetectedObject detobj in col.Colliders) {
+                LSL_Vector vec = new LSL_Vector(detobj.posVector.X,
+                                                detobj.posVector.Y,
+                                                detobj.posVector.Z);
+                EventParams eps = new EventParams(eventname, 
+                                                  new Object[] { vec }, 
+                                                  zeroDetectParams);
+                this.PostObjectEvent(localID, eps);
+            }
+        }
+
+        // timer: not handled here
+        // listen: not handled here
+
+        public void control(UUID itemID, UUID agentID, uint held, uint change)
+        {
+            this.PostScriptEvent(itemID, new EventParams(
+                    "control",new object[] {
+                    agentID.ToString(),
+                    (int)held,
+                    (int)change},
+                    zeroDetectParams));
+        }
+
+        public void email(uint localID, UUID itemID, string timeSent,
+                string address, string subject, string message, int numLeft)
+        {
+            this.PostObjectEvent(localID, new EventParams(
+                    "email",new object[] {
+                    timeSent,
+                    address,
+                    subject,
+                    message,
+                    numLeft},
+                    zeroDetectParams));
+        }
+
+        public void at_target(uint localID, uint handle, Vector3 targetpos,
+                Vector3 atpos)
+        {
+            this.PostObjectEvent(localID, new EventParams(
+                    "at_target", new object[] {
+                    (int)handle,
+                    new LSL_Vector(targetpos.X,targetpos.Y,targetpos.Z),
+                    new LSL_Vector(atpos.X,atpos.Y,atpos.Z) },
+                    zeroDetectParams));
+        }
+
+        public void not_at_target(uint localID)
+        {
+            this.PostObjectEvent(localID, new EventParams(
+                    "not_at_target",zeroObjectArray,
+                    zeroDetectParams));
+        }
+
+        public void at_rot_target(uint localID, uint handle, OpenMetaverse.Quaternion targetrot, OpenMetaverse.Quaternion atrot)
+        {
+            this.PostObjectEvent(
+                localID, 
+                new EventParams(
+                    "at_rot_target",
+                    new object[] {
+                        new LSL_Integer(handle), 
+                        new LSL_Rotation(targetrot.X, targetrot.Y, targetrot.Z, targetrot.W),
+                        new LSL_Rotation(atrot.X, atrot.Y, atrot.Z, atrot.W)
+                    },
+                    zeroDetectParams
+                )
+            );
+        }
+
+        public void not_at_rot_target(uint localID)
+        {
+            this.PostObjectEvent(localID, new EventParams(
+                    "not_at_rot_target",zeroObjectArray,
+                    zeroDetectParams));
+        }
+
+        // run_time_permissions: not handled here
+
+        public void attach(uint localID, UUID itemID, UUID avatar)
+        {
+            this.PostObjectEvent(localID, new EventParams(
+                    "attach",new object[] {
+                    avatar.ToString() },
+                    zeroDetectParams));
+        }
+
+        // dataserver: not handled here
+        // link_message: not handled here
+
+        public void moving_start(uint localID)
+        {
+            this.PostObjectEvent(localID, new EventParams(
+                    "moving_start",zeroObjectArray,
+                    zeroDetectParams));
+        }
+
+        public void moving_end(uint localID)
+        {
+            this.PostObjectEvent(localID, new EventParams(
+                    "moving_end",zeroObjectArray,
+                    zeroDetectParams));
+        }
+
+        // object_rez: not handled here
+        // remote_data: not handled here
+        // http_response: not handled here
+    }
+}

+ 172 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRHeapTracker.cs

@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public class HeapTrackerBase {
+        private int usage;
+        private XMRInstAbstract instance;
+
+        public HeapTrackerBase (XMRInstAbstract inst)
+        {
+            if (inst == null) throw new ArgumentNullException ("inst");
+            instance = inst;
+        }
+
+        ~HeapTrackerBase ()
+        {
+            usage = instance.UpdateHeapUse (usage, 0);
+        }
+
+        protected void NewUse (int newuse)
+        {
+            usage = instance.UpdateHeapUse (usage, newuse);
+        }
+    }
+
+    public class HeapTrackerList : HeapTrackerBase {
+        private LSL_List value;
+
+        public HeapTrackerList (XMRInstAbstract inst) : base (inst) { }
+
+        public void Pop (LSL_List lis)
+        {
+            NewUse (Size (lis));
+            value = lis;
+        }
+
+        public LSL_List Push ()
+        {
+            return value;
+        }
+
+        public static int Size (LSL_List lis)
+        {
+            return (!typeof (LSL_List).IsValueType && (lis == null)) ? 0 : lis.Size;
+        }
+    }
+
+    public class HeapTrackerObject : HeapTrackerBase {
+        public const int HT_CHAR = 2;
+        public const int HT_DELE = 8;
+        public const int HT_DOUB = 8;
+        public const int HT_SING = 4;
+        public const int HT_SFLT = 4;
+        public const int HT_INT  = 4;
+        public const int HT_VEC  = HT_DOUB * 3;
+        public const int HT_ROT  = HT_DOUB * 4;
+
+        private object value;
+
+        public HeapTrackerObject (XMRInstAbstract inst) : base (inst) { }
+
+        public void Pop (object obj)
+        {
+            NewUse (Size (obj));
+            value = obj;
+        }
+
+        public object Push ()
+        {
+            return value;
+        }
+
+        public static int Size (object obj)
+        {
+            if (obj == null) return 0;
+
+            if (obj is char)            return HT_CHAR;
+            if (obj is Delegate)        return HT_DELE;
+            if (obj is double)          return HT_DOUB;
+            if (obj is float)           return HT_SING;
+            if (obj is int)             return HT_INT;
+            if (obj is LSL_Float)       return HT_SFLT;
+            if (obj is LSL_Integer)     return HT_INT;
+            if (obj is LSL_List)        return ((LSL_List)obj).Size;
+            if (obj is LSL_Rotation)    return HT_ROT;
+            if (obj is LSL_String)      return ((LSL_String)obj).m_string.Length * HT_CHAR;
+            if (obj is LSL_Vector)      return HT_VEC;
+            if (obj is string)          return ((string)obj).Length * HT_CHAR;
+            if (obj is XMR_Array)       return 0;
+            if (obj is XMRArrayListKey) return ((XMRArrayListKey)obj).Size;
+            if (obj is XMRSDTypeClObj)  return 0;
+
+            if (obj is Array) {
+                Array ar = (Array)obj;
+                int len = ar.Length;
+                if (len == 0) return 0;
+                Type et = ar.GetType ().GetElementType ();
+                if (et.IsValueType) return Size (ar.GetValue (0)) * len;
+                int size = 0;
+                for (int i = 0; i < len; i ++) {
+                    size += Size (ar.GetValue (i));
+                }
+                return size;
+            }
+
+            throw new Exception ("unknown size of type " + obj.GetType ().Name);
+        }
+    }
+
+    public class HeapTrackerString : HeapTrackerBase {
+        private string value;
+
+        public HeapTrackerString (XMRInstAbstract inst) : base (inst) { }
+
+        public void Pop (string str)
+        {
+            NewUse (Size (str));
+            value = str;
+        }
+
+        public string Push ()
+        {
+            return value;
+        }
+
+        public static int Size (string str)
+        {
+            return (str == null) ? 0 : str.Length * HeapTrackerObject.HT_CHAR;
+        }
+    }
+}

+ 2031 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRInstAbstract.cs

@@ -0,0 +1,2031 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Reflection.Emit;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public class XMRInstArrays {
+        public XMR_Array[]      iarArrays;
+        public char[]           iarChars;
+        public double[]    iarFloats;
+        public int[]            iarIntegers;
+        public LSL_List[]       iarLists;
+        public object[]         iarObjects;
+        public LSL_Rotation[]   iarRotations;
+        public string[]         iarStrings;
+        public LSL_Vector[]     iarVectors;
+        public XMRSDTypeClObj[] iarSDTClObjs;
+        public Delegate[][]     iarSDTIntfObjs;
+
+        private XMRInstAbstract instance;
+        private int heapUse;
+
+        private static readonly XMR_Array[]      noArrays      = new XMR_Array[0];
+        private static readonly char[]           noChars       = new char[0];
+        private static readonly double[]    noFloats      = new double[0];
+        private static readonly int[]            noIntegers    = new int[0];
+        private static readonly LSL_List[]       noLists       = new LSL_List[0];
+        private static readonly object[]         noObjects     = new object[0];
+        private static readonly LSL_Rotation[]   noRotations   = new LSL_Rotation[0];
+        private static readonly string[]         noStrings     = new string[0];
+        private static readonly LSL_Vector[]     noVectors     = new LSL_Vector[0];
+        private static readonly XMRSDTypeClObj[] noSDTClObjs   = new XMRSDTypeClObj[0];
+        private static readonly Delegate[][]     noSDTIntfObjs = new Delegate[0][];
+
+        public XMRInstArrays (XMRInstAbstract inst)
+        {
+            instance = inst;
+        }
+
+        ~XMRInstArrays ()
+        {
+            heapUse = instance.UpdateHeapUse (heapUse, 0);
+        }
+
+        public void AllocVarArrays (XMRInstArSizes ars)
+        {
+            ClearOldArrays ();
+
+            heapUse = instance.UpdateHeapUse (heapUse,
+                ars.iasChars       * HeapTrackerObject.HT_CHAR +
+                ars.iasFloats      * HeapTrackerObject.HT_SFLT +
+                ars.iasIntegers    * HeapTrackerObject.HT_INT +
+                ars.iasRotations   * HeapTrackerObject.HT_ROT +
+                ars.iasVectors     * HeapTrackerObject.HT_VEC +
+                ars.iasSDTIntfObjs * HeapTrackerObject.HT_DELE);
+
+            iarArrays      = (ars.iasArrays      > 0) ? new XMR_Array     [ars.iasArrays]        : noArrays;
+            iarChars       = (ars.iasChars       > 0) ? new char          [ars.iasChars]         : noChars;
+            iarFloats      = (ars.iasFloats      > 0) ? new double   [ars.iasFloats]        : noFloats;
+            iarIntegers    = (ars.iasIntegers    > 0) ? new int           [ars.iasIntegers]      : noIntegers;
+            iarLists       = (ars.iasLists       > 0) ? new LSL_List      [ars.iasLists]         : noLists;
+            iarObjects     = (ars.iasObjects     > 0) ? new object        [ars.iasObjects]       : noObjects;
+            iarRotations   = (ars.iasRotations   > 0) ? new LSL_Rotation  [ars.iasRotations]     : noRotations;
+            iarStrings     = (ars.iasStrings     > 0) ? new string        [ars.iasStrings]       : noStrings;
+            iarVectors     = (ars.iasVectors     > 0) ? new LSL_Vector    [ars.iasVectors]       : noVectors;
+            iarSDTClObjs   = (ars.iasSDTClObjs   > 0) ? new XMRSDTypeClObj[ars.iasSDTClObjs]     : noSDTClObjs;
+            iarSDTIntfObjs = (ars.iasSDTIntfObjs > 0) ? new Delegate      [ars.iasSDTIntfObjs][] : noSDTIntfObjs;
+        }
+
+        /**
+         * @brief Do not write directly to iarLists[index], rather use this method.
+         */
+        public void PopList (int index, LSL_List lis)
+        {
+            LSL_List old = iarLists[index];
+            int newheapuse = heapUse + HeapTrackerList.Size (lis) - HeapTrackerList.Size (old);
+            heapUse = instance.UpdateHeapUse (heapUse, newheapuse);
+            iarLists[index] = lis;
+        }
+
+        /**
+         * @brief Do not write directly to iarObjects[index], rather use this method.
+         */
+        public void PopObject (int index, object obj)
+        {
+            object old = iarObjects[index];
+            int newheapuse = heapUse + HeapTrackerObject.Size (obj) - HeapTrackerObject.Size (old);
+            heapUse = instance.UpdateHeapUse (heapUse, newheapuse);
+            iarObjects[index] = obj;
+        }
+
+        /**
+         * @brief Do not write directly to iarStrings[index], rather use this method.
+         */
+        public void PopString (int index, string str)
+        {
+            string old = iarStrings[index];
+            int newheapuse = heapUse + HeapTrackerString.Size (str) - HeapTrackerString.Size (old);
+            heapUse = instance.UpdateHeapUse (heapUse, newheapuse);
+            iarStrings[index] = str;
+        }
+
+        /**
+         * @brief Write all arrays out to a file.
+         */
+        public delegate void Sender (object value);
+        public void SendArrays (Sender sender)
+        {
+            sender (iarArrays);
+            sender (iarChars);
+            sender (iarFloats);
+            sender (iarIntegers);
+            sender (iarLists);
+            sender (iarObjects);
+            sender (iarRotations);
+            sender (iarStrings);
+            sender (iarVectors);
+            sender (iarSDTClObjs);
+            sender (iarSDTIntfObjs);
+        }
+
+        /**
+         * @brief Read all arrays in from a file.
+         */
+        public delegate object Recver ();
+        public void RecvArrays (Recver recver)
+        {
+            ClearOldArrays ();
+
+            iarArrays           = (XMR_Array[])      recver ();
+            char[] chrs         = (char[])           recver ();
+            double[] flts  = (double[])    recver ();
+            int[] ints          = (int[])            recver ();
+            LSL_List[] liss     = (LSL_List[])       recver ();
+            object[] objs       = (object[])         recver ();
+            LSL_Rotation[] rots = (LSL_Rotation[])   recver ();
+            string[] strs       = (string[])         recver ();
+            LSL_Vector[] vecs   = (LSL_Vector[])     recver ();
+            iarSDTClObjs        = (XMRSDTypeClObj[]) recver ();
+            Delegate[][] dels   = (Delegate[][])     recver ();
+
+            int newheapuse = heapUse;
+
+            // value types simply are the size of the value * number of values
+            newheapuse += chrs.Length * HeapTrackerObject.HT_CHAR;
+            newheapuse += flts.Length * HeapTrackerObject.HT_SFLT;
+            newheapuse += ints.Length * HeapTrackerObject.HT_INT;
+            newheapuse += rots.Length * HeapTrackerObject.HT_ROT;
+            newheapuse += vecs.Length * HeapTrackerObject.HT_VEC;
+            newheapuse += dels.Length * HeapTrackerObject.HT_DELE;
+
+            // lists, objects, strings are the sum of the size of each element
+            foreach (LSL_List lis in liss) {
+                newheapuse += HeapTrackerList.Size (lis);
+            }
+            foreach (object obj in objs) {
+                newheapuse += HeapTrackerObject.Size (obj);
+            }
+            foreach (string str in strs) {
+                newheapuse += HeapTrackerString.Size (str);
+            }
+
+            // others (XMR_Array, XMRSDTypeClObj) keep track of their own heap usage
+
+            // update script heap usage, throwing an exception before finalizing changes
+            heapUse = instance.UpdateHeapUse (heapUse, newheapuse);
+
+            iarChars       = chrs;
+            iarFloats      = flts;
+            iarIntegers    = ints;
+            iarLists       = liss;
+            iarObjects     = objs;
+            iarRotations   = rots;
+            iarStrings     = strs;
+            iarVectors     = vecs;
+            iarSDTIntfObjs = dels;
+        }
+
+        private void ClearOldArrays ()
+        {
+            int newheapuse = heapUse;
+
+            iarArrays = null;
+            if (iarChars != null) {
+                newheapuse -= iarChars.Length * HeapTrackerObject.HT_CHAR;
+                iarChars = null;
+            }
+            if (iarFloats != null) {
+                newheapuse -= iarFloats.Length * HeapTrackerObject.HT_SFLT;
+                iarFloats = null;
+            }
+            if (iarIntegers != null) {
+                newheapuse -= iarIntegers.Length * HeapTrackerObject.HT_INT;
+                iarIntegers = null;
+            }
+            if (iarLists != null) {
+                foreach (LSL_List lis in iarLists) {
+                    newheapuse -= HeapTrackerList.Size (lis);
+                }
+                iarLists = null;
+            }
+            if (iarObjects != null) {
+                foreach (object obj in iarObjects) {
+                    newheapuse -= HeapTrackerObject.Size (obj);
+                }
+                iarObjects = null;
+            }
+            if (iarRotations != null) {
+                newheapuse -= iarRotations.Length * HeapTrackerObject.HT_ROT;
+                iarRotations = null;
+            }
+            if (iarStrings != null) {
+                foreach (string str in iarStrings) {
+                    newheapuse -= HeapTrackerString.Size (str);
+                }
+                iarStrings = null;
+            }
+            if (iarVectors != null) {
+                newheapuse -= iarVectors.Length * HeapTrackerObject.HT_VEC;
+                iarVectors = null;
+            }
+            iarSDTClObjs = null;
+            if (iarSDTIntfObjs != null) {
+                newheapuse -= iarSDTIntfObjs.Length * HeapTrackerObject.HT_DELE;
+                iarSDTIntfObjs = null;
+            }
+
+            heapUse = instance.UpdateHeapUse (heapUse, newheapuse);
+        }
+    }
+
+    public class XMRInstArSizes {
+        public int iasArrays;
+        public int iasChars;
+        public int iasFloats;
+        public int iasIntegers;
+        public int iasLists;
+        public int iasObjects;
+        public int iasRotations;
+        public int iasStrings;
+        public int iasVectors;
+        public int iasSDTClObjs;
+        public int iasSDTIntfObjs;
+
+        public void WriteAsmFile (TextWriter asmFileWriter, string label)
+        {
+            asmFileWriter.WriteLine ("  {0}Arrays       {1}", label, iasArrays);
+            asmFileWriter.WriteLine ("  {0}Chars        {1}", label, iasChars);
+            asmFileWriter.WriteLine ("  {0}Floats       {1}", label, iasFloats);
+            asmFileWriter.WriteLine ("  {0}Integers     {1}", label, iasIntegers);
+            asmFileWriter.WriteLine ("  {0}Lists        {1}", label, iasLists);
+            asmFileWriter.WriteLine ("  {0}Objects      {1}", label, iasObjects);
+            asmFileWriter.WriteLine ("  {0}Rotations    {1}", label, iasRotations);
+            asmFileWriter.WriteLine ("  {0}Strings      {1}", label, iasStrings);
+            asmFileWriter.WriteLine ("  {0}Vectors      {1}", label, iasVectors);
+            asmFileWriter.WriteLine ("  {0}SDTClObjs    {1}", label, iasSDTClObjs);
+            asmFileWriter.WriteLine ("  {0}SDTIntfObjs  {1}", label, iasSDTIntfObjs);
+        }
+        public void WriteToFile (BinaryWriter objFileWriter)
+        {
+            objFileWriter.Write (iasArrays);
+            objFileWriter.Write (iasChars);
+            objFileWriter.Write (iasFloats);
+            objFileWriter.Write (iasIntegers);
+            objFileWriter.Write (iasLists);
+            objFileWriter.Write (iasObjects);
+            objFileWriter.Write (iasRotations);
+            objFileWriter.Write (iasStrings);
+            objFileWriter.Write (iasVectors);
+            objFileWriter.Write (iasSDTClObjs);
+            objFileWriter.Write (iasSDTIntfObjs);
+        }
+        public void ReadFromFile (BinaryReader objFileReader)
+        {
+            iasArrays      = objFileReader.ReadInt32 ();
+            iasChars       = objFileReader.ReadInt32 ();
+            iasFloats      = objFileReader.ReadInt32 ();
+            iasIntegers    = objFileReader.ReadInt32 ();
+            iasLists       = objFileReader.ReadInt32 ();
+            iasObjects     = objFileReader.ReadInt32 ();
+            iasRotations   = objFileReader.ReadInt32 ();
+            iasStrings     = objFileReader.ReadInt32 ();
+            iasVectors     = objFileReader.ReadInt32 ();
+            iasSDTClObjs   = objFileReader.ReadInt32 ();
+            iasSDTIntfObjs = objFileReader.ReadInt32 ();
+        }
+    }
+
+    public class XMRStackFrame {
+        public XMRStackFrame nextSF;
+        public string funcName;
+        public int callNo;
+        public object[] objArray;
+    }
+
+    /*
+     * Contains only items required by the stand-alone compiler
+     * so the compiler doesn't need to pull in all of OpenSim.
+     *
+     * Inherit from ScriptBaseClass so we can be used as 'this'
+     * parameter for backend-API calls, eg llSay().
+     */
+    public abstract class XMRInstAbstract : ScriptBaseClass
+    {
+        public const int CallMode_NORMAL  = 0;  // when function is called, it proceeds normally
+        public const int CallMode_SAVE    = 1;  // StackSaveException() was thrown, push args/locals to stackFrames
+        public const int CallMode_RESTORE = 2;  // when function is called, it pops state from stackFrames
+
+        public bool suspendOnCheckRunHold;  // suspend script execution until explicitly set false
+        public bool suspendOnCheckRunTemp;  // suspend script execution for single step only
+        public int stackLimit;              // stack must have at least this many bytes free on entry to functions
+
+        public ScriptObjCode m_ObjCode;     // script object code this instance was created from
+
+        public object[] ehArgs;             // event handler argument array
+        public bool doGblInit = true;       // default state_entry() needs to initialize global variables
+        public int stateCode = 0;           // state the script is in (0 = 'default')
+        public int newStateCode = -1;       // if >= 0, in the middle of exiting 'stateCode' and entering 'newStateCode'
+        public ScriptEventCode eventCode = ScriptEventCode.None;
+                                            // what event handler is executing (or None if not)
+
+        public int callMode = CallMode_NORMAL;
+                                            // to capture stack frames on stackFrames:
+                                            //    set to CallMode_SAVE just before throwing StackSaveException()
+                                            //    from within CheckRun() and cleared to CallMode_NORMAL when
+                                            //    the exception is caught
+                                            // to restore stack frames from stackFrames:
+                                            //    set to CallMode_RESTORE just before calling CallSEH() and 
+                                            //    cleared to CallMode_NORMAL by CheckRun()
+        public XMRStackFrame stackFrames;   // stack frames being saved/restored
+
+        private static readonly char[] justacomma = { ',' };
+
+        /*
+         * These arrays hold the global variable values for the script instance.
+         * The array lengths are determined by the script compilation,
+         * and are found in ScriptObjCode.glblSizes.
+         */
+        public XMRInstArrays glblVars;
+
+        public XMRInstAbstract ()
+        {
+            glblVars = new XMRInstArrays (this);
+        }
+
+        /****************************************************************\
+         *  Abstract function prototypes.                               *
+         *  These functions require access to the OpenSim environment.  *
+        \****************************************************************/
+
+        public abstract void CheckRunWork ();
+        public abstract void StateChange  ();
+        public abstract int  xmrStackLeft ();
+
+        [xmrMethodCallsCheckRunAttribute] // calls CheckRun()
+        [xmrMethodIsNoisyAttribute]       // calls Stub<somethingorother>()
+        public abstract LSL_List xmrEventDequeue (double timeout, int returnMask1, int returnMask2,
+                                                  int backgroundMask1, int backgroundMask2);
+
+        [xmrMethodIsNoisyAttribute]       // calls Stub<somethingorother>()
+        public abstract void xmrEventEnqueue (LSL_List ev);
+
+        [xmrMethodIsNoisyAttribute]       // calls Stub<somethingorother>()
+        public abstract LSL_List xmrEventSaveDets ();
+
+        [xmrMethodIsNoisyAttribute]       // calls Stub<somethingorother>()
+        public abstract void xmrEventLoadDets (LSL_List dpList);
+
+        [xmrMethodIsNoisyAttribute]       // calls Stub<somethingorother>()
+        public abstract void xmrTrapRegionCrossing (int en);
+
+        [xmrMethodIsNoisyAttribute]       // calls Stub<somethingorother>()
+        public abstract bool xmrSetObjRegPosRotAsync (LSL_Vector pos, LSL_Rotation rot, int options, int evcode, LSL_List evargs);
+
+        /************************************\
+         *  Constants available to scripts  *
+        \************************************/
+
+        public const int XMRSORPRA_FLYACROSS = 0x00000001;
+
+        /**************************************************\
+         *  Functions what don't require runtime support  *
+         *  beyond what the compiler provides.            *
+        \**************************************************/
+
+        protected int heapLimit;
+        private   int heapUsed;
+
+        public virtual int UpdateHeapUse (int olduse, int newuse)
+        {
+            if (newuse <= olduse) {
+                Interlocked.Add (ref heapUsed, newuse - olduse);
+            } else {
+                int newtotal, oldtotal;
+                do {
+                    oldtotal = Interlocked.Add (ref heapUsed, 0);
+                    newtotal = oldtotal + newuse - olduse;
+                    if (newtotal > heapLimit) {
+                        System.GC.Collect ();
+                        System.GC.WaitForPendingFinalizers ();
+                        oldtotal = Interlocked.Add (ref heapUsed, 0);
+                        newtotal = oldtotal + newuse - olduse;
+                        if (newtotal > heapLimit) {
+                            throw new OutOfHeapException (oldtotal, newtotal, heapLimit);
+                        }
+                    }
+                } while (Interlocked.CompareExchange (ref heapUsed, newtotal, oldtotal) != oldtotal);
+            }
+
+            return newuse;
+        }
+
+        public int xmrHeapLeft ()
+        {
+            return heapLimit - heapUsed;
+        }
+        public int xmrHeapUsed ()
+        {
+            return heapUsed;
+        }
+
+        /**
+         * @brief Call script's event handler function from the very beginning.
+         * @param instance.stateCode = which state the event is happening in
+         * @param instance.eventCode = which event is happening in that state
+         * @returns when event handler has completed or throws an exception
+         *          with instance.eventCode = ScriptEventCode.None
+         */
+        public void CallSEH ()
+        {
+            ScriptEventHandler seh;
+
+            /*
+             * CallMode_NORMAL:  run event handler from the beginning normally
+             * CallMode_RESTORE: restore event handler stack from stackFrames
+             */
+            callMode = (stackFrames == null) ? XMRInstAbstract.CallMode_NORMAL :
+                                               XMRInstAbstract.CallMode_RESTORE;
+
+            while (true) {
+                if (this.newStateCode < 0) {
+
+                    /*
+                     * Process event given by 'stateCode' and 'eventCode'.
+                     * The event handler should call CheckRun() as often as convenient.
+                     */
+                    int newState = this.stateCode;
+                    seh = this.m_ObjCode.scriptEventHandlerTable[newState,(int)this.eventCode];
+                    if (seh != null) {
+                        try {
+                            seh (this);
+                        } catch (ScriptChangeStateException scse) {
+                            newState = scse.newState;
+                        }
+                    }
+                    this.ehArgs = null;  // we are done with them and no args for
+                                         // exit_state()/enter_state() anyway
+
+                    /*
+                     * The usual case is no state change.
+                     * Even a 'state <samestate>;' statement has no effect except to exit out.
+                     * It does not execute the state_exit() or state_entry() handlers.
+                     * See http://wiki.secondlife.com/wiki/State
+                     */
+                    if (newState == this.stateCode) break;
+
+                    /*
+                     * Save new state in a more permanent location in case we
+                     * get serialized out while in the state_exit() handler.
+                     */
+                    this.newStateCode = newState;
+                }
+
+                /*
+                 * Call old state's state_exit() handler.
+                 */
+                this.eventCode = ScriptEventCode.state_exit;
+                seh = this.m_ObjCode.scriptEventHandlerTable[this.stateCode,(int)ScriptEventCode.state_exit];
+                if (seh != null) {
+                    try {
+                        seh (this);
+                    } catch (ScriptChangeStateException scse) {
+                        this.newStateCode = scse.newState;
+                    }
+                }
+
+                /*
+                 * Switch over to the new state's state_entry() handler.
+                 */
+                this.stateCode    = this.newStateCode;
+                this.eventCode    = ScriptEventCode.state_entry;
+                this.newStateCode = -1;
+
+                /*
+                 * Now that the old state can't possibly start any more activity,
+                 * cancel any listening handlers, etc, of the old state.
+                 */
+                this.StateChange ();
+
+                /*
+                 * Loop back to execute new state's state_entry() handler.
+                 */
+            }
+
+            /*
+             * Event no longer being processed.
+             */
+            this.eventCode = ScriptEventCode.None;
+        }
+
+        /**
+         * @brief For compatibility with old code.
+         */
+        public void CheckRun (int line)
+        {
+            CheckRunStack ();
+        }
+
+        /**
+         * @brief Called at beginning of complex functions to see if they
+         *        are nested too deep possibly in a recursive loop.
+         */
+        public void CheckRunStack ()
+        {
+            if (xmrStackLeft () < stackLimit) {
+                throw new OutOfStackException ();
+            }
+            CheckRunQuick ();
+        }
+
+        /**
+         * @brief Called in each iteration of a loop to see if running too long.
+         */
+        public void CheckRunQuick ()
+        {
+            if (suspendOnCheckRunHold || suspendOnCheckRunTemp) {
+                CheckRunWork ();
+            }
+        }
+
+        /**
+         * @brief Called during CallMode_SAVE to create a stackframe save object that saves 
+         *        local variables and calling point within the function.
+         * @param funcName = name of function whose frame is being saved
+         * @param callNo = call number (ie, return address) within function to restart at
+         * @param nSaves = number of variables the function will save
+         * @returns an object[nSaves] where function can save variables
+         */
+        public object[] CaptureStackFrame (string funcName, int callNo, int nSaves)
+        {
+            XMRStackFrame sf = new XMRStackFrame ();
+            sf.nextSF   = stackFrames;
+            sf.funcName = funcName;
+            sf.callNo   = callNo;
+            sf.objArray = new object[nSaves];
+            stackFrames = sf;
+            return sf.objArray;
+        }
+
+        /**
+         * @brief Called during CallMode_RESTORE to pop a stackframe object to restore 
+         *        local variables and calling point within the function.
+         * @param funcName = name of function whose frame is being restored
+         * @returns the object[nSaves] where function can retrieve variables
+         *          callNo = as passed to CaptureStackFrame() indicating restart point
+         */
+        public object[] RestoreStackFrame (string funcName, out int callNo)
+        {
+            XMRStackFrame sf = stackFrames;
+            if (sf.funcName != funcName) {
+                throw new Exception ("frame mismatch " + sf.funcName + " vs " + funcName);
+            }
+            callNo = sf.callNo;
+            stackFrames = sf.nextSF;
+            return sf.objArray;
+        }
+
+        /**
+         * @brief Convert all LSL_Integers in a list to System.Int32s, 
+         *        as required by llParcelMediaQuery().
+         */
+        public static LSL_List FixLLParcelMediaQuery (LSL_List oldlist)
+        {
+            object[] oldarray = oldlist.Data;
+            int len = oldarray.Length;
+            object[] newarray = new object[len];
+            for (int i = 0; i < len; i ++) {
+                object obj = oldarray[i];
+                if (obj is LSL_Integer) obj = (int)(LSL_Integer)obj;
+                newarray[i] = obj;
+            }
+            return new LSL_List (newarray);
+        }
+
+        /**
+         * @brief Convert *SOME* LSL_Integers in a list to System.Int32s, 
+         *        as required by llParcelMediaCommandList().
+         */
+        public static LSL_List FixLLParcelMediaCommandList (LSL_List oldlist)
+        {
+            object[] oldarray = oldlist.Data;
+            int len = oldarray.Length;
+            object[] newarray = new object[len];
+            int verbatim = 0;
+            for (int i = 0; i < len; i ++) {
+                object obj = oldarray[i];
+                if (-- verbatim < 0) {
+                    if (obj is LSL_Integer) obj = (int)(LSL_Integer)obj;
+                    if (obj is int) {
+                        switch ((int)obj) {
+                            case ScriptBaseClass.PARCEL_MEDIA_COMMAND_AUTO_ALIGN: {
+                                // leave next integer as LSL_Integer
+                                verbatim = 1;
+                                break;
+                            }
+                            case ScriptBaseClass.PARCEL_MEDIA_COMMAND_SIZE: {
+                                // leave next two integers as LSL_Integer
+                                verbatim = 2;
+                                break;
+                            }
+                        }
+                    }
+                }
+                newarray[i] = obj;
+            }
+            return new LSL_List (newarray);
+        }
+
+        public static int xmrHashCode (int i)
+        {
+            return i.GetHashCode ();
+        }
+        public static int xmrHashCode (double f)
+        {
+            return f.GetHashCode ();
+        }
+        public static int xmrHashCode (object o)
+        {
+            return o.GetHashCode ();
+        }
+        public static int xmrHashCode (string s)
+        {
+            return s.GetHashCode ();
+        }
+
+        public bool xmrSetObjRegPosRotAsync (LSL_Vector pos, LSL_Rotation rot, int evcode, LSL_List evargs)
+        {
+            return xmrSetObjRegPosRotAsync (pos, rot, 0, evcode, evargs);
+        }
+
+        public string xmrTypeName (object o)
+        {
+            /*
+             * Basic types return constant strings of the script-visible type name.
+             */
+            if (o is XMR_Array)    return "array";
+            if (o is bool)         return "bool";
+            if (o is char)         return "char";
+            if (o is Exception)    return "exception";
+            if (o is double)       return "float";
+            if (o is float)        return "float";
+            if (o is LSL_Float)    return "float";
+            if (o is int)          return "integer";
+            if (o is LSL_Integer)  return "integer";
+            if (o is LSL_List)     return "list";
+            if (o is LSL_Rotation) return "rotation";
+            if (o is LSL_String)   return "string";
+            if (o is string)       return "string";
+            if (o is LSL_Vector)   return "vector";
+
+            /*
+             * A script-defined interface is represented as an array of delegates.
+             * If that is the case, convert it to the object of the script-defined 
+             * class that is implementing the interface.  This should let the next 
+             * step get the script-defined type name of the object.
+             */
+            if (o is Delegate[]) {
+                o = ((Delegate[])o)[0].Target;
+            }
+
+            /*
+             * If script-defined class instance, get the script-defined 
+             * type name.
+             */
+            if (o is XMRSDTypeClObj) {
+                return ((XMRSDTypeClObj)o).sdtcClass.longName.val;
+            }
+
+            /*
+             * If it's a delegate, maybe we can look up its script-defined type name.
+             */
+            Type ot = o.GetType ();
+            if (o is Delegate) {
+                String os;
+                if (m_ObjCode.sdDelTypes.TryGetValue (ot, out os)) return os;
+            }
+
+            /*
+             * Don't know what it is, get the C#-level type name.
+             */
+            return ot.ToString ();
+        }
+
+        /**
+         * @brief Call the current state's event handler.
+         * @param ev = as returned by xmrEventDequeue saying which event handler to call
+         *             and what argument list to pass to it.  The llDetect...() parameters
+         *             are as currently set for the script (use xmrEventLoadDets to set how
+         *             you want them to be different).
+         */
+        public void xmrEventCallHandler (LSL_List ev)
+        {
+            object[] data = ev.Data;
+            int evc = (int)(ev.GetLSLIntegerItem (0).value & 0xFFFFFFFF);
+            ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode,evc];
+            if (seh != null) {
+                int nargs = data.Length - 1;
+                object[] args = new object[nargs];
+                Array.Copy (data, 1, args, 0, nargs);
+
+                object[]        saveEHArgs    = this.ehArgs;
+                ScriptEventCode saveEventCode = this.eventCode;
+
+                this.ehArgs    = args;
+                this.eventCode = (ScriptEventCode)evc;
+
+                seh (this);
+
+                this.ehArgs    = saveEHArgs;
+                this.eventCode = saveEventCode;
+            }
+        }
+
+        /**
+         * @brief Sane substring functions.
+         */
+        public string xmrSubstring (string s, int offset)
+        {
+            if (offset >= s.Length) return "";
+            return s.Substring (offset);
+        }
+        // C# style
+        public string xmrSubstring (string s, int offset, int length)
+        {
+            if (length <= 0) return "";
+            if (offset >= s.Length) return "";
+            if (length > s.Length - offset) length = s.Length - offset;
+            return s.Substring (offset, length);
+        }
+        // java style
+        public string xmrJSubstring (string s, int beg, int end)
+        {
+            if (end <= beg) return "";
+            if (beg >= s.Length) return "";
+            if (end >  s.Length) end = s.Length;
+            return s.Substring (beg, end - beg);
+        }
+
+        /**
+         * @brief String begins and ends with test.
+         */
+        public bool xmrStringStartsWith (string s, string t)
+        {
+            return s.StartsWith (t);
+        }
+        public bool xmrStringEndsWith (string s, string t)
+        {
+            return s.EndsWith (t);
+        }
+
+        /**
+         * @brief [Last]IndexOf with starting position (just like C#)
+         */
+        public int xmrStringIndexOf (string haystack, string needle)
+        {
+            return haystack.IndexOf (needle);
+        }
+        public int xmrStringIndexOf (string haystack, string needle, int startat)
+        {
+            return haystack.IndexOf (needle, startat);
+        }
+        public int xmrStringLastIndexOf (string haystack, string needle)
+        {
+            return haystack.LastIndexOf (needle);
+        }
+        public int xmrStringLastIndexOf (string haystack, string needle, int startat)
+        {
+            return haystack.LastIndexOf (needle, startat);
+        }
+
+        /**
+         * @brief These conversions throw exceptions if there is anything stinky...
+         */
+        public double xmrString2Float (string s)
+        {
+            return double.Parse (s, CultureInfo.InvariantCulture);
+        }
+        public int xmrString2Integer (string s)
+        {
+            s = s.Trim ();
+            if (s.StartsWith ("0x") || s.StartsWith ("0X")) {
+                return int.Parse (s.Substring (2), NumberStyles.HexNumber);
+            }
+            return int.Parse (s, CultureInfo.InvariantCulture);
+        }
+        public LSL_Rotation xmrString2Rotation (string s)
+        {
+            s = s.Trim ();
+            if (!s.StartsWith ("<") || !s.EndsWith (">")) {
+                throw new FormatException ("doesn't begin with < and end with >");
+            }
+            s = s.Substring (1, s.Length - 2);
+            string[] splitup = s.Split (justacomma, 5);
+            if (splitup.Length != 4) {
+                throw new FormatException ("doesn't have exactly 3 commas");
+            }
+            double x = double.Parse (splitup[0], CultureInfo.InvariantCulture);
+            double y = double.Parse (splitup[1], CultureInfo.InvariantCulture);
+            double z = double.Parse (splitup[2], CultureInfo.InvariantCulture);
+            double w = double.Parse (splitup[3], CultureInfo.InvariantCulture);
+            return new LSL_Rotation (x, y, z, w);
+        }
+        public LSL_Vector xmrString2Vector (string s)
+        {
+            s = s.Trim ();
+            if (!s.StartsWith ("<") || !s.EndsWith (">")) {
+                throw new FormatException ("doesn't begin with < and end with >");
+            }
+            s = s.Substring (1, s.Length - 2);
+            string[] splitup = s.Split (justacomma, 4);
+            if (splitup.Length != 3) {
+                throw new FormatException ("doesn't have exactly 2 commas");
+            }
+            double x = double.Parse (splitup[0], CultureInfo.InvariantCulture);
+            double y = double.Parse (splitup[1], CultureInfo.InvariantCulture);
+            double z = double.Parse (splitup[2], CultureInfo.InvariantCulture);
+            return new LSL_Vector (x, y, z);
+        }
+
+        /**
+         * @brief Access C#-style formatted numeric conversions.
+         */
+        public string xmrFloat2String (double val, string fmt)
+        {
+            return val.ToString (fmt, CultureInfo.InvariantCulture);
+        }
+        public string xmrInteger2String (int val, string fmt)
+        {
+            return val.ToString (fmt, CultureInfo.InvariantCulture);
+        }
+        public string xmrRotation2String (LSL_Rotation val, string fmt)
+        {
+            return "<" + val.x.ToString (fmt, CultureInfo.InvariantCulture) + "," +
+                         val.y.ToString (fmt, CultureInfo.InvariantCulture) + "," +
+                         val.z.ToString (fmt, CultureInfo.InvariantCulture) + "," +
+                         val.s.ToString (fmt, CultureInfo.InvariantCulture) + ">";
+        }
+        public string xmrVector2String (LSL_Vector val, string fmt)
+        {
+            return "<" + val.x.ToString (fmt, CultureInfo.InvariantCulture) + "," +
+                         val.y.ToString (fmt, CultureInfo.InvariantCulture) + "," +
+                         val.z.ToString (fmt, CultureInfo.InvariantCulture) + ">";
+        }
+
+        /**
+         * @brief Get a delegate for a script-defined function.
+         * @param name = name of the function including arg types, eg,
+         *               "Verify(array,list,string)"
+         * @param sig  = script-defined type name
+         * @param targ = function's 'this' pointer or null if static
+         * @returns delegate for the script-defined function
+         */
+        public Delegate GetScriptMethodDelegate (string name, string sig, object targ)
+        {
+            DynamicMethod dm = m_ObjCode.dynamicMethods[name];
+            TokenDeclSDTypeDelegate dt = (TokenDeclSDTypeDelegate)m_ObjCode.sdObjTypesName[sig];
+            return dm.CreateDelegate (dt.GetSysType (), targ);
+        }
+
+        /**
+         * @brief Try to cast the thrown object to the given script-defined type.
+         * @param thrown = what object was thrown
+         * @param inst = what script instance we are running in
+         * @param sdtypeindex = script-defined type to try to cast it to
+         * @returns null: thrown is not castable to sdtypename
+         *          else: an object casted to sdtypename
+         */
+        public static object XMRSDTypeCatchTryCastToSDType (object thrown, XMRInstAbstract inst, int sdtypeindex)
+        {
+            TokenDeclSDType sdType = inst.m_ObjCode.sdObjTypesIndx[sdtypeindex];
+
+            /*
+             * If it is a script-defined interface object, convert to the original XMRSDTypeClObj.
+             */
+            if (thrown is Delegate[]) {
+                thrown = ((Delegate[])thrown)[0].Target;
+            }
+
+            /*
+             * If it is a script-defined delegate object, make sure it is an instance of the expected type.
+             */
+            if (thrown is Delegate) {
+                Type ot = thrown.GetType ();
+                Type tt = sdType.GetSysType ();
+                return (ot == tt) ? thrown : null;
+            }
+
+            /*
+             * If it is a script-defined class object, make sure it is an instance of the expected class.
+             */
+            if (thrown is XMRSDTypeClObj) {
+
+                /*
+                 * Step from the object's actual class rootward.
+                 * If we find the requested class along the way, the cast is valid.
+                 * If we run off the end of the root, the cast is not valid.
+                 */
+                for (TokenDeclSDTypeClass ac = ((XMRSDTypeClObj)thrown).sdtcClass; ac != null; ac = ac.extends) {
+                    if (ac == sdType) return thrown;
+                }
+            }
+
+            /*
+             * Don't know what it is, assume it is not what caller wants.
+             */
+            return null;
+        }
+
+        /**
+         * @brief Allocate and access fixed-dimension arrays.
+         */
+        public static object xmrFixedArrayAllocC (int len) { return new   char[len]; }
+        public static object xmrFixedArrayAllocF (int len) { return new double[len]; }
+        public static object xmrFixedArrayAllocI (int len) { return new    int[len]; }
+        public static object xmrFixedArrayAllocO (int len) { return new object[len]; }
+
+        public static char   xmrFixedArrayGetC (object arr, int idx) { return (  (char[])arr)[idx]; }
+        public static double xmrFixedArrayGetF (object arr, int idx) { return ((double[])arr)[idx]; }
+        public static int    xmrFixedArrayGetI (object arr, int idx) { return (   (int[])arr)[idx]; }
+        public static object xmrFixedArrayGetO (object arr, int idx) { return ((object[])arr)[idx]; }
+
+        public static void xmrFixedArraySetC (object arr, int idx, char   val) {   ((char[])arr)[idx] = val; }
+        public static void xmrFixedArraySetF (object arr, int idx, double val) { ((double[])arr)[idx] = val; }
+        public static void xmrFixedArraySetI (object arr, int idx, int    val) {    ((int[])arr)[idx] = val; }
+        public static void xmrFixedArraySetO (object arr, int idx, object val) { ((object[])arr)[idx] = val; }
+
+        /**
+         * @brief Copy from one script-defined array to another.
+         * @param srcobj = source script-defined array class object pointer
+         * @param srcstart = offset in source array to start copying from
+         * @param dstobj = destination script-defined array class object pointer
+         * @param dststart = offset in destination arry to start copying to
+         * @param count = number of elements to copy
+         */
+        public static void xmrArrayCopy (object srcobj, int srcstart, object dstobj, int dststart, int count)
+        {
+            /*
+             * The script writer should only pass us script-defined class objects.
+             * Throw exception otherwise.
+             */
+            XMRSDTypeClObj srcsdt = (XMRSDTypeClObj)srcobj;
+            XMRSDTypeClObj dstsdt = (XMRSDTypeClObj)dstobj;
+
+            /*
+             * Get the script-visible type name of the arrays, brackets and all.
+             */
+            string srctypename = srcsdt.sdtcClass.longName.val;
+            string dsttypename = dstsdt.sdtcClass.longName.val;
+
+            /*
+             * The part before the first '[' of each should match exactly,
+             * meaning the basic data type (eg, float, List<string>) is the same.
+             * And there must be a '[' in each meaning that it is a script-defined array type.
+             */
+            int i = srctypename.IndexOf ('[');
+            int j = dsttypename.IndexOf ('[');
+            if ((i < 0) || (j < 0)) throw new InvalidCastException ("non-array passed: " + srctypename + " and/or " + dsttypename);
+            if ((i != j) || !srctypename.StartsWith (dsttypename.Substring (0, j))) {
+                throw new ArrayTypeMismatchException (srctypename + " vs " + dsttypename);
+            }
+
+            /*
+             * The number of brackets must match exactly.
+             * This permits copying from something like a float[,][] to something like a float[][].
+             * But you cannot copy from a float[][] to a float[] or wisa wersa.
+             * Counting either '[' or ']' would work equally well.
+             */
+            int srclen  = srctypename.Length;
+            int dstlen  = dsttypename.Length;
+            int srcjags = 0;
+            int dstjags = 0;
+            while (++ i < srclen) if (srctypename[i] == ']') srcjags ++;
+            while (++ j < dstlen) if (dsttypename[j] == ']') dstjags ++;
+            if (dstjags != srcjags) {
+                throw new ArrayTypeMismatchException (srctypename + " vs " + dsttypename);
+            }
+
+            /*
+             * Perform the copy.
+             */
+            Array srcarray = (Array)srcsdt.instVars.iarObjects[0];
+            Array dstarray = (Array)dstsdt.instVars.iarObjects[0];
+            Array.Copy (srcarray, srcstart, dstarray, dststart, count);
+        }
+
+        /**
+         * @brief Copy from an array to a list.
+         * @param srcar = the array to copy from
+         * @param start = where to start in the array
+         * @param count = number of elements
+         * @returns the list
+         */
+        public static LSL_List xmrArray2List (object srcar, int start, int count)
+        {
+            /*
+             * Get the script-visible type of the array.
+             * We only do arrays.
+             */
+            XMRSDTypeClObj array = (XMRSDTypeClObj)srcar;
+            TokenDeclSDTypeClass sdtClass = array.sdtcClass;
+            if (sdtClass.arrayOfRank == 0) {
+                throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val);
+            }
+
+            /*
+             * Validate objects they want to put in the list.
+             * We can't allow anything funky that OpenSim runtime doesn't expect.
+             */
+            Array srcarray = (Array)array.instVars.iarObjects[0];
+            object[] output = new object[count];
+            for (int i = 0; i < count; i ++) {
+                object src = srcarray.GetValue (i + start);
+                if (src == null) throw new NullReferenceException ("null element " + i);
+                if (src is double) {
+                    output[i] = new LSL_Float ((double)src);
+                    continue;
+                }
+                if (src is int) {
+                    output[i] = new LSL_Integer ((int)src);
+                    continue;
+                }
+                if (src is LSL_Rotation) {
+                    output[i] = src;
+                    continue;
+                }
+                if (src is LSL_Vector) {
+                    output[i] = src;
+                    continue;
+                }
+                if (src is string) {
+                    output[i] = new LSL_String ((string)src);
+                    continue;
+                }
+                throw new InvalidCastException ("invalid element " + i + " type " + src.GetType ().Name);
+            }
+
+            /*
+             * Make a list out of that now immutable array.
+             */
+            return new LSL_List (output);
+        }
+
+        /**
+         * @brief Copy from a list to an array.
+         * @param srclist  = list to copy from
+         * @param srcstart = where to start in the list
+         * @param dstobj   = array to copy to
+         * @param dststart = where to start in the array
+         * @param count    = number of elements
+         */
+        public static void xmrList2Array (LSL_List srclist, int srcstart, object dstobj, int dststart, int count)
+        {
+            /*
+             * Get the script-visible type of the destination.
+             * We only do arrays.
+             */
+            XMRSDTypeClObj dstarray = (XMRSDTypeClObj)dstobj;
+            TokenDeclSDTypeClass sdtClass = dstarray.sdtcClass;
+            if (sdtClass.arrayOfType == null) {
+                throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val);
+            }
+
+            /*
+             * Copy from the immutable array to the mutable array.
+             * Strip off any LSL wrappers as the script code doesn't expect any.
+             */
+            object[] srcarr = srclist.Data;
+            Array dstarr    = (Array)dstarray.instVars.iarObjects[0];
+
+            for (int i = 0; i < count; i ++) {
+                object obj = srcarr[i+srcstart];
+                if (obj is LSL_Float)   obj = ((LSL_Float)obj).value;
+                if (obj is LSL_Integer) obj = ((LSL_Integer)obj).value;
+                if (obj is LSL_String)  obj = ((LSL_String)obj).m_string;
+                dstarr.SetValue (obj, i + dststart);
+            }
+        }
+
+        /**
+         * @brief Copy from an array of characters to a string.
+         * @param srcar = the array to copy from
+         * @param start = where to start in the array
+         * @param count = number of elements
+         * @returns the string
+         */
+        public static string xmrChars2String (object srcar, int start, int count)
+        {
+            /*
+             * Make sure they gave us a script-defined array object.
+             */
+            XMRSDTypeClObj array = (XMRSDTypeClObj)srcar;
+            TokenDeclSDTypeClass sdtClass = array.sdtcClass;
+            if (sdtClass.arrayOfRank == 0) {
+                throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val);
+            }
+
+            /*
+             * We get a type cast error from mono if they didn't give us a character array.
+             * But if it is ok, create a string from the requested characters.
+             */
+            char[] srcarray = (char[])array.instVars.iarObjects[0];
+            return new string (srcarray, start, count);
+        }
+
+        /**
+         * @brief Copy from a string to a character array.
+         * @param srcstr   = string to copy from
+         * @param srcstart = where to start in the string
+         * @param dstobj   = array to copy to
+         * @param dststart = where to start in the array
+         * @param count    = number of elements
+         */
+        public static void xmrString2Chars (string srcstr, int srcstart, object dstobj, int dststart, int count)
+        {
+            /*
+             * Make sure they gave us a script-defined array object.
+             */
+            XMRSDTypeClObj dstarray = (XMRSDTypeClObj)dstobj;
+            TokenDeclSDTypeClass sdtClass = dstarray.sdtcClass;
+            if (sdtClass.arrayOfType == null) {
+                throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val);
+            }
+
+            /*
+             * We get a type cast error from mono if they didn't give us a character array.
+             * But if it is ok, copy from the string to the character array.
+             */
+            char[] dstarr = (char[])dstarray.instVars.iarObjects[0];
+            for (int i = 0; i < count; i ++) {
+                dstarr[i+dststart] = srcstr[i+srcstart];
+            }
+        }
+
+        /**
+         * @brief Implement osParseJSON() so we return an array to the script.
+         *        No coherent example of its use in scripts on web found.
+         * see http://www.json.org/ for more details on JSON
+         */
+        private static LSL_List nullList = new LSL_List (new object[0]);
+        public new XMR_Array osParseJSON (string json)
+        {
+            XMR_Array dict = new XMR_Array (this);
+            int idx = ParseJSON (dict, nullList, json, 0);
+            while (idx < json.Length) {
+                if (json[idx] > ' ') throw new Exception ("left-over json " + json);
+                idx ++;
+            }
+            return dict;
+        }
+
+        private static int ParseJSON (XMR_Array dict, LSL_List keys, string json, int idx)
+        {
+            char c;
+
+            while ((c = json[idx++]) <= ' ') { }
+            switch (c) {
+
+                // '{' <keystring> ':' <value> [ ',' <keystring> ':' <value> ... ] '}'
+                case '{': {
+                    do {
+                        string key = ParseJSONString (json, ref idx);
+                        while ((c = json[idx++]) <= ' ') { }
+                        if (c != ':') throw new Exception ("missing : after key");
+                        idx = ParseJSON (dict, ParseJSONKeyAdd (keys, key), json, idx);
+                        while ((c = json[idx++]) <= ' ') { }
+                    } while (c == ',');
+                    if (c != '}') throw new Exception ("missing , or } after value");
+                    break;
+                }
+
+                // '[' <value> [ ',' <value> ... ] ']'
+                case '[': {
+                    int index = 0;
+                    do {
+                        object key = index ++;
+                        idx = ParseJSON (dict, ParseJSONKeyAdd (keys, key), json, idx);
+                        while ((c = json[idx++]) <= ' ') { }
+                    } while (c == ',');
+                    if (c != ']') throw new Exception ("missing , or ] after value");
+                    break;
+                }
+
+                // '"'<string>'"'
+                case '"': {
+                    -- idx;
+                    string val = ParseJSONString (json, ref idx);
+                    dict.SetByKey (keys, val);
+                    break;
+                }
+
+                // true false null
+                case 't': {
+                    if (json.Substring (idx, 3) != "rue") throw new Exception ("bad true in json");
+                    idx += 3;
+                    dict.SetByKey (keys, 1);
+                    break;
+                }
+
+                case 'f': {
+                    if (json.Substring (idx, 4) != "alse") throw new Exception ("bad false in json");
+                    idx += 4;
+                    dict.SetByKey (keys, 0);
+                    break;
+                }
+
+                case 'n': {
+                    if (json.Substring (idx, 3) != "ull") throw new Exception ("bad null in json");
+                    idx += 3;
+                    dict.SetByKey (keys, null);
+                    break;
+                }
+
+                // otherwise assume it's a number
+                default: {
+                    -- idx;
+                    object val = ParseJSONNumber (json, ref idx);
+                    dict.SetByKey (keys, val);
+                    break;
+                }
+            }
+
+            return idx;
+        }
+
+        // Given the key for a whole array, create a key for a given element of the array
+        private static LSL_List ParseJSONKeyAdd (LSL_List oldkeys, object key)
+        {
+            int oldkeyslen = oldkeys.Length;
+            object[] array = oldkeys.Data;
+            Array.Resize<object> (ref array, oldkeyslen + 1);
+            array[oldkeyslen] = key;
+            return new LSL_List (array);
+        }
+
+        // Parse out a JSON string
+        private static string ParseJSONString (string json, ref int idx)
+        {
+            char c;
+
+            while ((c = json[idx++]) <= ' ') { }
+            if (c != '"') throw new Exception ("bad start of json string");
+
+            StringBuilder sb = new StringBuilder ();
+            while ((c = json[idx++]) != '"') {
+                if (c == '\\') {
+                    c = json[idx++];
+                    switch (c) {
+                        case 'b': {
+                            c = '\b';
+                            break;
+                        }
+                        case 'f': {
+                            c = '\f';
+                            break;
+                        }
+                        case 'n': {
+                            c = '\n';
+                            break;
+                        }
+                        case 'r': {
+                            c = '\r';
+                            break;
+                        }
+                        case 't': {
+                            c = '\t';
+                            break;
+                        }
+                        case 'u': {
+                            c = (char) Int32.Parse (json.Substring (idx, 4), 
+                                                    System.Globalization.NumberStyles.HexNumber);
+                            idx += 4;
+                            break;
+                        }
+                        default: break;
+                    }
+                }
+                sb.Append (c);
+            }
+            return sb.ToString ();
+        }
+
+        // Parse out a JSON number
+        private static object ParseJSONNumber (string json, ref int idx)
+        {
+            char c;
+
+            while ((c = json[idx++]) <= ' ') { }
+
+            bool expneg = false;
+            bool isneg  = false;
+            int decpt   = -1;
+            int expon   = 0;
+            int ival    = 0;
+            double dval = 0;
+
+            if (c == '-') {
+                isneg = true;
+                c = json[idx++];
+            }
+            if ((c < '0') || (c > '9')) {
+                throw new Exception ("bad json number");
+            }
+            while ((c >= '0') && (c <= '9')) {
+                dval *= 10;
+                ival *= 10;
+                dval += c - '0';
+                ival += c - '0';
+                c = '\0';
+                if (idx < json.Length) c = json[idx++];
+            }
+            if (c == '.') {
+                decpt = 0;
+                c = '\0';
+                if (idx < json.Length) c = json[idx++];
+                while ((c >= '0') && (c <= '9')) {
+                    dval *= 10;
+                    dval += c - '0';
+                    decpt ++;
+                    c = '\0';
+                    if (idx < json.Length) c = json[idx++];
+                }
+            }
+            if ((c == 'e') || (c == 'E')) {
+                if (decpt < 0) decpt = 0;
+                c = json[idx++];
+                if (c == '-') expneg = true;
+                if ((c == '-') || (c == '+')) c = json[idx++];
+                while ((c >= '0') && (c <= '9')) {
+                    expon *= 10;
+                    expon += c - '0';
+                    c = '\0';
+                    if (idx < json.Length) c = json[idx++];
+                }
+                if (expneg) expon = -expon;
+            }
+
+            if (c != 0) -- idx;
+            if (decpt < 0) {
+                if (isneg) ival = -ival;
+                return ival;
+            } else {
+                if (isneg) dval = -dval;
+                dval *= Math.Pow (10, expon - decpt);
+                return dval;
+            }
+        }
+
+        /**
+         * @brief Exception-related runtime calls.
+         */
+        // Return exception message (no type information just the message)
+        public static string xmrExceptionMessage (Exception ex)
+        {
+            return ex.Message;
+        }
+
+        // Return stack trace (no type or message, just stack trace lines: at ... \n)
+        public string xmrExceptionStackTrace (Exception ex)
+        {
+            return XMRExceptionStackString (ex);
+        }
+
+        // Return value thrown by a throw statement
+        public static object xmrExceptionThrownValue (Exception ex)
+        {
+            return ((ScriptThrownException)ex).thrown;
+        }
+
+        // Return exception's short type name, eg, NullReferenceException, ScriptThrownException, etc.
+        public static string xmrExceptionTypeName (Exception ex)
+        {
+            return ex.GetType ().Name;
+        }
+
+        // internal use only: converts any IL addresses in script-defined methods to source location equivalent
+        // at (wrapper dynamic-method) object.__seh_0_30_default_state_entry (OpenSim.Region.ScriptEngine.XMREngine.XMRInstAbstract) <IL 0x00d65, 0x03a53>
+        public string XMRExceptionStackString (Exception ex)
+        {
+            string st = ex.StackTrace;
+            StringBuilder sb = new StringBuilder ();
+            int wrapDynMethObj = 0;
+            int leftOffAt = 0;
+            while ((wrapDynMethObj = st.IndexOf ("(wrapper dynamic-method) System.Object:", ++ wrapDynMethObj)) >= 0) {
+                try {
+                    int begFuncName  = wrapDynMethObj + 39;
+                    int endFuncName  = st.IndexOf (" (", begFuncName);
+                    string funcName  = st.Substring (begFuncName, endFuncName - begFuncName);
+                    KeyValuePair<int, ScriptSrcLoc>[] srcLocs = m_ObjCode.scriptSrcLocss[funcName];
+
+                    int il0xPrefix   = st.IndexOf (" [0x", endFuncName);
+                    int begILHex     = il0xPrefix + 4;
+                    int endILHex     = st.IndexOf (']', begILHex);
+                    string ilHex     = st.Substring (begILHex, endILHex - begILHex);
+                    int offset       = Int32.Parse (ilHex, System.Globalization.NumberStyles.HexNumber);
+
+                    int srcLocIdx;
+                    int srcLocLen    = srcLocs.Length;
+                    for (srcLocIdx = 0; ++ srcLocIdx < srcLocLen;) {
+                        if (offset < srcLocs[srcLocIdx].Key) break;
+                    }
+                    ScriptSrcLoc srcLoc = srcLocs[--srcLocIdx].Value;
+
+                    sb.Append (st.Substring (leftOffAt, wrapDynMethObj - leftOffAt));
+                    sb.Append (st.Substring (begFuncName, endFuncName - begFuncName));
+                    sb.Append (" <");
+                    sb.Append (srcLoc.file);
+                    sb.Append ('(');
+                    sb.Append (srcLoc.line);
+                    sb.Append (',');
+                    sb.Append (srcLoc.posn);
+                    sb.Append (")>");
+
+                    leftOffAt = ++ endILHex;
+                } catch {
+                }
+            }
+            sb.Append (st.Substring (leftOffAt));
+            return sb.ToString ();
+        }
+
+        /**
+         * @brief List fonts available.
+         */
+        public LSL_List xmrFontsAvailable ()
+        {
+            System.Drawing.FontFamily[] families = System.Drawing.FontFamily.Families;
+            object[] output = new object[families.Length];
+            for (int i = 0; i < families.Length; i ++) {
+                output[i] = new LSL_String (families[i].Name);
+            }
+            return new LSL_List (output);
+        }
+
+        /************************\
+         *  Used by decompiler  *
+        \************************/
+
+        public bool xmrRotationToBool (LSL_Rotation x) { return TypeCast.RotationToBool (x); }
+        public bool xmrStringToBool   (string x)       { return TypeCast.StringToBool   (x); }
+        public bool xmrVectorToBool   (LSL_Vector x)   { return TypeCast.VectorToBool   (x); }
+        public bool xmrKeyToBool      (string x)       { return TypeCast.KeyToBool      (x); }
+        public bool xmrListToBool     (LSL_List x)     { return TypeCast.ListToBool     (x); }
+
+        public int xmrStringCompare (string x, string y) { return string.Compare (x, y); }
+
+        /**
+         * @brief types of data we serialize
+         */
+        private enum Ser : byte {
+            NULL,
+            EVENTCODE,
+            LSLFLOAT,
+            LSLINT,
+            LSLKEY,
+            LSLLIST,
+            LSLROT,
+            LSLSTR,
+            LSLVEC,
+            SYSARRAY,
+            SYSDOUB,
+            SYSFLOAT,
+            SYSINT,
+            SYSSTR,
+            XMRARRAY,
+            DUPREF,
+            SYSBOOL,
+            XMRINST,
+            DELEGATE,
+            SDTCLOBJ,
+            SYSCHAR,
+            SYSERIAL,
+            THROWNEX
+        }
+
+        /**
+         * @brief Write state out to a stream.
+         *        Do not change script state.
+         */
+        public void MigrateOut (BinaryWriter mow)
+        {
+            try {
+                this.migrateOutWriter  = mow;
+                this.migrateOutObjects = new Dictionary<object,int> ();
+                this.migrateOutLists   = new Dictionary<object[],ObjLslList> ();
+                this.SendObjValue (this.ehArgs);
+                mow.Write (this.doGblInit);
+                mow.Write (this.stateCode);
+                mow.Write ((int)this.eventCode);
+                this.glblVars.SendArrays (this.SendObjValue);
+                if (this.newStateCode >= 0) {
+                    mow.Write ("**newStateCode**");
+                    mow.Write (this.newStateCode);
+                }
+                for (XMRStackFrame thisSF = this.stackFrames; thisSF != null; thisSF = thisSF.nextSF) {
+                    mow.Write (thisSF.funcName);
+                    mow.Write (thisSF.callNo);
+                    this.SendObjValue (thisSF.objArray);
+                }
+                mow.Write ("");
+            } finally {
+                this.migrateOutWriter  = null;
+                this.migrateOutObjects = null;
+                this.migrateOutLists   = null;
+            }
+        }
+
+        /**
+         * @brief Write an object to the output stream.
+         * @param graph = object to send
+         */
+        private BinaryWriter migrateOutWriter;
+        private Dictionary<object,int> migrateOutObjects;
+        private Dictionary<object[],ObjLslList> migrateOutLists;
+        public void SendObjValue (object graph)
+        {
+            BinaryWriter mow = this.migrateOutWriter;
+
+            /*
+             * Value types (including nulls) are always output directly.
+             */
+            if (graph == null) {
+                mow.Write ((byte)Ser.NULL);
+                return;
+            }
+            if (graph is ScriptEventCode) {
+                mow.Write ((byte)Ser.EVENTCODE);
+                mow.Write ((int)graph);
+                return;
+            }
+            if (graph is LSL_Float) {
+                mow.Write ((byte)Ser.LSLFLOAT);
+                mow.Write ((double)((LSL_Float)graph).value);
+                return;
+            }
+            if (graph is LSL_Integer) {
+                mow.Write ((byte)Ser.LSLINT);
+                mow.Write ((int)((LSL_Integer)graph).value);
+                return;
+            }
+            if (graph is LSL_Key) {
+                mow.Write ((byte)Ser.LSLKEY);
+                LSL_Key key = (LSL_Key)graph;
+                SendObjValue (key.m_string);  // m_string can be null
+                return;
+            }
+            if (graph is LSL_Rotation) {
+                mow.Write ((byte)Ser.LSLROT);
+                mow.Write ((double)((LSL_Rotation)graph).x);
+                mow.Write ((double)((LSL_Rotation)graph).y);
+                mow.Write ((double)((LSL_Rotation)graph).z);
+                mow.Write ((double)((LSL_Rotation)graph).s);
+                return;
+            }
+            if (graph is LSL_String) {
+                mow.Write ((byte)Ser.LSLSTR);
+                LSL_String str = (LSL_String)graph;
+                SendObjValue (str.m_string);  // m_string can be null
+                return;
+            }
+            if (graph is LSL_Vector) {
+                mow.Write ((byte)Ser.LSLVEC);
+                mow.Write ((double)((LSL_Vector)graph).x);
+                mow.Write ((double)((LSL_Vector)graph).y);
+                mow.Write ((double)((LSL_Vector)graph).z);
+                return;
+            }
+            if (graph is bool) {
+                mow.Write ((byte)Ser.SYSBOOL);
+                mow.Write ((bool)graph);
+                return;
+            }
+            if (graph is double) {
+                mow.Write ((byte)Ser.SYSDOUB);
+                mow.Write ((double)graph);
+                return;
+            }
+            if (graph is float) {
+                mow.Write ((byte)Ser.SYSFLOAT);
+                mow.Write ((float)graph);
+                return;
+            }
+            if (graph is int) {
+                mow.Write ((byte)Ser.SYSINT);
+                mow.Write ((int)graph);
+                return;
+            }
+            if (graph is char) {
+                mow.Write ((byte)Ser.SYSCHAR);
+                mow.Write ((char)graph);
+                return;
+            }
+
+            /*
+             * Script instance pointer is always just that.
+             */
+            if (graph == this) {
+                mow.Write ((byte)Ser.XMRINST);
+                return;
+            }
+
+            /*
+             * Convert lists to object type.
+             * This is compatible with old migration data and also
+             * two vars pointing to same list won't duplicate it.
+             */
+            if (graph is LSL_List) {
+                object[] data = ((LSL_List) graph).Data;
+                ObjLslList oll;
+                if (!this.migrateOutLists.TryGetValue (data, out oll)) {
+                    oll = new ObjLslList ();
+                    oll.objarray = data;
+                    this.migrateOutLists[data] = oll;
+                }
+                graph = oll;
+            }
+
+            /*
+             * If this same exact object was already serialized,
+             * just output an index telling the receiver to use
+             * that same old object, rather than creating a whole
+             * new object with the same values.  Also this prevents
+             * self-referencing objects (like arrays) from causing
+             * an infinite loop.
+             */
+            int ident;
+            if (this.migrateOutObjects.TryGetValue (graph, out ident)) {
+                mow.Write ((byte)Ser.DUPREF);
+                mow.Write (ident);
+                return;
+            }
+
+            /*
+             * Object not seen before, save its address with an unique
+             * ident number that the receiver can easily regenerate.
+             */
+            ident = this.migrateOutObjects.Count;
+            this.migrateOutObjects.Add (graph, ident);
+
+            /*
+             * Now output the object's value(s).
+             * If the object self-references, the object is alreay entered
+             * in the dictionary and so the self-reference will just emit
+             * a DUPREF tag instead of trying to output the whole object 
+             * again.
+             */
+            if (graph is ObjLslList) {
+                mow.Write ((byte)Ser.LSLLIST);
+                ObjLslList oll = (ObjLslList) graph;
+                SendObjValue (oll.objarray);
+            } else if (graph is XMR_Array) {
+                mow.Write ((byte)Ser.XMRARRAY);
+                ((XMR_Array)graph).SendArrayObj (this.SendObjValue);
+            } else if (graph is Array) {
+                Array array = (Array)graph;
+                mow.Write ((byte)Ser.SYSARRAY);
+                mow.Write (SysType2String (array.GetType ().GetElementType ()));
+                mow.Write ((int)array.Length);
+                for (int i = 0; i < array.Length; i ++) {
+                    this.SendObjValue (array.GetValue (i));
+                }
+            } else if (graph is string) {
+                mow.Write ((byte)Ser.SYSSTR);
+                mow.Write ((string)graph);
+            } else if (graph is Delegate) {
+                Delegate del = (Delegate)graph;
+                mow.Write ((byte)Ser.DELEGATE);
+                mow.Write (del.Method.Name);
+                Type delType = del.GetType ();
+                foreach (KeyValuePair<string, TokenDeclSDType> kvp in m_ObjCode.sdObjTypesName) {
+                    TokenDeclSDType sdt = kvp.Value;
+                    if (sdt is TokenDeclSDTypeDelegate) {
+                        TokenDeclSDTypeDelegate sdtd = (TokenDeclSDTypeDelegate)sdt;
+                        if (sdtd.GetSysType () == delType) {
+                            mow.Write (kvp.Key);
+                            goto found;
+                        }
+                    }
+                }
+                throw new Exception ("cant find script-defined delegate for " + del.Method.Name + " type " + del.GetType ());
+            found:
+                SendObjValue (del.Target);
+            } else if (graph is XMRSDTypeClObj) {
+                mow.Write ((byte)Ser.SDTCLOBJ);
+                ((XMRSDTypeClObj)graph).Capture (this.SendObjValue);
+            } else if (graph is ScriptThrownException) {
+                MemoryStream memoryStream = new MemoryStream ();
+                System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter = 
+                        new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
+                bformatter.Serialize (memoryStream, graph);
+                byte[] rawBytes = memoryStream.ToArray ();
+                mow.Write ((byte)Ser.THROWNEX);
+                mow.Write ((int)rawBytes.Length);
+                mow.Write (rawBytes);
+                SendObjValue (((ScriptThrownException)graph).thrown);
+            } else {
+                MemoryStream memoryStream = new MemoryStream ();
+                System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter = 
+                        new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
+                bformatter.Serialize (memoryStream, graph);
+                byte[] rawBytes = memoryStream.ToArray ();
+                mow.Write ((byte)Ser.SYSERIAL);
+                mow.Write ((int)rawBytes.Length);
+                mow.Write (rawBytes);
+            }
+        }
+
+        /**
+         * @brief Use short strings for known type names.
+         */
+        private static string SysType2String (Type type)
+        {
+            if (type.IsArray && (type.GetArrayRank () == 1)) {
+                string str = KnownSysType2String (type.GetElementType ());
+                if (str != null) return str + "[]";
+            } else {
+                string str = KnownSysType2String (type);
+                if (str != null) return str;
+            }
+            return type.ToString ();
+        }
+        private static string KnownSysType2String (Type type)
+        {
+            if (type == typeof (bool))           return "bo";
+            if (type == typeof (char))           return "ch";
+            if (type == typeof (Delegate))       return "de";
+            if (type == typeof (double))         return "do";
+            if (type == typeof (float))          return "fl";
+            if (type == typeof (int))            return "in";
+            if (type == typeof (LSL_List))       return "li";
+            if (type == typeof (object))         return "ob";
+            if (type == typeof (LSL_Rotation))   return "ro";
+            if (type == typeof (XMRSDTypeClObj)) return "sc";
+            if (type == typeof (string))         return "st";
+            if (type == typeof (LSL_Vector))     return "ve";
+            if (type == typeof (XMR_Array))      return "xa";
+            return null;
+        }
+        private static Type String2SysType (string str)
+        {
+            if (str.EndsWith ("[]")) {
+                return String2SysType (str.Substring (0, str.Length - 2)).MakeArrayType ();
+            }
+            if (str == "bo") return typeof (bool);
+            if (str == "ch") return typeof (char);
+            if (str == "de") return typeof (Delegate);
+            if (str == "do") return typeof (double);
+            if (str == "fl") return typeof (float);
+            if (str == "in") return typeof (int);
+            if (str == "li") return typeof (LSL_List);
+            if (str == "ob") return typeof (object);
+            if (str == "ro") return typeof (LSL_Rotation);
+            if (str == "sc") return typeof (XMRSDTypeClObj);
+            if (str == "st") return typeof (string);
+            if (str == "ve") return typeof (LSL_Vector);
+            if (str == "xa") return typeof (XMR_Array);
+            return Type.GetType (str, true);
+        }
+
+        /**
+         * @brief Read state in from a stream.
+         */
+        public void MigrateIn (BinaryReader mir)
+        {
+            try {
+                this.migrateInReader  = mir;
+                this.migrateInObjects = new Dictionary<int, object> ();
+                this.ehArgs    = (object[])this.RecvObjValue ();
+                this.doGblInit = mir.ReadBoolean ();
+                this.stateCode = mir.ReadInt32 ();
+                this.eventCode = (ScriptEventCode)mir.ReadInt32 ();
+                this.newStateCode = -1;
+                this.glblVars.RecvArrays (this.RecvObjValue);
+                XMRStackFrame lastSF = null;
+                string funcName;
+                while ((funcName = mir.ReadString ()) != "") {
+                    if (funcName == "**newStateCode**") {
+                        this.newStateCode = mir.ReadInt32 ();
+                        continue;
+                    }
+                    XMRStackFrame thisSF = new XMRStackFrame ();
+                    thisSF.funcName = funcName;
+                    thisSF.callNo   = mir.ReadInt32 ();
+                    thisSF.objArray = (object[])this.RecvObjValue ();
+                    if (lastSF == null) this.stackFrames = thisSF;
+                                   else lastSF.nextSF = thisSF;
+                    lastSF = thisSF;
+                }
+            } finally {
+                this.migrateInReader  = null;
+                this.migrateInObjects = null;
+            }
+        }
+
+        /**
+         * @brief Read a single value from the stream.
+         * @returns value (boxed as needed)
+         */
+        private BinaryReader migrateInReader;
+        private Dictionary<int, object> migrateInObjects;
+        public object RecvObjValue ()
+        {
+            BinaryReader mir = this.migrateInReader;
+            int ident = this.migrateInObjects.Count;
+            Ser code = (Ser)mir.ReadByte ();
+            switch (code) {
+                case Ser.NULL: {
+                    return null;
+                }
+                case Ser.EVENTCODE: {
+                    return (ScriptEventCode)mir.ReadInt32 ();
+                }
+                case Ser.LSLFLOAT: {
+                    return new LSL_Float (mir.ReadDouble ());
+                }
+                case Ser.LSLINT: {
+                    return new LSL_Integer (mir.ReadInt32 ());
+                }
+                case Ser.LSLKEY: {
+                    return new LSL_Key ((string)RecvObjValue ());
+                }
+                case Ser.LSLLIST: {
+                    this.migrateInObjects.Add (ident, null);    // placeholder
+                    object[] data = (object[])RecvObjValue ();  // read data, maybe using another index
+                    LSL_List list = new LSL_List (data);        // make LSL-level list
+                    this.migrateInObjects[ident] = list;        // fill in slot
+                    return list;
+                }
+                case Ser.LSLROT: {
+                    double x = mir.ReadDouble ();
+                    double y = mir.ReadDouble ();
+                    double z = mir.ReadDouble ();
+                    double s = mir.ReadDouble ();
+                    return new LSL_Rotation (x, y, z, s);
+                }
+                case Ser.LSLSTR: {
+                    return new LSL_String ((string)RecvObjValue ());
+                }
+                case Ser.LSLVEC: {
+                    double x = mir.ReadDouble ();
+                    double y = mir.ReadDouble ();
+                    double z = mir.ReadDouble ();
+                    return new LSL_Vector (x, y, z);
+                }
+                case Ser.SYSARRAY: {
+                    Type eletype = String2SysType (mir.ReadString ());
+                    int length = mir.ReadInt32 ();
+                    Array array = Array.CreateInstance (eletype, length);
+                    this.migrateInObjects.Add (ident, array);
+                    for (int i = 0; i < length; i ++) {
+                        array.SetValue (RecvObjValue (), i);
+                    }
+                    return array;
+                }
+                case Ser.SYSBOOL: {
+                    return mir.ReadBoolean ();
+                }
+                case Ser.SYSDOUB: {
+                    return mir.ReadDouble ();
+                }
+                case Ser.SYSFLOAT: {
+                    return mir.ReadSingle ();
+                }
+                case Ser.SYSINT: {
+                    return mir.ReadInt32 ();
+                }
+                case Ser.SYSCHAR: {
+                    return mir.ReadChar ();
+                }
+                case Ser.SYSSTR: {
+                    string s = mir.ReadString ();
+                    this.migrateInObjects.Add (ident, s);
+                    return s;
+                }
+                case Ser.XMRARRAY: {
+                    XMR_Array array = new XMR_Array (this);
+                    this.migrateInObjects.Add (ident, array);
+                    array.RecvArrayObj (this.RecvObjValue);
+                    return array;
+                }
+                case Ser.DUPREF: {
+                    ident = mir.ReadInt32 ();
+                    object obj = this.migrateInObjects[ident];
+                    if (obj is ObjLslList) obj = new LSL_List (((ObjLslList) obj).objarray);
+                    return obj;
+                }
+                case Ser.XMRINST: {
+                    return this;
+                }
+                case Ser.DELEGATE: {
+                    this.migrateInObjects.Add (ident, null);  // placeholder
+                    string name  = mir.ReadString ();         // function name
+                    string sig   = mir.ReadString ();         // delegate type
+                    object targ  = this.RecvObjValue ();      // 'this' object
+                    Delegate del = this.GetScriptMethodDelegate (name, sig, targ);
+                    this.migrateInObjects[ident] = del;       // actual value
+                    return del;
+                }
+                case Ser.SDTCLOBJ: {
+                    XMRSDTypeClObj clobj = new XMRSDTypeClObj ();
+                    this.migrateInObjects.Add (ident, clobj);
+                    clobj.Restore (this, this.RecvObjValue);
+                    return clobj;
+                }
+                case Ser.SYSERIAL: {
+                    int rawLength   = mir.ReadInt32 ();
+                    byte[] rawBytes = mir.ReadBytes (rawLength);
+                    MemoryStream memoryStream = new MemoryStream (rawBytes);
+                    System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter = 
+                            new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
+                    object graph = bformatter.Deserialize (memoryStream);
+                    this.migrateInObjects.Add (ident, graph);
+                    return graph;
+                }
+                case Ser.THROWNEX: {
+                    int rawLength   = mir.ReadInt32 ();
+                    byte[] rawBytes = mir.ReadBytes (rawLength);
+                    MemoryStream memoryStream = new MemoryStream (rawBytes);
+                    System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter = 
+                            new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
+                    object graph = bformatter.Deserialize (memoryStream);
+                    this.migrateInObjects.Add (ident, graph);
+                    ((ScriptThrownException)graph).thrown = RecvObjValue ();
+                    return graph;
+                }
+                default: throw new Exception ("bad stream code " + code.ToString ());
+            }
+        }
+
+        // wrapper around list object arrays to make sure they are always object types for migration purposes
+        private class ObjLslList {
+            public object[] objarray;
+        }
+    }
+
+    /**
+     * @brief Common access to script microthread.
+     */
+    public interface IScriptUThread : IDisposable
+    {
+        Exception ResumeEx  ();     // called by macrothread to resume execution at most recent Hiber()
+        Exception StartEx   ();     // called by macrothread to start execution at CallSEH()
+        int       Active    ();     // called by macrothread to query state of microthread
+        int       StackLeft ();     // called by microthread to query amount of remaining stack space
+        void      Hiber     ();     // called by microthread to hibernate
+    }
+
+    // Any xmr...() methods that call CheckRun() must be tagged with this attribute
+    // so the ScriptCodeGen will know the method is non-trivial.
+    public class xmrMethodCallsCheckRunAttribute : Attribute { }
+
+    // Any xmr...() methods in xmrengtest that call Stub<somethingorother>() must be 
+    // tagged with this attribute so the -builtins option will tell the user that 
+    // they are a stub function.
+    public class xmrMethodIsNoisyAttribute : Attribute { }
+
+    // Any script callable methods that really return a key not a string should be
+    // tagged with this attribute so the compiler will know they return type key and
+    // not type string.
+    public class xmrMethodReturnsKeyAttribute : Attribute { }
+
+    [SerializableAttribute]
+    public class OutOfHeapException : Exception {
+        public OutOfHeapException (int oldtotal, int newtotal, int limit)
+                : base ("oldtotal=" + oldtotal + ", newtotal=" + newtotal + ", limit=" + limit)
+        { }
+    }
+
+    [SerializableAttribute]
+    public class OutOfStackException : Exception { }
+}

+ 644 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs

@@ -0,0 +1,644 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Threading;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.Remoting.Lifetime;
+using System.Security.Policy;
+using System.IO;
+using System.Xml;
+using System.Text;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.ScriptEngine.Interfaces;
+using OpenSim.Region.ScriptEngine.Shared;
+using OpenSim.Region.ScriptEngine.Shared.Api;
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.Framework.Scenes.Scripting;
+using OpenSim.Region.Framework.Interfaces;
+using log4net;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    /****************************************************\
+     *  This file contains routines called by scripts.  *
+    \****************************************************/
+
+    public class XMRLSL_Api : LSL_Api
+    {
+        public AsyncCommandManager acm;
+        private XMRInstance inst;
+
+        public void InitXMRLSLApi(XMRInstance i)
+        {
+            acm = AsyncCommands;
+            inst = i;
+        }
+
+        protected override void ScriptSleep(int ms)
+        {
+            inst.Sleep(ms);
+        }
+
+        public override void llSleep(double sec)
+        {
+            inst.Sleep((int)(sec * 1000.0));
+        }
+
+        public override void llDie()
+        {
+            inst.Die();
+        }
+
+        /**
+         * @brief Seat avatar on prim.
+         * @param owner = true: owner of prim script is running in
+         *               false: avatar that has given ANIMATION permission on the prim
+         * @returns 0: successful
+         *         -1: no permission to animate
+         *         -2: no av granted perms
+         *         -3: av not in region
+         */
+        public int xmrSeatAvatar (bool owner)
+        {
+            // Get avatar to be seated and make sure they have given us ANIMATION permission
+
+            UUID avuuid;
+            if (owner) {
+                avuuid = inst.m_Part.OwnerID;
+            } else {
+                if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION) == 0) {
+                    return -1;
+                }
+                avuuid = m_item.PermsGranter;
+            }
+            if (avuuid == UUID.Zero) {
+                return -2;
+            }
+
+            ScenePresence presence = World.GetScenePresence (avuuid);
+            if (presence == null) {
+                return -3;
+            }
+
+            // remoteClient = not used by ScenePresence.HandleAgentRequestSit()
+            //      agentID = not used by ScenePresence.HandleAgentRequestSit()
+            //     targetID = UUID of prim to sit on
+            //       offset = offset of sitting position
+
+            presence.HandleAgentRequestSit (null, UUID.Zero, m_host.UUID, OpenMetaverse.Vector3.Zero);
+            return 0;
+        }
+
+        /**
+         * @brief llTeleportAgent() is broken in that if you pass it a landmark,
+         *        it still subjects the position to spawn points, as it always
+         *        calls RequestTeleportLocation() with TeleportFlags.ViaLocation.
+         *        See llTeleportAgent() and CheckAndAdjustTelehub().
+         *
+         * @param agent    = what agent to teleport
+         * @param landmark = inventory name or UUID of a landmark object
+         * @param lookat   = looking direction after teleport
+         */
+        public void xmrTeleportAgent2Landmark (string agent, string landmark, LSL_Vector lookat)
+        {
+            // find out about agent to be teleported
+            UUID agentId;
+            if (!UUID.TryParse (agent, out agentId)) throw new ApplicationException ("bad agent uuid");
+
+            ScenePresence presence = World.GetScenePresence (agentId);
+            if (presence == null) throw new ApplicationException ("agent not present in scene");
+            if (presence.IsNPC) throw new ApplicationException ("agent is an NPC");
+            if (presence.IsGod) throw new ApplicationException ("agent is a god");
+
+            // prim must be owned by land owner or prim must be attached to agent
+            if (m_host.ParentGroup.AttachmentPoint == 0) {
+                if (m_host.OwnerID != World.LandChannel.GetLandObject (presence.AbsolutePosition).LandData.OwnerID) {
+                    throw new ApplicationException ("prim not owned by land's owner");
+                }
+            } else {
+                if (m_host.OwnerID != presence.UUID) throw new ApplicationException ("prim not attached to agent");
+            }
+
+            // find landmark in inventory or by UUID
+            UUID assetID = ScriptUtils.GetAssetIdFromKeyOrItemName (m_host, landmark);
+            if (assetID == UUID.Zero) throw new ApplicationException ("no such landmark");
+
+            // read it in and make sure it is a landmark
+            AssetBase lma = World.AssetService.Get (assetID.ToString ());
+            if ((lma == null) || (lma.Type != (sbyte)AssetType.Landmark)) throw new ApplicationException ("not a landmark");
+
+            // parse the record
+            AssetLandmark lm = new AssetLandmark (lma);
+
+            // the regionhandle (based on region's world X,Y) might be out of date
+            // re-read the handle so we can pass it to RequestTeleportLocation()
+            var region = World.GridService.GetRegionByUUID (World.RegionInfo.ScopeID, lm.RegionID);
+            if (region == null) throw new ApplicationException ("no such region");
+
+            // finally ready to teleport
+            World.RequestTeleportLocation (presence.ControllingClient,
+                                           region.RegionHandle,
+                                           lm.Position,
+                                           lookat,
+                                           (uint)TeleportFlags.ViaLandmark);
+        }
+
+        /**
+         * @brief Allow any member of group given by config SetParcelMusicURLGroup to set music URL.
+         *        Code modelled after llSetParcelMusicURL().
+         * @param newurl = new URL to set (or "" to leave it alone)
+         * @returns previous URL string
+         */
+        public string xmrSetParcelMusicURLGroup (string newurl)
+        {
+            string groupname = m_ScriptEngine.Config.GetString ("SetParcelMusicURLGroup", "");
+            if (groupname == "") throw new ApplicationException ("no SetParcelMusicURLGroup config param set");
+
+            IGroupsModule igm = World.RequestModuleInterface<IGroupsModule> ();
+            if (igm == null) throw new ApplicationException ("no GroupsModule loaded");
+
+            GroupRecord grouprec = igm.GetGroupRecord (groupname);
+            if (grouprec == null) throw new ApplicationException ("no such group " + groupname);
+
+            GroupMembershipData gmd = igm.GetMembershipData (grouprec.GroupID, m_host.OwnerID);
+            if (gmd == null) throw new ApplicationException ("not a member of group " + groupname);
+
+            ILandObject land = World.LandChannel.GetLandObject (m_host.AbsolutePosition);
+            if (land == null) throw new ApplicationException ("no land at " + m_host.AbsolutePosition.ToString ());
+            string oldurl = land.GetMusicUrl ();
+            if (oldurl == null) oldurl = "";
+            if ((newurl != null) && (newurl != "")) land.SetMusicUrl (newurl);
+            return oldurl;
+        }
+    }
+
+    public partial class XMRInstance
+    {
+        /**
+         * @brief The script is calling llReset().
+         *        We throw an exception to unwind the script out to its main
+         *        causing all the finally's to execute and it will also set
+         *        eventCode = None to indicate event handler has completed.
+         */
+        public void ApiReset()
+        {
+            ClearQueueExceptLinkMessages();
+            throw new ScriptResetException();
+        }
+
+        /**
+         * @brief The script is calling one of the llDetected...(int number)
+         *        functions.  Return corresponding DetectParams pointer.
+         */
+        public DetectParams GetDetectParams(int number)
+        {
+            DetectParams dp = null;
+            if ((number >= 0) && (m_DetectParams != null) && (number < m_DetectParams.Length)) {
+                dp = m_DetectParams[number];
+            }
+            return dp;
+        }
+
+        /**
+         * @brief Script is calling llDie, so flag the run loop to delete script
+         *        once we are off the microthread stack, and throw an exception
+         *        to unwind the stack asap.
+         */
+        public void Die()
+        {
+            // llDie doesn't work in attachments!
+            if (m_Part.ParentGroup.IsAttachment || m_DetachQuantum > 0)
+                return;
+
+            throw new ScriptDieException();
+        }
+
+        /**
+         * @brief Called by script to sleep for the given number of milliseconds.
+         */
+        public void Sleep(int ms)
+        {
+            lock (m_QueueLock) {
+
+                /*
+                 * Say how long to sleep.
+                 */
+                m_SleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds(ms);
+
+                /*
+                 * Don't wake on any events.
+                 */
+                m_SleepEventMask1 = 0;
+                m_SleepEventMask2 = 0;
+            }
+
+            /*
+             * The compiler follows all calls to llSleep() with a call to CheckRun().
+             * So tell CheckRun() to suspend the microthread.
+             */
+            suspendOnCheckRunTemp = true;
+        }
+
+        /**
+         * Block script execution until an event is queued or a timeout is reached.
+         * @param timeout = maximum number of seconds to wait
+         * @param returnMask = if event is queued that matches these mask bits,
+         *                     the script is woken, that event is dequeued and
+         *                     returned to the caller.  The event handler is not
+         *                     executed.
+         * @param backgroundMask = if any of these events are queued while waiting,
+         *                         execute their event handlers.  When any such event
+         *                         handler exits, continue waiting for events or the
+         *                         timeout.
+         * @returns empty list: no event was queued that matched returnMask and the timeout was reached
+         *                      or a background event handler changed state (eg, via 'state' statement)
+         *                else: list giving parameters of the event:
+         *                      [0] = event code (integer)
+         *                   [1..n] = call parameters to the event, if any
+         * Notes:
+         *   1) Scrips should use XMREVENTMASKn_<eventname> symbols for the mask arguments,
+         *      where n is 1 or 2 for mask1 or mask2 arguments.
+         *      The list[0] return argument can be decoded by using XMREVENTCODE_<eventname> symbols.
+         *   2) If all masks are zero, the call ends up acting like llSleep.
+         *   3) If an event is enabled in both returnMask and backgroundMask, the returnMask bit
+         *      action takes precedence, ie, the event is returned.  This allows a simple specification
+         *      of -1 for both backgroundMask arguments to indicate that all events not listed in
+         *      the returnMask argumetns should be handled in the background.
+         *   4) Any events not listed in either returnMask or backgroundMask arguments will be
+         *      queued for later processing (subject to normal queue limits).
+         *   5) Background event handlers execute as calls from within xmrEventDequeue, they do
+         *      not execute as separate threads.  Thus any background event handlers must return
+         *      before the call to xmrEventDequeue will return.
+         *   6) If a background event handler changes state (eg, via 'state' statement), the state
+         *      is immediately changed and the script-level xmrEventDequeue call does not return.
+         *   7) For returned events, the detect parameters are overwritten by the returned event.
+         *      For background events, the detect parameters are saved and restored.
+         *   8) Scripts must contain dummy event handler definitions for any event types that may
+         *      be returned by xmrEventDequeue, to let the runtime know that the script is capable
+         *      of processing that event type.  Otherwise, the event may not be queued to the script.
+         */
+        private static LSL_List emptyList = new LSL_List (new object[0]);
+
+        public override LSL_List xmrEventDequeue (double timeout, int returnMask1, int returnMask2,
+                                                  int backgroundMask1, int backgroundMask2)
+        {
+            DateTime sleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds (timeout * 1000.0);
+            EventParams evt = null;
+            int callNo, evc2;
+            int evc1 = 0;
+            int mask1 = returnMask1 | backgroundMask1;  // codes 00..31
+            int mask2 = returnMask2 | backgroundMask2;  // codes 32..63
+            LinkedListNode<EventParams> lln = null;
+            object[] sv;
+            ScriptEventCode evc = ScriptEventCode.None;
+
+            callNo = -1;
+            try {
+                if (callMode == CallMode_NORMAL) goto findevent;
+
+                /*
+                 * Stack frame is being restored as saved via CheckRun...().
+                 * Restore necessary values then jump to __call<n> label to resume processing.
+                 */
+                sv          = RestoreStackFrame ("xmrEventDequeue", out callNo);
+                sleepUntil  = DateTime.Parse ((string)sv[0]);
+                returnMask1 = (int)sv[1];
+                returnMask2 = (int)sv[2];
+                mask1       = (int)sv[3];
+                mask2       = (int)sv[4];
+                switch (callNo) {
+                    case 0: goto __call0;
+                    case 1: {
+                        evc1 = (int)sv[5];
+                        evc  = (ScriptEventCode)(int)sv[6];
+                        DetectParams[] detprms = ObjArrToDetPrms ((object[])sv[7]);
+                        object[]       ehargs  = (object[])sv[8];
+                        evt = new EventParams (evc.ToString (), ehargs, detprms);
+                        goto __call1;
+                    }
+                }
+                throw new ScriptBadCallNoException (callNo);
+
+                /*
+                 * Find first event that matches either the return or background masks.
+                 */
+            findevent:
+                Monitor.Enter (m_QueueLock);
+                for (lln = m_EventQueue.First; lln != null; lln = lln.Next) {
+                    evt  = lln.Value;
+                    evc  = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), evt.EventName);
+                    evc1 = (int)evc;
+                    evc2 = evc1 - 32;
+                    if ((((uint)evc1 < (uint)32) && (((mask1 >> evc1) & 1) != 0)) ||
+                        (((uint)evc2 < (uint)32) && (((mask2 >> evc2) & 1) != 0))) goto remfromq;
+                }
+
+                /*
+                 * Nothing found, sleep while one comes in.
+                 */
+                m_SleepUntil = sleepUntil;
+                m_SleepEventMask1 = mask1;
+                m_SleepEventMask2 = mask2;
+                Monitor.Exit (m_QueueLock);
+                suspendOnCheckRunTemp = true;
+                callNo = 0;
+            __call0:
+                CheckRunQuick ();
+                goto checktmo;
+
+                /*
+                 * Found one, remove it from queue.
+                 */
+            remfromq:
+                m_EventQueue.Remove (lln);
+                if ((uint)evc1 < (uint)m_EventCounts.Length) {
+                    m_EventCounts[evc1] --;
+                }
+                Monitor.Exit (m_QueueLock);
+                m_InstEHEvent ++;
+
+                /*
+                 * See if returnable or background event.
+                 */
+                if ((((uint)evc1 < (uint)32) && (((returnMask1 >> evc1) & 1) != 0)) ||
+                    (((uint)evc2 < (uint)32) && (((returnMask2 >> evc2) & 1) != 0))) {
+
+                    /*
+                     * Returnable event, return its parameters in a list.
+                     * Also set the detect parameters to what the event has.
+                     */
+                    int plen = evt.Params.Length;
+                    object[] plist = new object[plen+1];
+                    plist[0] = (LSL_Integer)evc1;
+                    for (int i = 0; i < plen;) {
+                        object ob = evt.Params[i];
+                             if (ob is int)         ob = (LSL_Integer)(int)ob;
+                        else if (ob is double) ob = (LSL_Float)(double)ob;
+                        else if (ob is string)      ob = (LSL_String)(string)ob;
+                        plist[++i] = ob;
+                    }
+                    m_DetectParams = evt.DetectParams;
+                    return new LSL_List (plist);
+                }
+
+                /*
+                 * It is a background event, simply call its event handler,
+                 * then check event queue again.
+                 */
+                callNo = 1;
+            __call1:
+                ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode,evc1];
+                if (seh == null) goto checktmo;
+
+                DetectParams[]  saveDetParams = this.m_DetectParams;
+                object[]        saveEHArgs    = this.ehArgs;
+                ScriptEventCode saveEventCode = this.eventCode;
+
+                this.m_DetectParams = evt.DetectParams;
+                this.ehArgs         = evt.Params;
+                this.eventCode      = evc;
+
+                try {
+                    seh (this);
+                } finally {
+                    this.m_DetectParams = saveDetParams;
+                    this.ehArgs         = saveEHArgs;
+                    this.eventCode      = saveEventCode;
+                }
+
+                /*
+                 * Keep waiting until we find a returnable event or timeout.
+                 */
+            checktmo:
+                if (DateTime.UtcNow < sleepUntil) goto findevent;
+
+                /*
+                 * We timed out, return an empty list.
+                 */
+                return emptyList;
+            } finally {
+                if (callMode != CallMode_NORMAL) {
+
+                    /*
+                     * Stack frame is being saved by CheckRun...().
+                     * Save everything we need at the __call<n> labels so we can restore it
+                     * when we need to.
+                     */
+                    sv    = CaptureStackFrame ("xmrEventDequeue", callNo, 9);
+                    sv[0] = sleepUntil.ToString ();                  // needed at __call0,__call1
+                    sv[1] = returnMask1;                             // needed at __call0,__call1
+                    sv[2] = returnMask2;                             // needed at __call0,__call1
+                    sv[3] = mask1;                                   // needed at __call0,__call1
+                    sv[4] = mask2;                                   // needed at __call0,__call1
+                    if (callNo == 1) {
+                        sv[5] = evc1;                                // needed at __call1
+                        sv[6] = (int)evc;                            // needed at __call1
+                        sv[7] = DetPrmsToObjArr (evt.DetectParams);  // needed at __call1
+                        sv[8] = evt.Params;                          // needed at __call1
+                    }
+                }
+            }
+        }
+
+        /**
+         * @brief Enqueue an event
+         * @param ev = as returned by xmrEventDequeue saying which event type to queue
+         *             and what argument list to pass to it.  The llDetect...() parameters
+         *             are as currently set for the script (use xmrEventLoadDets to set how
+         *             you want them to be different).
+         */
+        public override void xmrEventEnqueue (LSL_List ev)
+        {
+            object[] data = ev.Data;
+            ScriptEventCode evc = (ScriptEventCode)ListInt (data[0]);
+
+            int nargs = data.Length - 1;
+            object[] args = new object[nargs];
+            Array.Copy (data, 1, args, 0, nargs);
+
+            PostEvent (new EventParams (evc.ToString (), args, m_DetectParams));
+        }
+
+        /**
+         * @brief Save current detect params into a list
+         * @returns a list containing current detect param values
+         */
+        private const int saveDPVer = 1;
+
+        public override LSL_List xmrEventSaveDets ()
+        {
+            object[] obs = DetPrmsToObjArr (m_DetectParams);
+            return new LSL_List (obs);
+        }
+
+        private static object[] DetPrmsToObjArr (DetectParams[] dps)
+        {
+            int len = dps.Length;
+            object[] obs = new object[len*16+1];
+            int j = 0;
+            obs[j++] = (LSL_Integer)saveDPVer;
+            for (int i = 0; i < len; i ++) {
+                DetectParams dp = dps[i];
+                obs[j++] = (LSL_String)dp.Key.ToString();    // UUID
+                obs[j++] = dp.OffsetPos;                     // vector
+                obs[j++] = (LSL_Integer)dp.LinkNum;          // integer
+                obs[j++] = (LSL_String)dp.Group.ToString();  // UUID
+                obs[j++] = (LSL_String)dp.Name;              // string
+                obs[j++] = (LSL_String)dp.Owner.ToString();  // UUID
+                obs[j++] = dp.Position;                      // vector
+                obs[j++] = dp.Rotation;                      // rotation
+                obs[j++] = (LSL_Integer)dp.Type;             // integer
+                obs[j++] = dp.Velocity;                      // vector
+                obs[j++] = dp.TouchST;                       // vector
+                obs[j++] = dp.TouchNormal;                   // vector
+                obs[j++] = dp.TouchBinormal;                 // vector
+                obs[j++] = dp.TouchPos;                      // vector
+                obs[j++] = dp.TouchUV;                       // vector
+                obs[j++] = (LSL_Integer)dp.TouchFace;        // integer
+            }
+            return obs;
+        }
+
+
+        /**
+         * @brief Load current detect params from a list
+         * @param dpList = as returned by xmrEventSaveDets()
+         */
+        public override void xmrEventLoadDets (LSL_List dpList)
+        {
+            m_DetectParams = ObjArrToDetPrms (dpList.Data);
+        }
+
+        private static DetectParams[] ObjArrToDetPrms (object[] objs)
+        {
+            int j = 0;
+            if ((objs.Length % 16 != 1) || (ListInt (objs[j++]) != saveDPVer)) {
+                throw new Exception ("invalid detect param format");
+            }
+
+            int len = objs.Length / 16;
+            DetectParams[] dps = new DetectParams[len];
+
+            for (int i = 0; i < len; i ++) {
+                DetectParams dp = new DetectParams ();
+
+                dp.Key         = new UUID (ListStr (objs[j++]));
+                dp.OffsetPos   = (LSL_Vector)objs[j++];
+                dp.LinkNum     = ListInt (objs[j++]);
+                dp.Group       = new UUID (ListStr (objs[j++]));
+                dp.Name        = ListStr (objs[j++]);
+                dp.Owner       = new UUID (ListStr (objs[j++]));
+                dp.Position    = (LSL_Vector)objs[j++];
+                dp.Rotation    = (LSL_Rotation)objs[j++];
+                dp.Type        = ListInt (objs[j++]);
+                dp.Velocity    = (LSL_Vector)objs[j++];
+
+                SurfaceTouchEventArgs stea = new SurfaceTouchEventArgs ();
+
+                stea.STCoord   = LSLVec2OMVec ((LSL_Vector)objs[j++]);
+                stea.Normal    = LSLVec2OMVec ((LSL_Vector)objs[j++]);
+                stea.Binormal  = LSLVec2OMVec ((LSL_Vector)objs[j++]);
+                stea.Position  = LSLVec2OMVec ((LSL_Vector)objs[j++]);
+                stea.UVCoord   = LSLVec2OMVec ((LSL_Vector)objs[j++]);
+                stea.FaceIndex = ListInt (objs[j++]);
+
+                dp.SurfaceTouchArgs = stea;
+
+                dps[i] = dp;
+            }
+
+            return dps;
+        }
+
+        /**
+         * @brief The script is executing a 'state <newState>;' command.
+         * Tell outer layers to cancel any event triggers, like llListen(),
+         * then tell outer layers which events the new state has handlers for.
+         * We also clear the event queue as per http://wiki.secondlife.com/wiki/State
+         */
+        public override void StateChange()
+        {
+            /*
+             * Cancel any llListen()s etc.
+             * But llSetTimerEvent() should persist.
+             */
+            object[] timers = m_XMRLSLApi.acm.TimerPlugin.GetSerializationData(m_ItemID);
+            AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
+            m_XMRLSLApi.acm.TimerPlugin.CreateFromData(m_LocalID, m_ItemID, UUID.Zero, timers);
+
+            /*
+             * Tell whoever cares which event handlers the new state has.
+             */
+            m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(stateCode));
+
+            /*
+             * Clear out any old events from the queue.
+             */
+            lock (m_QueueLock) {
+                m_EventQueue.Clear();
+                for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
+            }
+        }
+
+        /**
+         * @brief Script is calling xmrStackLeft().
+         */
+        public override int xmrStackLeft ()
+        {
+            return microthread.StackLeft ();
+        }
+    }
+
+    /**
+     * @brief Thrown by things like llResetScript() to unconditionally
+     *        unwind as script and reset it to the default state_entry
+     *        handler.  We don't want script-level try/catch to intercept
+     *        these so scripts can't interfere with the behavior.
+     */
+    public class ScriptResetException : Exception, IXMRUncatchable { }
+
+    /**
+     * @brief Thrown by things like llDie() to unconditionally unwind as 
+     *        script.  We don't want script-level try/catch to intercept
+     *        these so scripts can't interfere with the behavior.
+     */
+    public class ScriptDieException : Exception, IXMRUncatchable { }
+}

+ 436 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs

@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Threading;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.Remoting.Lifetime;
+using System.Security.Policy;
+using System.IO;
+using System.Xml;
+using System.Text;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.ScriptEngine.Interfaces;
+using OpenSim.Region.ScriptEngine.Shared;
+using OpenSim.Region.ScriptEngine.Shared.Api;
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using OpenSim.Region.Framework.Scenes;
+using log4net;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public partial class XMRInstance
+    {
+        /********************************************************************************\
+         *  The only method of interest to outside this module is GetExecutionState()   *
+         *  which captures the current state of the script into an XML document.        *
+         *                                                                              *
+         *  The rest of this module contains support routines for GetExecutionState().  *
+        \********************************************************************************/
+
+        /**
+         * @brief Create an XML element that gives the current state of the script.
+         *   <ScriptState Engine="XMREngine" SourceHash=m_ObjCode.sourceHash Asset=m_Item.AssetID>
+         *     <Snapshot>globalsandstackdump</Snapshot>
+         *     <Running>m_Running</Running>
+         *     <DetectArray ...
+         *     <EventQueue ...
+         *     <Permissions ...
+         *     <Plugins />
+         *   </ScriptState>
+         * Updates the .state file while we're at it.
+         */
+        public XmlElement GetExecutionState(XmlDocument doc)
+        {
+            /*
+             * When we're detaching an attachment, we need to wait here.
+             */
+
+            // Change this to a 5 second timeout. If things do mess up,
+            // we don't want to be stuck forever.
+            //
+            m_DetachReady.WaitOne (5000, false);
+
+            XmlElement scriptStateN = doc.CreateElement("", "ScriptState", "");
+            scriptStateN.SetAttribute("Engine", m_Engine.ScriptEngineName);
+            scriptStateN.SetAttribute("Asset", m_Item.AssetID.ToString());
+            scriptStateN.SetAttribute ("SourceHash", m_ObjCode.sourceHash);
+
+            /*
+             * Make sure we aren't executing part of the script so it stays 
+             * stable.  Setting suspendOnCheckRun tells CheckRun() to suspend
+             * and return out so RunOne() will release the lock asap.
+             */
+            suspendOnCheckRunHold = true;
+            lock (m_RunLock)
+            {
+                m_RunOnePhase = "GetExecutionState enter";
+                CheckRunLockInvariants(true);
+
+                /*
+                 * Get copy of script globals and stack in relocateable form.
+                 */
+                MemoryStream snapshotStream = new MemoryStream();
+                MigrateOutEventHandler(snapshotStream);
+                Byte[] snapshotBytes = snapshotStream.ToArray();
+                snapshotStream.Close();
+                string snapshotString = Convert.ToBase64String(snapshotBytes);
+                XmlElement snapshotN = doc.CreateElement("", "Snapshot", "");
+                snapshotN.AppendChild(doc.CreateTextNode(snapshotString));
+                scriptStateN.AppendChild(snapshotN);
+                m_RunOnePhase = "GetExecutionState B"; CheckRunLockInvariants(true);
+
+                /*
+                 * "Running" says whether or not we are accepting new events.
+                 */
+                XmlElement runningN = doc.CreateElement("", "Running", "");
+                runningN.AppendChild(doc.CreateTextNode(m_Running.ToString()));
+                scriptStateN.AppendChild(runningN);
+                m_RunOnePhase = "GetExecutionState C"; CheckRunLockInvariants(true);
+
+                /*
+                 * "DoGblInit" says whether or not default:state_entry() will init global vars.
+                 */
+                XmlElement doGblInitN = doc.CreateElement("", "DoGblInit", "");
+                doGblInitN.AppendChild(doc.CreateTextNode(doGblInit.ToString()));
+                scriptStateN.AppendChild(doGblInitN);
+                m_RunOnePhase = "GetExecutionState D"; CheckRunLockInvariants(true);
+
+                /*
+                 * More misc data.
+                 */
+                XmlNode permissionsN = doc.CreateElement("", "Permissions", "");
+                scriptStateN.AppendChild(permissionsN);
+
+                XmlAttribute granterA = doc.CreateAttribute("", "granter", "");
+                granterA.Value = m_Item.PermsGranter.ToString();
+                permissionsN.Attributes.Append(granterA);
+
+                XmlAttribute maskA = doc.CreateAttribute("", "mask", "");
+                maskA.Value = m_Item.PermsMask.ToString();
+                permissionsN.Attributes.Append(maskA);
+                m_RunOnePhase = "GetExecutionState E"; CheckRunLockInvariants(true);
+
+                /*
+                 * "DetectParams" are returned by llDetected...() script functions
+                 * for the currently active event, if any.
+                 */
+                if (m_DetectParams != null)
+                {
+                    XmlElement detParArrayN = doc.CreateElement("", "DetectArray", "");
+                    AppendXMLDetectArray(doc, detParArrayN, m_DetectParams);
+                    scriptStateN.AppendChild(detParArrayN);
+                }
+                m_RunOnePhase = "GetExecutionState F"; CheckRunLockInvariants(true);
+
+                /*
+                 * Save any events we have in the queue.
+                 * <EventQueue>
+                 *   <Event Name="...">
+                 *     <param>...</param> ...
+                 *     <DetectParams>...</DetectParams> ...
+                 *   </Event>
+                 *   ...
+                 * </EventQueue>
+                 */
+                XmlElement queuedEventsN = doc.CreateElement("", "EventQueue", "");
+                lock (m_QueueLock)
+                {
+                    foreach (EventParams evt in m_EventQueue)
+                    {
+                        XmlElement singleEventN = doc.CreateElement("", "Event", "");
+                        singleEventN.SetAttribute("Name", evt.EventName);
+                        AppendXMLObjectArray(doc, singleEventN, evt.Params, "param");
+                        AppendXMLDetectArray(doc, singleEventN, evt.DetectParams);
+                        queuedEventsN.AppendChild(singleEventN);
+                    }
+                }
+                scriptStateN.AppendChild(queuedEventsN);
+                m_RunOnePhase = "GetExecutionState G"; CheckRunLockInvariants(true);
+
+                /*
+                 * "Plugins" indicate enabled timers and listens, etc.
+                 */
+                Object[] pluginData = 
+                        AsyncCommandManager.GetSerializationData(m_Engine,
+                                m_ItemID);
+
+                XmlNode plugins = doc.CreateElement("", "Plugins", "");
+                AppendXMLObjectArray(doc, plugins, pluginData, "plugin");
+                scriptStateN.AppendChild(plugins);
+                m_RunOnePhase = "GetExecutionState H"; CheckRunLockInvariants(true);
+
+                /*
+                 * Let script run again.
+                 */
+                suspendOnCheckRunHold = false;
+
+                m_RunOnePhase = "GetExecutionState leave";
+                CheckRunLockInvariants(true);
+            }
+
+            /*
+             * scriptStateN represents the contents of the .state file so
+             * write the .state file while we are here.
+             */
+            FileStream fs = File.Create(m_StateFileName);
+            StreamWriter sw = new StreamWriter(fs);
+            sw.Write(scriptStateN.OuterXml);
+            sw.Close();
+            fs.Close();
+
+            return scriptStateN;
+        }
+
+        /**
+         * @brief Write script state to output stream.
+         *        The script microthread is at same state on return,
+         *        ie, either inactive or suspended inside CheckRun().
+         *
+         * Input:
+         *  stream = stream to write event handler state information to
+         */
+        private void MigrateOutEventHandler (Stream stream)
+        {
+            moehexcep = null;
+
+            // do all the work in the MigrateOutEventHandlerThread() method below
+            moehstream = stream;
+
+            XMRScriptThread cst = XMRScriptThread.CurrentScriptThread ();
+            if (cst != null) {
+
+                // we might be getting called inside some LSL Api function
+                // so we are already in script thread and thus must do
+                // migration directly
+                MigrateOutEventHandlerThread ();
+            } else {
+
+                // some other thread, do migration via a script thread
+                lock (XMRScriptThread.m_WakeUpLock) {
+                    m_Engine.m_ThunkQueue.Enqueue (this.MigrateOutEventHandlerThread);
+                }
+                XMRScriptThread.WakeUpOne ();
+
+                // wait for it to complete
+                lock (moehdone) {
+                    while (moehstream != null) {
+                        Monitor.Wait (moehdone);
+                    }
+                }
+            }
+
+            // maybe it threw up
+            if (moehexcep != null) throw moehexcep;
+        }
+        private Exception moehexcep;
+        private object moehdone = new object ();
+        private Stream moehstream;
+        private void MigrateOutEventHandlerThread ()
+        {
+            Exception except;
+
+            try {
+
+                /*
+                 * Resume the microthread and it will throw a StackCaptureException()
+                 * with the stack frames saved to this.stackFrames.
+                 * Then write the saved stack frames to the output stream.
+                 *
+                 * There is a stack only if the event code is not None.
+                 */
+                if (this.eventCode != ScriptEventCode.None) {
+
+                    // tell microthread to continue
+                    // it should see captureStackFrames and throw StackCaptureException()
+                    // ...generating XMRStackFrames as it unwinds
+                    this.captureStackFrames = true;
+                    except = this.microthread.ResumeEx ();
+                    this.captureStackFrames = false;
+                    if (except == null) {
+                        throw new Exception ("stack save did not complete");
+                    }
+                    if (!(except is StackCaptureException)) {
+                        throw except;
+                    }
+                }
+
+                /*
+                 * Write script state out, frames and all, to the stream.
+                 * Does not change script state.
+                 */
+                moehstream.WriteByte (migrationVersion);
+                moehstream.WriteByte ((byte)16);
+                this.MigrateOut (new BinaryWriter (moehstream));
+
+                /*
+                 * Now restore script stack.
+                 * Microthread will suspend inside CheckRun() when restore is complete.
+                 */
+                if (this.eventCode != ScriptEventCode.None) {
+                    this.stackFramesRestored = false;
+                    except = this.microthread.StartEx ();
+                    if (except != null) {
+                        throw except;
+                    }
+                    if (!this.stackFramesRestored) {
+                        throw new Exception ("restore after save did not complete");
+                    }
+                }
+            } catch (Exception e) {
+                moehexcep = e;
+            } finally {
+
+                // make sure CheckRunLockInvariants() won't puque
+                if (this.microthread.Active () == 0) {
+                    this.eventCode = ScriptEventCode.None;
+                }
+
+                // wake the MigrateOutEventHandler() method above
+                lock (moehdone) {
+                    moehstream = null;
+                    Monitor.Pulse (moehdone);
+                }
+            }
+        }
+
+        /**
+         * @brief Convert an DetectParams[] to corresponding XML.
+         *        DetectParams[] holds the values retrievable by llDetected...() for
+         *        a given event.
+         */
+        private static void AppendXMLDetectArray(XmlDocument doc, XmlElement parent, DetectParams[] detect)
+        {
+            foreach (DetectParams d in detect)
+            {
+                XmlElement detectParamsN = GetXMLDetect(doc, d);
+                parent.AppendChild(detectParamsN);
+            }
+        }
+
+        private static XmlElement GetXMLDetect(XmlDocument doc, DetectParams d)
+        {
+            XmlElement detectParamsN = doc.CreateElement("", "DetectParams", "");
+
+            XmlAttribute d_key = doc.CreateAttribute("", "key", "");
+            d_key.Value = d.Key.ToString();
+            detectParamsN.Attributes.Append(d_key);
+
+            XmlAttribute pos = doc.CreateAttribute("", "pos", "");
+            pos.Value = d.OffsetPos.ToString();
+            detectParamsN.Attributes.Append(pos);
+
+            XmlAttribute d_linkNum = doc.CreateAttribute("", "linkNum", "");
+            d_linkNum.Value = d.LinkNum.ToString();
+            detectParamsN.Attributes.Append(d_linkNum);
+
+            XmlAttribute d_group = doc.CreateAttribute("", "group", "");
+            d_group.Value = d.Group.ToString();
+            detectParamsN.Attributes.Append(d_group);
+
+            XmlAttribute d_name = doc.CreateAttribute("", "name", "");
+            d_name.Value = d.Name.ToString();
+            detectParamsN.Attributes.Append(d_name);
+
+            XmlAttribute d_owner = doc.CreateAttribute("", "owner", "");
+            d_owner.Value = d.Owner.ToString();
+            detectParamsN.Attributes.Append(d_owner);
+
+            XmlAttribute d_position = doc.CreateAttribute("", "position", "");
+            d_position.Value = d.Position.ToString();
+            detectParamsN.Attributes.Append(d_position);
+
+            XmlAttribute d_rotation = doc.CreateAttribute("", "rotation", "");
+            d_rotation.Value = d.Rotation.ToString();
+            detectParamsN.Attributes.Append(d_rotation);
+
+            XmlAttribute d_type = doc.CreateAttribute("", "type", "");
+            d_type.Value = d.Type.ToString();
+            detectParamsN.Attributes.Append(d_type);
+
+            XmlAttribute d_velocity = doc.CreateAttribute("", "velocity", "");
+            d_velocity.Value = d.Velocity.ToString();
+            detectParamsN.Attributes.Append(d_velocity);
+
+            return detectParamsN;
+        }
+
+        /**
+         * @brief Append elements of an array of objects to an XML parent.
+         * @param doc = document the parent is part of
+         * @param parent = parent to append the items to
+         * @param array = array of objects
+         * @param tag = <tag ..>...</tag> for each element
+         */
+        private static void AppendXMLObjectArray(XmlDocument doc, XmlNode parent, object[] array, string tag)
+        {
+            foreach (object o in array)
+            {
+                XmlElement element = GetXMLObject(doc, o, tag);
+                parent.AppendChild(element);
+            }
+        }
+
+        /**
+         * @brief Get and XML representation of an object.
+         * @param doc = document the tag will be put in
+         * @param o = object to be represented
+         * @param tag = <tag ...>...</tag>
+         */
+        private static XmlElement GetXMLObject(XmlDocument doc, object o, string tag)
+        {
+            XmlAttribute typ = doc.CreateAttribute("", "type", "");
+            XmlElement n = doc.CreateElement("", tag, "");
+
+            if (o is LSL_List)
+            {
+                typ.Value = "list";
+                n.Attributes.Append(typ);
+                AppendXMLObjectArray(doc, n, ((LSL_List)o).Data, "item");
+            }
+            else
+            {
+                typ.Value = o.GetType().ToString();
+                n.Attributes.Append(typ);
+                n.AppendChild(doc.CreateTextNode(o.ToString()));
+            }
+            return n;
+        }
+    }
+}

+ 878 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs

@@ -0,0 +1,878 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Threading;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.Remoting.Lifetime;
+using System.Security.Policy;
+using System.IO;
+using System.Xml;
+using System.Text;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.ScriptEngine.Interfaces;
+using OpenSim.Region.ScriptEngine.Shared;
+using OpenSim.Region.ScriptEngine.Shared.Api;
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using OpenSim.Region.Framework.Scenes;
+using log4net;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public partial class XMRInstance
+    {
+        /****************************************************************************\
+         *  The only method of interest to outside this module is the Initializer.  *
+         *                                                                          *
+         *  The rest of this module contains support routines for the Initializer.  *
+        \****************************************************************************/
+
+        /**
+         * @brief Initializer, loads script in memory and all ready for running.
+         * @param engine = XMREngine instance this is part of
+         * @param scriptBasePath = directory name where files are
+         * @param stackSize = number of bytes to allocate for stacks
+         * @param errors = return compiler errors in this array
+         * @param forceRecomp = force recompile
+         * Throws exception if any error, so it was successful if it returns.
+         */
+        public void Initialize(XMREngine engine, string scriptBasePath,
+                               int stackSize, int heapSize, ArrayList errors)
+        {
+            if (stackSize < 16384) stackSize = 16384;
+            if (heapSize  < 16384) heapSize  = 16384;
+
+            /*
+             * Save all call parameters in instance vars for easy access.
+             */
+            m_Engine         = engine;
+            m_ScriptBasePath = scriptBasePath;
+            m_StackSize      = stackSize;
+            m_HeapSize       = heapSize;
+            m_CompilerErrors = errors;
+            m_StateFileName  = GetStateFileName(scriptBasePath, m_ItemID);
+
+            /*
+             * Not in any XMRInstQueue.
+             */
+            m_NextInst = this;
+            m_PrevInst = this;
+
+            /*
+             * Set up list of API calls it has available.
+             * This also gets the API modules ready to accept setup data, such as
+             * active listeners being restored.
+             */
+            IScriptApi scriptApi;
+            ApiManager am = new ApiManager();
+            foreach (string api in am.GetApis())
+            {
+                /*
+                 * Instantiate the API for this script instance.
+                 */
+                if (api != "LSL") {
+                    scriptApi = am.CreateApi(api);
+                } else {
+                    scriptApi = m_XMRLSLApi = new XMRLSL_Api();
+                }
+
+                /*
+                 * Connect it up to the instance.
+                 */
+                InitScriptApi (engine, api, scriptApi);
+            }
+
+            m_XMRLSLApi.InitXMRLSLApi(this);
+
+            /*
+             * Get object loaded, compiling script and reading .state file as
+             * necessary to restore the state.
+             */
+            suspendOnCheckRunHold = true;
+            InstantiateScript();
+            m_SourceCode = null;
+            if (m_ObjCode == null) throw new ArgumentNullException ("m_ObjCode");
+            if (m_ObjCode.scriptEventHandlerTable == null) {
+                throw new ArgumentNullException ("m_ObjCode.scriptEventHandlerTable");
+            }
+
+            suspendOnCheckRunHold = false;
+            suspendOnCheckRunTemp = false;
+
+            /*
+             * Declare which events the script's current state can handle.
+             */
+            int eventMask = GetStateEventFlags(stateCode);
+            m_Part.SetScriptEvents(m_ItemID, eventMask);
+        }
+
+        private void InitScriptApi (XMREngine engine, string api, IScriptApi scriptApi)
+        {
+            /*
+             * Set up m_ApiManager_<APINAME> = instance pointer.
+             */
+            engine.m_XMRInstanceApiCtxFieldInfos[api].SetValue (this, scriptApi);
+
+            /*
+             * Initialize the API instance.
+             */
+            scriptApi.Initialize(m_Engine, m_Part, m_Item);
+            this.InitApi (api, scriptApi);
+        }
+
+
+        // Get script object code loaded in memory and all ready to run,
+        // ready to resume it from where the .state file says it was last
+        private void InstantiateScript()
+        {
+            bool compiledIt = false;
+            ScriptObjCode objCode;
+
+            /*
+             * If source code string is empty, use the asset ID as the object file name.
+             * Allow lines of // comments at the beginning (for such as engine selection).
+             */
+            int i, j, len;
+            if (m_SourceCode == null) m_SourceCode = String.Empty;
+            for (len = m_SourceCode.Length; len > 0; -- len) {
+                if (m_SourceCode[len-1] > ' ') break;
+            }
+            for (i = 0; i < len; i ++) {
+                char c = m_SourceCode[i];
+                if (c <= ' ') continue;
+                if (c != '/') break;
+                if ((i + 1 >= len) || (m_SourceCode[i+1] != '/')) break;
+                i = m_SourceCode.IndexOf ('\n', i);
+                if (i < 0) i = len - 1;
+            }
+            if ((i >= len) || !m_Engine.m_UseSourceHashCode) {
+ 
+                /*
+                 * Source consists of nothing but // comments and whitespace,
+                 * or we are being forced to use the asset-id as the key, to
+                 * open an already existing object code file.
+                 */
+                m_ScriptObjCodeKey = m_Item.AssetID.ToString ();
+                if (i >= len) m_SourceCode = "";
+            } else {
+
+                /*
+                 * Make up dictionary key for the object code.
+                 * Use the same object code for identical source code
+                 * regardless of asset ID, so we don't care if they
+                 * copy scripts or not.
+                 */
+                byte[] scbytes   = System.Text.Encoding.UTF8.GetBytes (m_SourceCode);
+                StringBuilder sb = new StringBuilder ((256 + 5) / 6);
+                ByteArrayToSixbitStr (sb, System.Security.Cryptography.SHA256.Create ().ComputeHash (scbytes));
+                m_ScriptObjCodeKey = sb.ToString ();
+
+                /*
+                 * But source code can be just a sixbit string itself
+                 * that identifies an already existing object code file.
+                 */
+                if (len - i == m_ScriptObjCodeKey.Length) {
+                    for (j = len; -- j >= i;) {
+                        if (sixbit.IndexOf (m_SourceCode[j]) < 0) break;
+                    }
+                    if (j < i) {
+                        m_ScriptObjCodeKey = m_SourceCode.Substring (i, len - i);
+                        m_SourceCode = "";
+                    }
+                }
+            }
+
+            /*
+             * There may already be an ScriptObjCode struct in memory that
+             * we can use.  If not, try to compile it.
+             */
+            lock (m_CompileLock) {
+                if (!m_CompiledScriptObjCode.TryGetValue (m_ScriptObjCodeKey, out objCode) || m_ForceRecomp) {
+                    objCode = TryToCompile ();
+                    compiledIt = true;
+                }
+
+                /*
+                 * Loaded successfully, increment reference count.
+                 *
+                 * If we just compiled it though, reset count to 0 first as
+                 * this is the one-and-only existance of this objCode struct,
+                 * and we want any old ones for this source code to be garbage
+                 * collected.
+                 */
+                if (compiledIt) {
+                    m_CompiledScriptObjCode[m_ScriptObjCodeKey] = objCode;
+                    objCode.refCount = 0;
+                }
+                objCode.refCount ++;
+
+                /*
+                 * Now set up to decrement ref count on dispose.
+                 */
+                m_ObjCode = objCode;
+            }
+
+            try {
+    
+                /*
+                 * Fill in script instance from object code
+                 * Script instance is put in a "never-ever-has-run-before" state.
+                 */
+                LoadObjCode();
+
+                /*
+                 * Fill in script intial state
+                 * - either as loaded from a .state file
+                 * - or initial default state_entry() event
+                 */
+                LoadInitialState();
+            } catch {
+
+                /*
+                 * If any error loading, decrement object code reference count.
+                 */
+                DecObjCodeRefCount ();
+                throw;
+            }
+        }
+
+        private const string sixbit = "0123456789_abcdefghijklmnopqrstuvwxyz@ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        private static void ByteArrayToSixbitStr (StringBuilder sb, byte[] bytes)
+        {
+            int bit = 0;
+            int val = 0;
+            foreach (byte b in bytes) {
+                val |= (int)((uint)b << bit);
+                bit += 8;
+                while (bit >= 6) {
+                    sb.Append (sixbit[val&63]);
+                    val >>= 6;
+                    bit  -= 6;
+                }
+            }
+            if (bit > 0) {
+                sb.Append (sixbit[val&63]);
+            }
+        }
+
+        // Try to create object code from source code
+        // If error, just throw exception
+        private ScriptObjCode TryToCompile ()
+        {
+            m_CompilerErrors.Clear();
+
+            /*
+             * If object file exists, create ScriptObjCode directly from that.
+             * Otherwise, compile the source to create object file then create
+             * ScriptObjCode from that.
+             */
+            string assetID  = m_Item.AssetID.ToString();
+            m_CameFrom = "asset://" + assetID;
+            ScriptObjCode objCode = Compile ();
+            if (m_CompilerErrors.Count != 0)
+            {
+                throw new Exception ("compilation errors");
+            }
+            if (objCode == null)
+            {
+                throw new Exception ("compilation failed");
+            }
+
+            return objCode;
+        }
+
+        /*
+         * Retrieve source from asset server.
+         */
+        private string FetchSource (string cameFrom)
+        {
+            m_log.Debug ("[XMREngine]: fetching source " + cameFrom);
+            if (!cameFrom.StartsWith ("asset://")) {
+                throw new Exception ("unable to retrieve source from " + cameFrom);
+            }
+            string assetID = cameFrom.Substring (8);
+            AssetBase asset = m_Engine.World.AssetService.Get(assetID);
+            if (asset == null) {
+                throw new Exception ("source not found " + cameFrom);
+            }
+            string source = Encoding.UTF8.GetString (asset.Data);
+            if (EmptySource (source)) {
+                throw new Exception ("fetched source empty " + cameFrom);
+            }
+            return source;
+        }
+
+        /*
+         * Fill in script object initial contents.
+         * Set the initial state to "default".
+         */
+        private void LoadObjCode ()
+        {
+            /*
+             * Script must leave this much stack remaining on calls to CheckRun().
+             */
+            this.stackLimit = m_StackSize / 2;
+
+            /*
+             * This is how many total heap bytes script is allowed to use.
+             */
+            this.heapLimit = m_HeapSize;
+
+            /*
+             * Allocate global variable arrays.
+             */
+            this.glblVars.AllocVarArrays (m_ObjCode.glblSizes);
+
+            /*
+             * Script can handle these event codes.
+             */
+            m_HaveEventHandlers = new bool[m_ObjCode.scriptEventHandlerTable.GetLength(1)];
+            for (int i = m_ObjCode.scriptEventHandlerTable.GetLength(0); -- i >= 0;) {
+                for (int j = m_ObjCode.scriptEventHandlerTable.GetLength(1); -- j >= 0;) {
+                    if (m_ObjCode.scriptEventHandlerTable[i,j] != null) {
+                        m_HaveEventHandlers[j] = true;
+                    }
+                }
+            }
+
+            /*
+             * Set up microthread object which actually calls the script event handler functions.
+             */
+            this.microthread = (IScriptUThread)m_Engine.uThreadCtor.Invoke (new object[] { this });
+        }
+
+        //  LoadInitialState()
+        //      if no state XML file exists for the asset,
+        //          post initial default state events
+        //      else
+        //          try to restore from .state file
+        //  If any error, throw exception
+        //
+        private void LoadInitialState()
+        {
+            /*
+             * If no .state file exists, start from default state
+             * Otherwise, read initial state from the .state file
+             */
+            if (!File.Exists(m_StateFileName)) {
+                m_Running = true;                  // event processing is enabled
+                eventCode = ScriptEventCode.None;  // not processing any event
+
+                // default state_entry() must initialize global variables
+                doGblInit = true;
+                stateCode = 0;
+
+                PostEvent(new EventParams("state_entry", 
+                                          zeroObjectArray,
+                                          zeroDetectParams));
+            } else {
+                FileStream fs = File.Open(m_StateFileName, 
+                                          FileMode.Open, 
+                                          FileAccess.Read);
+                StreamReader ss = new StreamReader(fs);
+                string xml = ss.ReadToEnd();
+                ss.Close();
+                fs.Close();
+
+                XmlDocument doc = new XmlDocument();
+                doc.LoadXml(xml);
+                LoadScriptState(doc);
+            }
+
+            /*
+             * Post event(s) saying what caused the script to start.
+             */
+            if (m_PostOnRez) {
+                PostEvent(new EventParams("on_rez",
+                          new Object[] { m_StartParam }, 
+                          zeroDetectParams));
+            }
+
+            switch (m_StateSource) {
+                case StateSource.AttachedRez: {
+//                    PostEvent(new EventParams("attach",
+//                              new object[] { m_Part.ParentGroup.AttachedAvatar.ToString() }, 
+//                              zeroDetectParams));
+                    break;
+                }
+
+                case StateSource.PrimCrossing: {
+                    PostEvent(new EventParams("changed",
+                              sbcCR, 
+                              zeroDetectParams));
+                    break;
+                }
+
+                case StateSource.Teleporting: {
+                    PostEvent(new EventParams("changed",
+                              sbcCR, 
+                              zeroDetectParams));
+                    PostEvent(new EventParams("changed",
+                              sbcCT, 
+                              zeroDetectParams));
+                    break;
+                }
+
+                case StateSource.RegionStart: {
+                    PostEvent(new EventParams("changed",
+                              sbcCRS, 
+                              zeroDetectParams));
+                    break;
+                }
+            }
+        }
+
+        private static Object[] sbcCRS = new Object[] { ScriptBaseClass.CHANGED_REGION_START };
+        private static Object[] sbcCR  = new Object[] { ScriptBaseClass.CHANGED_REGION };
+        private static Object[] sbcCT  = new Object[] { ScriptBaseClass.CHANGED_TELEPORT };
+
+        /**
+         * @brief Save compilation error messages for later retrieval
+         *        via GetScriptErrors().
+         */
+        private void ErrorHandler(Token token, string message)
+        {
+            if (token != null) {
+                string srcloc = token.SrcLoc;
+                if (srcloc.StartsWith (m_CameFrom)) {
+                    srcloc = srcloc.Substring (m_CameFrom.Length);
+                }
+                m_CompilerErrors.Add(srcloc + " Error: " + message);
+            } else if (message != null) {
+                m_CompilerErrors.Add("(0,0) Error: " + message);
+            } else {
+                m_CompilerErrors.Add("(0,0) Error compiling, see exception in log");
+            }
+        }
+
+        /**
+         * @brief Load script state from the given XML doc into the script memory
+         *  <ScriptState Engine="XMREngine" Asset=...>
+         *      <Running>...</Running>
+         *      <DoGblInit>...</DoGblInit>
+         *      <Permissions granted=... mask=... />
+         *      RestoreDetectParams()
+         *      <Plugins>
+         *          ExtractXMLObjectArray("plugin")
+         *      </Plugins>
+         *      <Snapshot>
+         *          MigrateInEventHandler()
+         *      </Snapshot>
+         *  </ScriptState>
+         */
+        private void LoadScriptState(XmlDocument doc)
+        {
+            DetectParams[] detParams;
+            LinkedList<EventParams> eventQueue;
+
+            // Everything we know is enclosed in <ScriptState>...</ScriptState>
+            XmlElement scriptStateN = (XmlElement)doc.SelectSingleNode("ScriptState");
+            if (scriptStateN == null) {
+                throw new Exception("no <ScriptState> tag");
+            }
+            string sen = scriptStateN.GetAttribute("Engine");
+            if ((sen == null) || (sen != m_Engine.ScriptEngineName)) {
+                throw new Exception("<ScriptState> missing Engine=\"XMREngine\" attribute");
+            }
+
+            // AssetID is unique for the script source text so make sure the
+            // state file was written for that source file
+            string assetID = scriptStateN.GetAttribute("Asset");
+            if (assetID != m_Item.AssetID.ToString())
+            {
+                throw new Exception("<ScriptState> assetID mismatch");
+            }
+
+            // Also match the sourceHash in case script was
+            // loaded via 'xmroption fetchsource' and has changed
+            string sourceHash = scriptStateN.GetAttribute ("SourceHash");
+            if ((sourceHash == null) || (sourceHash != m_ObjCode.sourceHash)) {
+                throw new Exception ("<ScriptState> SourceHash mismatch");
+            }
+
+            // Get various attributes
+            XmlElement runningN = (XmlElement)scriptStateN.SelectSingleNode("Running");
+            m_Running = bool.Parse(runningN.InnerText);
+
+            XmlElement doGblInitN = (XmlElement)scriptStateN.SelectSingleNode("DoGblInit");
+            doGblInit = bool.Parse(doGblInitN.InnerText);
+
+            XmlElement permissionsN = (XmlElement)scriptStateN.SelectSingleNode("Permissions");
+            m_Item.PermsGranter = new UUID(permissionsN.GetAttribute("granter"));
+            m_Item.PermsMask = Convert.ToInt32(permissionsN.GetAttribute("mask"));
+            m_Part.Inventory.UpdateInventoryItem(m_Item, false, false);
+
+            // get values used by stuff like llDetectedGrab, etc.
+            detParams = RestoreDetectParams(scriptStateN.SelectSingleNode("DetectArray"));
+
+            // Restore queued events
+            eventQueue = RestoreEventQueue(scriptStateN.SelectSingleNode("EventQueue"));
+
+            // Restore timers and listeners
+            XmlElement pluginN = (XmlElement)scriptStateN.SelectSingleNode("Plugins");
+            Object[] pluginData = ExtractXMLObjectArray(pluginN, "plugin");
+
+            // Script's global variables and stack contents
+            XmlElement snapshotN = 
+                    (XmlElement)scriptStateN.SelectSingleNode("Snapshot");
+
+            Byte[] data = Convert.FromBase64String(snapshotN.InnerText);
+            MemoryStream ms = new MemoryStream();
+            ms.Write(data, 0, data.Length);
+            ms.Seek(0, SeekOrigin.Begin);
+            MigrateInEventHandler(ms);
+            ms.Close();
+
+            // Restore event queues, preserving any events that queued
+            // whilst we were restoring the state
+            lock (m_QueueLock) {
+                m_DetectParams = detParams;
+                foreach (EventParams evt in m_EventQueue) {
+                    eventQueue.AddLast (evt);
+                }
+                m_EventQueue   = eventQueue;
+                for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
+                foreach (EventParams evt in m_EventQueue)
+                {
+                    ScriptEventCode eventCode = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
+                                                                             evt.EventName);
+                    m_EventCounts[(int)eventCode] ++;
+                }
+            }
+
+            // Requeue timer and listeners (possibly queuing new events)
+            AsyncCommandManager.CreateFromData(m_Engine,
+                    m_LocalID, m_ItemID, m_Part.UUID,
+                    pluginData);
+        }
+
+        /**
+         * @brief Read llDetectedGrab, etc, values from XML
+         *  <EventQueue>
+         *      <DetectParams>...</DetectParams>
+         *          .
+         *          .
+         *          .
+         *  </EventQueue>
+         */
+        private LinkedList<EventParams> RestoreEventQueue(XmlNode eventsN)
+        {
+            LinkedList<EventParams> eventQueue = new LinkedList<EventParams>();
+            if (eventsN != null) {
+                XmlNodeList eventL = eventsN.SelectNodes("Event");
+                foreach (XmlNode evnt in eventL)
+                {
+                    string name            = ((XmlElement)evnt).GetAttribute("Name");
+                    object[] parms         = ExtractXMLObjectArray(evnt, "param");
+                    DetectParams[] detects = RestoreDetectParams(evnt);
+
+                    if (parms   == null) parms   = zeroObjectArray;
+                    if (detects == null) detects = zeroDetectParams;
+
+                    EventParams evt = new EventParams(name, parms, detects);
+                    eventQueue.AddLast(evt);
+                }
+            }
+            return eventQueue;
+        }
+
+        /**
+         * @brief Read llDetectedGrab, etc, values from XML
+         *  <DetectArray>
+         *      <DetectParams>...</DetectParams>
+         *          .
+         *          .
+         *          .
+         *  </DetectArray>
+         */
+        private DetectParams[] RestoreDetectParams(XmlNode detectedN)
+        {
+            if (detectedN == null) return null;
+
+            List<DetectParams> detected = new List<DetectParams>();
+            XmlNodeList detectL = detectedN.SelectNodes("DetectParams");
+
+            DetectParams detprm = new DetectParams();
+            foreach (XmlNode detxml in detectL) {
+                try {
+                    detprm.Group     = new UUID(detxml.Attributes.GetNamedItem("group").Value);
+                    detprm.Key       = new UUID(detxml.Attributes.GetNamedItem("key").Value);
+                    detprm.Owner     = new UUID(detxml.Attributes.GetNamedItem("owner").Value);
+
+                    detprm.LinkNum   = Int32.Parse(detxml.Attributes.GetNamedItem("linkNum").Value);
+                    detprm.Type      = Int32.Parse(detxml.Attributes.GetNamedItem("type").Value);
+
+                    detprm.Name      = detxml.Attributes.GetNamedItem("name").Value;
+
+                    detprm.OffsetPos = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("pos").Value);
+                    detprm.Position  = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("position").Value);
+                    detprm.Velocity  = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("velocity").Value);
+
+                    detprm.Rotation  = new LSL_Types.Quaternion(detxml.Attributes.GetNamedItem("rotation").Value);
+
+                    detected.Add(detprm);
+                    detprm = new DetectParams();
+                } catch (Exception e) {
+                    m_log.Warn("[XMREngine]: RestoreDetectParams bad XML: " + detxml.ToString());
+                    m_log.Warn("[XMREngine]: ... " + e.ToString());
+                }
+            }
+
+            return detected.ToArray();
+        }
+
+        /**
+         * @brief Extract elements of an array of objects from an XML parent.
+         *        Each element is of form <tag ...>...</tag>
+         * @param parent = XML parent to extract them from
+         * @param tag = what the value's tag is
+         * @returns object array of the values
+         */
+        private static object[] ExtractXMLObjectArray(XmlNode parent, string tag)
+        {
+            List<Object> olist = new List<Object>();
+
+            XmlNodeList itemL = parent.SelectNodes(tag);
+            foreach (XmlNode item in itemL)
+            {
+                olist.Add(ExtractXMLObjectValue(item));
+            }
+
+            return olist.ToArray();
+        }
+
+        private static object ExtractXMLObjectValue(XmlNode item)
+        {
+            string itemType = item.Attributes.GetNamedItem("type").Value;
+
+            if (itemType == "list")
+            {
+                return new LSL_List(ExtractXMLObjectArray(item, "item"));
+            }
+
+            if (itemType == "OpenMetaverse.UUID")
+            {
+                UUID val = new UUID();
+                UUID.TryParse(item.InnerText, out val);
+                return val;
+            }
+
+            Type itemT = Type.GetType(itemType);
+            if (itemT == null)
+            {
+                Object[] args = new Object[] { item.InnerText };
+
+                string assembly = itemType + ", OpenSim.Region.ScriptEngine.Shared";
+                itemT = Type.GetType(assembly);
+                if (itemT == null)
+                {
+                    return null;
+                }
+                return Activator.CreateInstance(itemT, args);
+            }
+
+            return Convert.ChangeType(item.InnerText, itemT);
+        }
+
+        /*
+         * Migrate an event handler in from a stream.
+         *
+         * Input:
+         *  stream = as generated by MigrateOutEventHandler()
+         */
+        private void MigrateInEventHandler (Stream stream)
+        {
+            miehexcep = null;
+
+            // do all the work in the MigrateInEventHandlerThread() method below
+            miehstream = stream;
+
+            XMRScriptThread cst = XMRScriptThread.CurrentScriptThread ();
+            if (cst != null) {
+
+                // in case we are getting called inside some LSL Api function
+                MigrateInEventHandlerThread ();
+            } else {
+
+                // some other thread, do migration via a script thread
+                lock (XMRScriptThread.m_WakeUpLock) {
+                    m_Engine.m_ThunkQueue.Enqueue (this.MigrateInEventHandlerThread);
+                }
+                XMRScriptThread.WakeUpOne ();
+
+                // wait for it to complete
+                lock (miehdone) {
+                    while (miehstream != null) {
+                        Monitor.Wait (miehdone);
+                    }
+                }
+            }
+
+            // maybe it threw up
+            if (miehexcep != null) throw miehexcep;
+        }
+        private Exception miehexcep;
+        private object miehdone = new object ();
+        private Stream miehstream;
+        private void MigrateInEventHandlerThread ()
+        {
+            try {
+                int mv = miehstream.ReadByte ();
+                if (mv != migrationVersion) {
+                    throw new Exception ("incoming migration version " + mv + " but accept only " + migrationVersion);
+                }
+                miehstream.ReadByte ();  // ignored
+
+                /*
+                 * Restore script variables and stack and other state from stream.
+                 * And it also marks us busy (by setting this.eventCode) so we can't be
+                 * started again and this event lost.
+                 */
+                BinaryReader br = new BinaryReader (miehstream);
+                this.MigrateIn (br);
+
+                /*
+                 * If eventCode is None, it means the script was idle when migrated.
+                 */
+                if (this.eventCode != ScriptEventCode.None) {
+
+                    /*
+                     * So microthread.Start() calls XMRScriptUThread.Main() which calls the
+                     * event handler function.  The event handler function sees the stack
+                     * frames in this.stackFrames and restores its args and locals, then calls
+                     * whatever it was calling when the snapshot was taken.  That function also
+                     * sees this.stackFrames and restores its args and locals, and so on...
+                     * Eventually it gets to the point of calling CheckRun() which sees we are
+                     * doing a restore and it suspends, returning here with the microthread
+                     * stack all restored.  It shouldn't ever throw an exception.
+                     */
+                    this.stackFramesRestored = false;
+                    Exception te = microthread.StartEx ();
+                    if (te != null) throw te;
+                    if (!this.stackFramesRestored) throw new Exception ("migrate in did not complete");
+                }
+            } catch (Exception e) {
+                miehexcep = e;
+            } finally {
+
+                /*
+                 * Wake the MigrateInEventHandler() method above.
+                 */
+                lock (miehdone) {
+                    miehstream = null;
+                    Monitor.Pulse (miehdone);
+                }
+            }
+        }
+
+        /**
+         * See if permitted by configuration file.
+         * See OSSL_Api.CheckThreatLevelTest().
+         */
+        public string CheckFetchbinaryAllowed ()
+        {
+            string ownerPerm = m_Engine.Config.GetString ("Allow_fetchbinary",    "");
+            UUID ownerID = m_Item.OwnerID;
+            string[] ids = ownerPerm.Split (new char[] { ',' });
+            foreach (string id in ids) {
+                string curuc = id.Trim ().ToUpperInvariant ();
+
+                switch (curuc) {
+                    case "ESTATE_MANAGER": {
+                        if (m_Engine.m_Scene.RegionInfo.EstateSettings.IsEstateManagerOrOwner (ownerID) &&
+                                (m_Engine.m_Scene.RegionInfo.EstateSettings.EstateOwner != ownerID)) {
+                            return null;
+                        }
+                        break;
+                    }
+
+                    case "ESTATE_OWNER": {
+                        if (m_Engine.m_Scene.RegionInfo.EstateSettings.EstateOwner == ownerID) {
+                            return null;
+                        }
+                        break;
+                    }
+
+                    case "PARCEL_GROUP_MEMBER": {
+                        ILandObject land = m_Engine.m_Scene.LandChannel.GetLandObject (m_Part.AbsolutePosition);
+                        if (land.LandData.GroupID == m_Item.GroupID && land.LandData.GroupID != UUID.Zero) {
+                            return null;
+                        }
+                        break;
+                    }
+
+                    case "PARCEL_OWNER": {
+                        ILandObject land = m_Engine.m_Scene.LandChannel.GetLandObject (m_Part.AbsolutePosition);
+                        if (land.LandData.OwnerID == ownerID) {
+                            return null;
+                        }
+                        break;
+                    }
+
+                    case "TRUE": {
+                        return null;
+                    }
+
+                    default: {
+                        UUID uuid;
+                        if (UUID.TryParse (curuc, out uuid)) {
+                            if (uuid == ownerID) return null;
+                        }
+                        break;
+                    }
+                }
+            }
+
+            string creatorPerm = m_Engine.Config.GetString ("Creators_fetchbinary", "");
+            UUID creatorID = m_Item.CreatorID;
+            ids = creatorPerm.Split (new char[] { ',' });
+            foreach (string id in ids) {
+                string current = id.Trim ();
+                UUID uuid;
+                if (UUID.TryParse (current, out uuid)) {
+                    if (uuid != UUID.Zero) {
+                        if (creatorID == uuid) return null;
+                    }
+                }
+            }
+
+            return "fetchbinary not enabled for owner " + ownerID + " creator " + creatorID;
+        }
+    }
+}

+ 230 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRInstMain.cs

@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Threading;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.Remoting.Lifetime;
+using System.Security.Policy;
+using System.IO;
+using System.Xml;
+using System.Text;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.ScriptEngine.Interfaces;
+using OpenSim.Region.ScriptEngine.Shared;
+using OpenSim.Region.ScriptEngine.Shared.Api;
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using OpenSim.Region.Framework.Scenes;
+using log4net;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+// This class exists in the main app domain
+//
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    /**
+     * @brief Which queue it is in as far as running is concerned,
+     *        ie, m_StartQueue, m_YieldQueue, m_SleepQueue, etc.
+     * Allowed transitions:
+     *   Starts in CONSTRUCT when constructed
+     *   CONSTRUCT->ONSTARTQ              : only by thread that constructed and compiled it
+     *   IDLE->ONSTARTQ,RESETTING         : by any thread but must have m_QueueLock when transitioning
+     *   ONSTARTQ->RUNNING,RESETTING      : only by thread that removed it from m_StartQueue
+     *   ONYIELDQ->RUNNING,RESETTING      : only by thread that removed it from m_YieldQueue
+     *   ONSLEEPQ->REMDFROMSLPQ           : by any thread but must have m_SleepQueue when transitioning
+     *   REMDFROMSLPQ->ONYIELDQ,RESETTING : only by thread that removed it from m_SleepQueue
+     *   RUNNING->whatever1               : only by thread that transitioned it to RUNNING
+     *                                      whatever1 = IDLE,ONSLEEPQ,ONYIELDQ,ONSTARTQ,SUSPENDED,FINISHED
+     *   FINSHED->whatever2               : only by thread that transitioned it to FINISHED
+     *                                      whatever2 = IDLE,ONSTARTQ,DISPOSED
+     *   SUSPENDED->ONSTARTQ              : by any thread (NOT YET IMPLEMENTED, should be under some kind of lock?)
+     *   RESETTING->ONSTARTQ              : only by the thread that transitioned it to RESETTING
+     */
+    public enum XMRInstState {
+        CONSTRUCT,     // it is being constructed
+        IDLE,          // nothing happening (finished last event and m_EventQueue is empty)
+        ONSTARTQ,      // inserted on m_Engine.m_StartQueue
+        RUNNING,       // currently being executed by RunOne()
+        ONSLEEPQ,      // inserted on m_Engine.m_SleepQueue
+        REMDFROMSLPQ,  // removed from m_SleepQueue but not yet on m_YieldQueue
+        ONYIELDQ,      // inserted on m_Engine.m_YieldQueue
+        FINISHED,      // just finished handling an event
+        SUSPENDED,     // m_SuspendCount > 0
+        RESETTING,     // being reset via external call
+        DISPOSED       // has been disposed
+    }
+
+    public partial class XMRInstance : XMRInstAbstract, IDisposable
+    {
+        /******************************************************************\
+         *  This module contains the instance variables for XMRInstance.  *
+        \******************************************************************/
+
+        public const int MAXEVENTQUEUE = 64;
+
+        public static readonly DetectParams[] zeroDetectParams = new DetectParams[0];
+        public static readonly object[] zeroObjectArray = new object[0];
+
+        public static readonly ILog m_log =
+            LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+        // For a given m_Item.AssetID, do we have the compiled object code and where
+        // is it?
+        public static object m_CompileLock = new object();
+        private static Dictionary<string,ScriptObjCode> m_CompiledScriptObjCode = new Dictionary<string,ScriptObjCode>();
+
+        public  XMRInstance  m_NextInst;  // used by XMRInstQueue
+        public  XMRInstance  m_PrevInst;
+        public  XMRInstState m_IState;
+
+        public  bool m_ForceRecomp = false;
+        public  SceneObjectPart m_Part = null;
+        public  uint m_LocalID = 0;
+        public  TaskInventoryItem m_Item = null;
+        public  UUID m_ItemID;
+        public  UUID m_PartUUID;
+        private string m_CameFrom;
+        private string m_ScriptObjCodeKey;
+
+        private XMREngine m_Engine = null;
+        private string m_ScriptBasePath;
+        private string m_StateFileName;
+        public  string m_SourceCode;
+        public  bool m_PostOnRez;
+        private DetectParams[] m_DetectParams = null;
+        public  int m_StartParam = 0;
+        public  StateSource m_StateSource;
+        public  string m_DescName;
+        private bool[] m_HaveEventHandlers;
+        public  int m_StackSize;
+        public  int m_HeapSize;
+        private ArrayList m_CompilerErrors;
+        private DateTime m_LastRanAt = DateTime.MinValue;
+        private string m_RunOnePhase = "hasn't run";
+        private string m_CheckRunPhase = "hasn't checked";
+        public  int m_InstEHEvent  = 0;  // number of events dequeued (StartEventHandler called)
+        public  int m_InstEHSlice  = 0;  // number of times handler timesliced (ResumeEx called)
+        public  double m_CPUTime   = 0;  // accumulated CPU time (milliseconds)
+
+        // If code needs to have both m_QueueLock and m_RunLock,
+        // be sure to lock m_RunLock first then m_QueueLock, as
+        // that is the order used in RunOne().
+        // These locks are currently separated to allow the script
+        // to call API routines that queue events back to the script.
+        // If we just had one lock, then the queuing would deadlock.
+
+        // guards m_DetachQuantum, m_EventQueue, m_EventCounts, m_Running, m_Suspended
+        public Object m_QueueLock = new Object();
+
+        // true iff allowed to accept new events
+        public bool m_Running = true;
+
+        // queue of events that haven't been acted upon yet
+        public LinkedList<EventParams> m_EventQueue = new LinkedList<EventParams> ();
+
+        // number of events of each code currently in m_EventQueue.
+        private int[] m_EventCounts = new int[(int)ScriptEventCode.Size];
+
+        // locked whilst running on the microthread stack (or about to run on it or just ran on it)
+        private Object m_RunLock = new Object();
+
+        // script won't step while > 0.  bus-atomic updates only.
+        private int m_SuspendCount = 0;
+
+        // don't run any of script until this time
+        // or until one of these events are queued
+        public DateTime m_SleepUntil = DateTime.MinValue;
+        public int m_SleepEventMask1 = 0;
+        public int m_SleepEventMask2 = 0;
+
+        private XMRLSL_Api m_XMRLSLApi;
+
+        /*
+         * We will use this microthread to run the scripts event handlers.
+         */
+        private IScriptUThread microthread;
+
+        /*
+         * Set to perform migration.
+         */
+        public bool stackFramesRestored; // set true by CheckRun() when stack has been 
+                                         // restored and is about to suspend the microthread
+        public bool captureStackFrames;  // set true to tell CheckRun() to throw a
+                                         // StackCaptureException() causing it to capture a
+                                         // snapshot of the script's stack
+
+        /*
+         * Makes sure migration data version is same on both ends.
+         */
+        public static byte migrationVersion = 10;
+
+        // Incremented each time script gets reset.
+        public int m_ResetCount = 0;
+
+        // Scripts start suspended now. This means that event queues will
+        // accept events, but will not actually run them until the core
+        // tells it it's OK. This is needed to prevent loss of link messages
+        // in complex objects, where no event can be allowed to run until
+        // all possible link message receivers' queues are established.
+        // Guarded by m_QueueLock.
+        public bool m_Suspended = true;
+
+        // We really don't want to save state for a script that hasn't had
+        // a chance to run, because it's state will be blank. That would
+        // cause attachment state loss.
+        public bool m_HasRun = false;
+
+        // When llDie is executed within the attach(NULL_KEY) event of
+        // a script being detached to inventory, the DeleteSceneObject call
+        // it causes will delete the script instances before their state can
+        // be saved. Therefore, the instance needs to know that it's being
+        // detached to inventory, rather than to ground.
+        // Also, the attach(NULL_KEY) event needs to run with priority, and
+        // it also needs to have a limited quantum.
+        // If this is nonzero, we're detaching to inventory.
+        // Guarded by m_QueueLock.
+        private int m_DetachQuantum = 0;
+
+        // Finally, we need to wait until the quantum is done, or the script
+        // suspends itself. This should be efficient, so we use an event
+        // for it instead of spinning busy.
+        // It's born ready, but will be reset when the detach is posted.
+        // It will then be set again on suspend/completion
+        private ManualResetEvent m_DetachReady = new ManualResetEvent(true);
+    }
+}

+ 384 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRInstMisc.cs

@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Threading;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+using System.Runtime.Remoting.Lifetime;
+using System.Security.Policy;
+using System.IO;
+using System.Xml;
+using System.Text;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.ScriptEngine.Interfaces;
+using OpenSim.Region.ScriptEngine.Shared;
+using OpenSim.Region.ScriptEngine.Shared.Api;
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using OpenSim.Region.Framework.Scenes;
+using log4net;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+// This class exists in the main app domain
+//
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public partial class XMRInstance
+    {
+
+        // In case Dispose() doesn't get called, we want to be sure to clean
+        // up.  This makes sure we decrement m_CompiledScriptRefCount.
+        ~XMRInstance()
+        {
+            Dispose();
+        }
+
+        /**
+         * @brief Clean up stuff.
+         *        We specifically leave m_DescName intact for 'xmr ls' command.
+         */
+        public void Dispose()
+        {
+            /*
+             * Tell script stop executing next time it calls CheckRun().
+             */
+            suspendOnCheckRunHold = true;
+
+            /*
+             * Wait for it to stop executing and prevent it from starting again
+             * as it can't run without a microthread.
+             */
+            lock (m_RunLock)
+            {
+                if (microthread != null)
+                {
+                    m_RunOnePhase = "disposing";
+                    CheckRunLockInvariants(true);
+                    microthread.Dispose ();
+                    microthread = null;
+                }
+            }
+
+            /*
+             * Don't send us any more events.
+             */
+            if (m_Part != null)
+            {
+                xmrTrapRegionCrossing (0);
+                m_Part.RemoveScriptEvents(m_ItemID);
+                AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
+                m_Part = null;
+            }
+
+            /*
+             * Let script methods get garbage collected if no one else is using
+             * them.
+             */
+            DecObjCodeRefCount ();
+        }
+
+        private void DecObjCodeRefCount ()
+        {
+            if (m_ObjCode != null) {
+                lock (m_CompileLock) {
+                    ScriptObjCode objCode;
+
+                    if (m_CompiledScriptObjCode.TryGetValue (m_ScriptObjCodeKey, out objCode) &&
+                        (objCode == m_ObjCode) &&
+                        (-- objCode.refCount == 0)) {
+                        m_CompiledScriptObjCode.Remove (m_ScriptObjCodeKey);
+                    }
+                }
+                m_ObjCode = null;
+            }
+        }
+
+        public void Verbose (string format, params object[] args)
+        {
+            if (m_Engine.m_Verbose) m_log.DebugFormat (format, args);
+        }
+
+        // Called by 'xmr top' console command
+        // to dump this script's state to console
+        //  Sacha 
+        public void RunTestTop()
+        {
+          if (m_InstEHSlice > 0){
+            Console.WriteLine(m_DescName);
+            Console.WriteLine("    m_LocalID       = " + m_LocalID);
+            Console.WriteLine("    m_ItemID        = " + m_ItemID);
+            Console.WriteLine("    m_Item.AssetID  = " + m_Item.AssetID);
+            Console.WriteLine("    m_StartParam    = " + m_StartParam);
+            Console.WriteLine("    m_PostOnRez     = " + m_PostOnRez);
+            Console.WriteLine("    m_StateSource   = " + m_StateSource);
+            Console.WriteLine("    m_SuspendCount  = " + m_SuspendCount);
+            Console.WriteLine("    m_SleepUntil    = " + m_SleepUntil);
+            Console.WriteLine("    m_IState        = " + m_IState.ToString());
+            Console.WriteLine("    m_StateCode     = " + GetStateName(stateCode));
+            Console.WriteLine("    eventCode       = " + eventCode.ToString());
+            Console.WriteLine("    m_LastRanAt     = " + m_LastRanAt.ToString());
+            Console.WriteLine("    heapUsed/Limit  = " + xmrHeapUsed () + "/" + heapLimit);
+            Console.WriteLine("    m_InstEHEvent   = " + m_InstEHEvent.ToString());
+            Console.WriteLine("    m_InstEHSlice   = " + m_InstEHSlice.ToString());
+          }
+        }
+
+        // Called by 'xmr ls' console command
+        // to dump this script's state to console
+        public string RunTestLs(bool flagFull)
+        {
+            if (flagFull) {
+                StringBuilder sb = new StringBuilder();
+                sb.AppendLine(m_DescName);
+                sb.AppendLine("    m_LocalID            = " + m_LocalID);
+                sb.AppendLine("    m_ItemID             = " + m_ItemID  + "  (.state file)");
+                sb.AppendLine("    m_Item.AssetID       = " + m_Item.AssetID);
+                sb.AppendLine("    m_Part.WorldPosition = " + m_Part.GetWorldPosition ());
+                sb.AppendLine("    m_ScriptObjCodeKey   = " + m_ScriptObjCodeKey + "  (source text)");
+                sb.AppendLine("    m_StartParam         = " + m_StartParam);
+                sb.AppendLine("    m_PostOnRez          = " + m_PostOnRez);
+                sb.AppendLine("    m_StateSource        = " + m_StateSource);
+                sb.AppendLine("    m_SuspendCount       = " + m_SuspendCount);
+                sb.AppendLine("    m_SleepUntil         = " + m_SleepUntil);
+                sb.AppendLine("    m_SleepEvMask1       = 0x" + m_SleepEventMask1.ToString("X"));
+                sb.AppendLine("    m_SleepEvMask2       = 0x" + m_SleepEventMask2.ToString("X"));
+                sb.AppendLine("    m_IState             = " + m_IState.ToString());
+                sb.AppendLine("    m_StateCode          = " + GetStateName(stateCode));
+                sb.AppendLine("    eventCode            = " + eventCode.ToString());
+                sb.AppendLine("    m_LastRanAt          = " + m_LastRanAt.ToString());
+                sb.AppendLine("    m_RunOnePhase        = " + m_RunOnePhase);
+                sb.AppendLine("    suspOnCkRunHold      = " + suspendOnCheckRunHold);
+                sb.AppendLine("    suspOnCkRunTemp      = " + suspendOnCheckRunTemp);
+                sb.AppendLine("    m_CheckRunPhase      = " + m_CheckRunPhase);
+                sb.AppendLine("    heapUsed/Limit       = " + xmrHeapUsed () + "/" + heapLimit);
+                sb.AppendLine("    m_InstEHEvent        = " + m_InstEHEvent.ToString());
+                sb.AppendLine("    m_InstEHSlice        = " + m_InstEHSlice.ToString());
+                sb.AppendLine("    m_CPUTime            = " + m_CPUTime);
+                sb.AppendLine("    callMode             = " + callMode);
+                sb.AppendLine("    captureStackFrames   = " + captureStackFrames);
+                sb.AppendLine("    stackFramesRestored  = " + stackFramesRestored);
+                lock (m_QueueLock)
+                {
+                    sb.AppendLine("    m_Running            = " + m_Running);
+                    foreach (EventParams evt in m_EventQueue)
+                    {
+                        sb.AppendLine("        evt.EventName        = " + evt.EventName);
+                    }
+                }
+                return sb.ToString();
+            } else {
+                return String.Format("{0} {1} {2} {3} {4} {5}", 
+                        m_ItemID, 
+                        m_CPUTime.ToString("F3").PadLeft(9), 
+                        m_InstEHEvent.ToString().PadLeft(9), 
+                        m_IState.ToString().PadRight(10), 
+                        m_Part.GetWorldPosition().ToString().PadRight(32),
+                        m_DescName);
+            }
+        }
+
+        /**
+         * @brief For a given stateCode, get a mask of the low 32 event codes
+         *        that the state has handlers defined for.
+         */
+        public int GetStateEventFlags(int stateCode)
+        {
+            if ((stateCode < 0) ||
+                (stateCode >= m_ObjCode.scriptEventHandlerTable.GetLength(0)))
+            {
+                return 0;
+            }
+
+            int code = 0;
+            for (int i = 0 ; i < 32; i ++)
+            {
+                if (m_ObjCode.scriptEventHandlerTable[stateCode, i] != null)
+                {
+                    code |= 1 << i;
+                }
+            }
+
+            return code;
+        }
+
+        /**
+         * @brief Get the .state file name.
+         */
+        public static string GetStateFileName (string scriptBasePath, UUID itemID)
+        {
+            return GetScriptFileName (scriptBasePath, itemID.ToString() + ".state");
+        }
+
+        public string GetScriptFileName (string filename)
+        {
+            return GetScriptFileName (m_ScriptBasePath, filename);
+        }
+
+        public static string GetScriptFileName (string scriptBasePath, string filename)
+        {
+            /*
+             * Get old path, ie, all files lumped in a single huge directory.
+             */
+            string oldPath = Path.Combine (scriptBasePath, filename);
+
+            /*
+             * Get new path, ie, files split up based on first 2 chars of name.
+             */
+            string subdir = filename.Substring (0, 2);
+            filename = filename.Substring (2);
+            scriptBasePath = Path.Combine (scriptBasePath, subdir);
+            Directory.CreateDirectory (scriptBasePath);
+            string newPath = Path.Combine (scriptBasePath, filename);
+
+            /*
+             * If file exists only in old location, move to new location.
+             * If file exists in both locations, delete old location.
+             */
+            if (File.Exists (oldPath)) {
+                if (File.Exists (newPath)) {
+                    File.Delete (oldPath);
+                } else {
+                    File.Move (oldPath, newPath);
+                }
+            }
+
+            /*
+             * Always return new location.
+             */
+            return newPath;
+        }
+
+        /**
+         * @brief Decode state code (int) to state name (string).
+         */
+        public string GetStateName (int stateCode)
+        {
+            try {
+                return m_ObjCode.stateNames[stateCode];
+            } catch {
+                return stateCode.ToString ();
+            }
+        }
+
+        /**
+         * @brief various gets & sets.
+         */
+        public int StartParam
+        {
+            get { return m_StartParam; }
+            set { m_StartParam = value; }
+        }
+
+        public SceneObjectPart SceneObject
+        {
+            get { return m_Part; }
+        }
+
+        public DetectParams[] DetectParams
+        {
+            get { return m_DetectParams; }
+            set { m_DetectParams = value; }
+        }
+
+        public UUID ItemID
+        {
+            get { return m_ItemID; }
+        }
+
+        public UUID AssetID
+        {
+            get { return m_Item.AssetID; }
+        }
+
+        public bool Running
+        {
+            get { return m_Running; }
+            set
+            {
+                lock (m_QueueLock)
+                {
+                    m_Running = value;
+                    if (!value)
+                    {
+                        EmptyEventQueues ();
+                    }
+                }
+            }
+        }
+
+        /**
+         * @brief Empty out the event queues.
+         *        Assumes caller has the m_QueueLock locked.
+         */
+        public void EmptyEventQueues ()
+        {
+            m_EventQueue.Clear();
+            for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
+        }
+
+        /**
+         * @brief Convert an LSL vector to an Openmetaverse vector.
+         */
+        public static OpenMetaverse.Vector3 LSLVec2OMVec (LSL_Vector lslVec)
+        {
+            return new OpenMetaverse.Vector3 ((float)lslVec.x, (float)lslVec.y, (float)lslVec.z);
+        }
+
+        /**
+         * @brief Extract an integer from an element of an LSL_List.
+         */
+        public static int ListInt (object element)
+        {
+            if (element is LSL_Integer) {
+                return (int)(LSL_Integer)element;
+            }
+            return (int)element;
+        }
+
+        /**
+         * @brief Extract a string from an element of an LSL_List.
+         */
+        public static string ListStr (object element)
+        {
+            if (element is LSL_String) {
+                return (string)(LSL_String)element;
+            }
+            return (string)element;
+        }
+    }
+}

+ 186 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRInstQueue.cs

@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    /**
+     * @brief Implements a queue of XMRInstance's.
+     *        Do our own queue to avoid shitty little mallocs.
+     *
+     * Note: looping inst.m_NextInst and m_PrevInst back to itself
+     *       when inst is removed from a queue is purely for debug.
+     */
+    public class XMRInstQueue
+    {
+        private XMRInstance m_Head = null;
+        private XMRInstance m_Tail = null;
+
+        /**
+         * @brief Insert instance at head of queue (in front of all others)
+         * @param inst = instance to insert
+         */
+        public void InsertHead(XMRInstance inst)
+        {
+            if ((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) {
+                throw new Exception("already in list");
+            }
+            inst.m_PrevInst = null;
+            if ((inst.m_NextInst = m_Head) == null) {
+                m_Tail = inst;
+            } else {
+                m_Head.m_PrevInst = inst;
+            }
+            m_Head = inst;
+        }
+
+        /**
+         * @brief Insert instance at tail of queue (behind all others)
+         * @param inst = instance to insert
+         */
+        public void InsertTail(XMRInstance inst)
+        {
+            if ((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) {
+                throw new Exception("already in list");
+            }
+            inst.m_NextInst = null;
+            if ((inst.m_PrevInst = m_Tail) == null) {
+                m_Head = inst;
+            } else {
+                m_Tail.m_NextInst = inst;
+            }
+            m_Tail = inst;
+        }
+
+        /**
+         * @brief Insert instance before another element in queue
+         * @param inst  = instance to insert
+         * @param after = element that is to come after one being inserted
+         */
+        public void InsertBefore(XMRInstance inst, XMRInstance after)
+        {
+            if ((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) {
+                throw new Exception("already in list");
+            }
+            if (after == null) {
+                InsertTail(inst);
+            } else {
+                inst.m_NextInst = after;
+                inst.m_PrevInst = after.m_PrevInst;
+                if (inst.m_PrevInst == null) {
+                    m_Head = inst;
+                } else {
+                    inst.m_PrevInst.m_NextInst = inst;
+                }
+                after.m_PrevInst = inst;
+            }
+        }
+
+        /**
+         * @brief Peek to see if anything in queue
+         * @returns first XMRInstance in queue but doesn't remove it
+         *          null if queue is empty
+         */
+        public XMRInstance PeekHead()
+        {
+            return m_Head;
+        }
+
+        /**
+         * @brief Remove first element from queue, if any
+         * @returns null if queue is empty
+         *          else returns first element in queue and removes it
+         */
+        public XMRInstance RemoveHead()
+        {
+            XMRInstance inst = m_Head;
+            if (inst != null) {
+                if ((m_Head = inst.m_NextInst) == null) {
+                    m_Tail = null;
+                } else {
+                    m_Head.m_PrevInst = null;
+                }
+                inst.m_NextInst = inst;
+                inst.m_PrevInst = inst;
+            }
+            return inst;
+        }
+
+        /**
+         * @brief Remove last element from queue, if any
+         * @returns null if queue is empty
+         *          else returns last element in queue and removes it
+         */
+        public XMRInstance RemoveTail()
+        {
+            XMRInstance inst = m_Tail;
+            if (inst != null) {
+                if ((m_Tail = inst.m_PrevInst) == null) {
+                    m_Head = null;
+                } else {
+                    m_Tail.m_NextInst = null;
+                }
+                inst.m_NextInst = inst;
+                inst.m_PrevInst = inst;
+            }
+            return inst;
+        }
+
+        /**
+         * @brief Remove arbitrary element from queue, if any
+         * @param inst = element to remove (assumed to be in the queue)
+         * @returns with element removed
+         */
+        public void Remove(XMRInstance inst)
+        {
+            XMRInstance next = inst.m_NextInst;
+            XMRInstance prev = inst.m_PrevInst;
+            if ((prev == inst) || (next == inst)) {
+                throw new Exception("not in a list");
+            }
+            if (next == null) {
+                if (m_Tail != inst) {
+                    throw new Exception("not in this list");
+                }
+                m_Tail = prev;
+            } else {
+                next.m_PrevInst = prev;
+            }
+            if (prev == null) {
+                if (m_Head != inst) {
+                    throw new Exception("not in this list");
+                }
+                m_Head = next;
+            } else {
+                prev.m_NextInst = next;
+            }
+            inst.m_NextInst = inst;
+            inst.m_PrevInst = inst;
+        }
+    }
+}

+ 1051 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs

@@ -0,0 +1,1051 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Threading;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+using System.Runtime.Remoting.Lifetime;
+using System.Security.Policy;
+using System.IO;
+using System.Xml;
+using System.Text;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.ScriptEngine.Interfaces;
+using OpenSim.Region.ScriptEngine.Shared;
+using OpenSim.Region.ScriptEngine.Shared.Api;
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using OpenSim.Region.ScriptEngine.XMREngine;
+using OpenSim.Region.Framework.Scenes;
+using log4net;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public partial class XMRInstance
+    {
+        /************************************************************************************\
+         * This module contains these externally useful methods:                            *
+         *   PostEvent() - queues an event to script and wakes script thread to process it  *
+         *   RunOne() - runs script for a time slice or until it volunteers to give up cpu  *
+         *   CallSEH() - runs in the microthread to call the event handler                  *
+        \************************************************************************************/
+
+        /**
+         * @brief This can be called in any thread (including the script thread itself)
+         *        to queue event to script for processing.
+         */
+        public void PostEvent(EventParams evt)
+        {
+            ScriptEventCode evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), 
+                                                                 evt.EventName);
+
+            /*
+             * Put event on end of event queue.
+             */
+            bool startIt = false;
+            bool wakeIt  = false;
+            lock (m_QueueLock)
+            {
+                bool construct = (m_IState == XMRInstState.CONSTRUCT);
+
+                /*
+                 * Ignore event if we don't even have such an handler in any state.
+                 * We can't be state-specific here because state might be different
+                 * by the time this event is dequeued and delivered to the script.
+                 */
+                if (!construct &&                      // make sure m_HaveEventHandlers is filled in 
+                    ((uint)evc < (uint)m_HaveEventHandlers.Length) &&
+                    !m_HaveEventHandlers[(int)evc]) {  // don't bother if we don't have such a handler in any state
+                    return;
+                }
+
+                /*
+                 * Not running means we ignore any incoming events.
+                 * But queue if still constructing because m_Running is not yet valid.
+                 */
+                if (!m_Running && !construct) {
+                    return;
+                }
+
+                /*
+                 * Only so many of each event type allowed to queue.
+                 */
+                if ((uint)evc < (uint)m_EventCounts.Length) {
+                    int maxAllowed = MAXEVENTQUEUE;
+                    if (evc == ScriptEventCode.timer) maxAllowed = 1;
+                    if (m_EventCounts[(int)evc] >= maxAllowed)
+                    {
+                        return;
+                    }
+                    m_EventCounts[(int)evc] ++;
+                }
+
+                /*
+                 * Put event on end of instance's event queue.
+                 */
+                LinkedListNode<EventParams> lln = new LinkedListNode<EventParams>(evt);
+                switch (evc) {
+
+                    /*
+                     * These need to go first.  The only time we manually
+                     * queue them is for the default state_entry() and we
+                     * need to make sure they go before any attach() events
+                     * so the heapLimit value gets properly initialized.
+                     */
+                    case ScriptEventCode.state_entry: {
+                        m_EventQueue.AddFirst(lln);
+                        break;
+                    }
+
+                    /*
+                     * The attach event sneaks to the front of the queue.
+                     * This is needed for quantum limiting to work because
+                     * we want the attach(NULL_KEY) event to come in front
+                     * of all others so the m_DetachQuantum won't run out
+                     * before attach(NULL_KEY) is executed.
+                     */
+                    case ScriptEventCode.attach: {
+                        if (evt.Params[0].ToString() == UUID.Zero.ToString())
+                        {
+                            LinkedListNode<EventParams> lln2 = null;
+                            for (lln2 = m_EventQueue.First; lln2 != null; lln2 = lln2.Next) {
+                                EventParams evt2 = lln2.Value;
+                                ScriptEventCode evc2 = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), 
+                                                                         evt2.EventName);
+                                if ((evc2 != ScriptEventCode.state_entry) &&
+                                    (evc2 != ScriptEventCode.attach)) break;
+                            }
+                            if (lln2 == null) {
+                                m_EventQueue.AddLast(lln);
+                            } else {
+                                m_EventQueue.AddBefore(lln2, lln);
+                            }
+                            /* If we're detaching, limit the qantum. This will also
+                             * cause the script to self-suspend after running this
+                             * event
+                             */
+
+                            m_DetachReady.Reset();
+                            m_DetachQuantum = 100;
+                        }
+                        else
+                        {
+                            m_EventQueue.AddLast(lln);
+                        }
+                        break;
+                    }
+
+                    /*
+                     * All others just go on end in the order queued.
+                     */
+                    default: {
+                        m_EventQueue.AddLast(lln);
+                        break;
+                    }
+                }
+
+                /*
+                 * If instance is idle (ie, not running or waiting to run),
+                 * flag it to be on m_StartQueue as we are about to do so.
+                 * Flag it now before unlocking so another thread won't try
+                 * to do the same thing right now.
+                 * Dont' flag it if it's still suspended!
+                 */
+                if ((m_IState == XMRInstState.IDLE) && !m_Suspended) {
+                    m_IState = XMRInstState.ONSTARTQ;
+                    startIt = true;
+                }
+
+                /*
+                 * If instance is sleeping (ie, possibly in xmrEventDequeue),
+                 * wake it up if event is in the mask.
+                 */
+                if ((m_SleepUntil > DateTime.UtcNow) && !m_Suspended) {
+                    int evc1 = (int)evc;
+                    int evc2 = evc1 - 32;
+                    if ((((uint)evc1 < (uint)32) && (((m_SleepEventMask1 >> evc1) & 1) != 0)) ||
+                        (((uint)evc2 < (uint)32) && (((m_SleepEventMask2 >> evc2) & 1) != 0))) {
+                        wakeIt = true;
+                    }
+                }
+            }
+
+            /*
+             * If transitioned from IDLE->ONSTARTQ, actually go insert it
+             * on m_StartQueue and give the RunScriptThread() a wake-up.
+             */
+            if (startIt) {
+                m_Engine.QueueToStart(this);
+            }
+
+            /*
+             * Likewise, if the event mask triggered a wake, wake it up.
+             */
+            if (wakeIt) {
+                m_SleepUntil = DateTime.MinValue;
+                m_Engine.WakeFromSleep(this);
+            }
+        }
+
+        /*
+         * This is called in the script thread to step script until it calls
+         * CheckRun().  It returns what the instance's next state should be,
+         * ONSLEEPQ, ONYIELDQ, SUSPENDED or FINISHED.
+         */
+        public XMRInstState RunOne()
+        {
+            DateTime now = DateTime.UtcNow;
+
+            /*
+             * If script has called llSleep(), don't do any more until time is
+             * up.
+             */
+            m_RunOnePhase = "check m_SleepUntil";
+            if (m_SleepUntil > now)
+            {
+                m_RunOnePhase = "return is sleeping";
+                return XMRInstState.ONSLEEPQ;
+            }
+
+            /*
+             * Also, someone may have called Suspend().
+             */
+            m_RunOnePhase = "check m_SuspendCount";
+            if (m_SuspendCount > 0) {
+                m_RunOnePhase = "return is suspended";
+                return XMRInstState.SUSPENDED;
+            }
+
+            /*
+             * Make sure we aren't being migrated in or out and prevent that 
+             * whilst we are in here.  If migration has it locked, don't call
+             * back right away, delay a bit so we don't get in infinite loop.
+             */
+            m_RunOnePhase = "lock m_RunLock";
+            if (!Monitor.TryEnter (m_RunLock)) {
+                m_SleepUntil = now.AddMilliseconds(3);
+                m_RunOnePhase = "return was locked";
+                return XMRInstState.ONSLEEPQ;
+            }
+            try
+            {
+                m_RunOnePhase = "check entry invariants";
+                CheckRunLockInvariants(true);
+                Exception e = null;
+
+                /*
+                 * Maybe we have been disposed.
+                 */
+                m_RunOnePhase = "check disposed";
+                if (microthread == null)
+                {
+                    m_RunOnePhase = "return disposed";
+                    return XMRInstState.DISPOSED;
+                }
+
+                /*
+                 * Do some more of the last event if it didn't finish.
+                 */
+                else if (this.eventCode != ScriptEventCode.None)
+                {
+                    lock (m_QueueLock)
+                    {
+                        if (m_DetachQuantum > 0 && --m_DetachQuantum == 0)
+                        {
+                            m_Suspended = true;
+                            m_DetachReady.Set();
+                            m_RunOnePhase = "detach quantum went zero";
+                            CheckRunLockInvariants(true);
+                            return XMRInstState.FINISHED;
+                        }
+                    }
+
+                    m_RunOnePhase = "resume old event handler";
+                    m_LastRanAt   = now;
+                    m_InstEHSlice ++;
+                    callMode = CallMode_NORMAL;
+                    e = microthread.ResumeEx ();
+                }
+
+                /*
+                 * Otherwise, maybe we can dequeue a new event and start 
+                 * processing it.
+                 */
+                else
+                {
+                    m_RunOnePhase = "lock event queue";
+                    EventParams evt = null;
+                    ScriptEventCode evc = ScriptEventCode.None;
+
+                    lock (m_QueueLock)
+                    {
+
+                        /* We can't get here unless the script has been resumed
+                         * after creation, then suspended again, and then had
+                         * an event posted to it. We just pretend there is no
+                         * event int he queue and let the normal mechanics
+                         * carry out the suspension. A Resume will handle the
+                         * restarting gracefully. This is taking the easy way
+                         * out and may be improved in the future.
+                         */
+
+                        if (m_Suspended)
+                        {
+                            m_RunOnePhase = "m_Suspended is set";
+                            CheckRunLockInvariants(true);
+                            return XMRInstState.FINISHED;
+                        }
+
+                        m_RunOnePhase = "dequeue event";
+                        if (m_EventQueue.First != null)
+                        {
+                            evt = m_EventQueue.First.Value;
+                            if (m_DetachQuantum > 0)
+                            {
+                                evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), 
+                                                               evt.EventName);
+                                if (evc != ScriptEventCode.attach)
+                                {
+                                    /*
+                                     * This is the case where the attach event
+                                     * has completed and another event is queued
+                                     * Stop it from running and suspend
+                                     */
+                                    m_Suspended = true;
+                                    m_DetachReady.Set();
+                                    m_DetachQuantum = 0;
+                                    m_RunOnePhase = "nothing to do #3";
+                                    CheckRunLockInvariants(true);
+                                    return XMRInstState.FINISHED;
+                                }
+                            }
+                            m_EventQueue.RemoveFirst();
+                            evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), 
+                                                               evt.EventName);
+                            if ((int)evc >= 0) m_EventCounts[(int)evc] --;
+                        }
+
+                        /*
+                         * If there is no event to dequeue, don't run this script
+                         * until another event gets queued.
+                         */
+                        if (evt == null)
+                        {
+                            if (m_DetachQuantum > 0)
+                            {
+                                /*
+                                 * This will happen if the attach event has run
+                                 * and exited with time slice left.
+                                 */
+                                m_Suspended = true;
+                                m_DetachReady.Set();
+                                m_DetachQuantum = 0;
+                            }
+                            m_RunOnePhase = "nothing to do #4";
+                            CheckRunLockInvariants(true);
+                            return XMRInstState.FINISHED;
+                        }
+                    }
+
+                    /*
+                     * Dequeued an event, so start it going until it either 
+                     * finishes or it calls CheckRun().
+                     */
+                    m_RunOnePhase  = "start event handler";
+                    m_DetectParams = evt.DetectParams;
+                    m_LastRanAt    = now;
+                    m_InstEHEvent ++;
+                    e = StartEventHandler (evc, evt.Params);
+                }
+                m_RunOnePhase = "done running";
+                m_CPUTime    += DateTime.UtcNow.Subtract(now).TotalMilliseconds;
+
+                /*
+                 * Maybe it puqued.
+                 */
+                if (e != null)
+                {
+                    m_RunOnePhase = "handling exception " + e.Message;
+                    HandleScriptException(e);
+                    m_RunOnePhase = "return had exception " + e.Message;
+                    CheckRunLockInvariants(true);
+                    return XMRInstState.FINISHED;
+                }
+
+                /*
+                 * If event handler completed, get rid of detect params.
+                 */
+                if (this.eventCode == ScriptEventCode.None)
+                {
+                    m_DetectParams = null;
+                }
+            }
+            finally
+            {
+                m_RunOnePhase += "; checking exit invariants and unlocking";
+                CheckRunLockInvariants(false);
+                Monitor.Exit(m_RunLock);
+            }
+
+            /*
+             * Cycle script through the yield queue and call it back asap.
+             */
+            m_RunOnePhase = "last return";
+            return XMRInstState.ONYIELDQ;
+        }
+
+        /**
+         * @brief Immediately after taking m_RunLock or just before releasing it, check invariants.
+         */
+        private ScriptEventCode lastEventCode = ScriptEventCode.None;
+        private int             lastActive    = 0;
+        private string          lastRunPhase  = "";
+
+        public void CheckRunLockInvariants(bool throwIt)
+        {
+            /*
+             * If not executing any event handler, active should be 0 indicating the microthread stack is not in use.
+             * If executing an event handler, active should be -1 indicating stack is in use but suspended.
+             */
+            IScriptUThread uth = microthread;
+            if (uth != null) {
+                int active = uth.Active ();
+                ScriptEventCode ec = this.eventCode;
+                if (((ec == ScriptEventCode.None) && (active != 0)) ||
+                    ((ec != ScriptEventCode.None) && (active >= 0))) {
+                    Console.WriteLine("CheckRunLockInvariants: script=" + m_DescName);
+                    Console.WriteLine("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString());
+                    Console.WriteLine("CheckRunLockInvariants: m_RunOnePhase=" + m_RunOnePhase);
+                    Console.WriteLine("CheckRunLockInvariants: lastec=" + lastEventCode + ", lastAct=" + lastActive + ", lastPhase=" + lastRunPhase);
+                    if (throwIt) {
+                        throw new Exception("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString());
+                    }
+                }
+                lastEventCode = ec;
+                lastActive    = active;
+                lastRunPhase  = m_RunOnePhase;
+            }
+        }
+
+        /*
+         * Start event handler.
+         *
+         * Input:
+         *  eventCode = code of event to be processed
+         *  ehArgs    = arguments for the event handler
+         *
+         * Caution:
+         *  It is up to the caller to make sure ehArgs[] is correct for
+         *  the particular event handler being called.  The first thing
+         *  a script event handler method does is to unmarshall the args
+         *  from ehArgs[] and will throw an array bounds or cast exception 
+         *  if it can't.
+         */
+        private Exception StartEventHandler (ScriptEventCode eventCode, object[] ehArgs)
+        {
+            /*
+             * We use this.eventCode == ScriptEventCode.None to indicate we are idle.
+             * So trying to execute ScriptEventCode.None might make a mess.
+             */
+            if (eventCode == ScriptEventCode.None) {
+                return new Exception ("Can't process ScriptEventCode.None");
+            }
+
+            /*
+             * Silly to even try if there is no handler defined for this event.
+             */
+            if (((int)eventCode >= 0) && (m_ObjCode.scriptEventHandlerTable[this.stateCode,(int)eventCode] == null)) {
+                return null;
+            }
+
+            /*
+             * The microthread shouldn't be processing any event code.
+             * These are assert checks so we throw them directly as exceptions.
+             */
+            if (this.eventCode != ScriptEventCode.None) {
+                throw new Exception ("still processing event " + this.eventCode.ToString ());
+            }
+            int active = microthread.Active ();
+            if (active != 0) {
+                throw new Exception ("microthread is active " + active.ToString ());
+            }
+
+            /*
+             * Save eventCode so we know what event handler to run in the microthread.
+             * And it also marks us busy so we can't be started again and this event lost.
+             */
+            this.eventCode = eventCode;
+            this.ehArgs    = ehArgs;
+
+            /*
+             * This calls ScriptUThread.Main() directly, and returns when Main() [indirectly]
+             * calls Suspend() or when Main() returns, whichever occurs first.
+             * Setting stackFrames = null means run the event handler from the beginning
+             * without doing any stack frame restores first.
+             */
+            this.stackFrames = null;
+            Exception e;
+            e = microthread.StartEx ();
+            return e;
+        }
+
+
+        /**
+         * @brief There was an exception whilst starting/running a script event handler.
+         *        Maybe we handle it directly or just print an error message.
+         */
+        private void HandleScriptException(Exception e)
+        {
+            /*
+             * The script threw some kind of exception that was not caught at
+             * script level, so the script is no longer running an event handler.
+             */
+            eventCode = ScriptEventCode.None;
+
+            if (e is ScriptDeleteException)
+            {
+                /*
+                 * Script did something like llRemoveInventory(llGetScriptName());
+                 * ... to delete itself from the object.
+                 */
+                m_SleepUntil = DateTime.MaxValue;
+                Verbose ("[XMREngine]: script self-delete {0}", m_ItemID);
+                m_Part.Inventory.RemoveInventoryItem(m_ItemID);
+            }
+            else if (e is ScriptDieException)
+            {
+                /*
+                 * Script did an llDie()
+                 */
+                m_RunOnePhase = "dying...";
+                m_SleepUntil = DateTime.MaxValue;
+                m_Engine.World.DeleteSceneObject(m_Part.ParentGroup, false);
+            }
+            else if (e is ScriptResetException)
+            {
+                /*
+                 * Script did an llResetScript().
+                 */
+                m_RunOnePhase = "resetting...";
+                ResetLocked("HandleScriptResetException");
+            }
+            else
+            {
+                /*
+                 * Some general script error.
+                 */
+                SendErrorMessage(e);
+            }
+            return;
+        }
+
+        /**
+         * @brief There was an exception running script event handler.
+         *        Display error message and disable script (in a way
+         *        that the script can be reset to be restarted).
+         */
+        private void SendErrorMessage(Exception e)
+        {
+            StringBuilder msg = new StringBuilder();
+
+            msg.Append ("[XMREngine]: Exception while running ");
+            msg.Append (m_ItemID);
+            msg.Append ('\n');
+
+            /*
+             * Add exception message.
+             */
+            string des = e.Message;
+            des = (des == null) ? "" : (": " + des);
+            msg.Append (e.GetType ().Name + des + "\n");
+
+            /*
+             * Tell script owner what to do.
+             */
+            msg.Append ("Prim: <");
+            msg.Append (m_Part.Name);
+            msg.Append (">, Script: <");
+            msg.Append (m_Item.Name);
+            msg.Append (">, Location: ");
+            msg.Append (m_Engine.World.RegionInfo.RegionName);
+            msg.Append (" <");
+            Vector3 pos = m_Part.AbsolutePosition;
+            msg.Append ((int) Math.Floor (pos.X));
+            msg.Append (',');
+            msg.Append ((int) Math.Floor (pos.Y));
+            msg.Append (',');
+            msg.Append ((int) Math.Floor (pos.Z));
+            msg.Append (">\nScript must be Reset to re-enable.\n");
+
+            /*
+             * Display full exception message in log.
+             */
+            m_log.Info (msg.ToString() + XMRExceptionStackString (e), e);
+
+            /*
+             * Give script owner the stack dump.
+             */
+            msg.Append (XMRExceptionStackString (e));
+
+            /*
+             * Send error message to owner.
+             * Suppress internal code stack trace lines.
+             */
+            string msgst = msg.ToString();
+            if (!msgst.EndsWith ("\n")) msgst += '\n';
+            int j = 0;
+            StringBuilder imstr = new StringBuilder ();
+            for (int i = 0; (i = msgst.IndexOf ('\n', i)) >= 0; j = ++ i) {
+                string line = msgst.Substring (j, i - j);
+                if (line.StartsWith ("at ")) {
+                    if (line.StartsWith ("at (wrapper")) continue;  // at (wrapper ...
+                    int k = line.LastIndexOf (".cs:");  // ... .cs:linenumber
+                    if (Int32.TryParse (line.Substring (k + 4), out k)) continue;
+                }
+                this.llOwnerSay (line);
+                imstr.Append (line);
+                imstr.Append ('\n');
+            }
+
+            /*
+             * Send as instant message in case user not online.
+             * Code modelled from llInstantMessage().
+             */
+            IMessageTransferModule transferModule = m_Engine.World.RequestModuleInterface<IMessageTransferModule>();
+            if (transferModule != null) {
+                UUID friendTransactionID = UUID.Random();
+                GridInstantMessage gim = new GridInstantMessage();
+                gim.fromAgentID = new Guid (m_Part.UUID.ToString());
+                gim.toAgentID = new Guid (m_Part.OwnerID.ToString ());
+                gim.imSessionID = new Guid(friendTransactionID.ToString());
+                gim.timestamp = (uint)Util.UnixTimeSinceEpoch();
+                gim.message = imstr.ToString ();
+                gim.dialog = (byte)19; // messgage from script
+                gim.fromGroup = false;
+                gim.offline = (byte)0;
+                gim.ParentEstateID = 0;
+                gim.Position = pos;
+                gim.RegionID = m_Engine.World.RegionInfo.RegionID.Guid;
+                gim.binaryBucket = Util.StringToBytes256(
+                    "{0}/{1}/{2}/{3}",
+                    m_Engine.World.RegionInfo.RegionName,
+                    (int)Math.Floor(pos.X),
+                    (int)Math.Floor(pos.Y),
+                    (int)Math.Floor(pos.Z));
+                transferModule.SendInstantMessage(gim, delegate(bool success) {});
+            }
+
+            /*
+             * Say script is sleeping for a very long time.
+             * Reset() is able to cancel this sleeping.
+             */
+            m_SleepUntil = DateTime.MaxValue;
+        }
+
+        /**
+         * @brief The user clicked the Reset Script button.
+         *        We want to reset the script to a never-has-ever-run-before state.
+         */
+        public void Reset()
+        {
+        checkstate:
+            XMRInstState iState = m_IState;
+            switch (iState) {
+
+                /*
+                 * If it's really being constructed now, that's about as reset as we get.
+                 */
+                case XMRInstState.CONSTRUCT: {
+                    return;
+                }
+
+                /*
+                 * If it's idle, that means it is ready to receive a new event.
+                 * So we lock the event queue to prevent another thread from taking
+                 * it out of idle, verify that it is still in idle then transition
+                 * it to resetting so no other thread will touch it.
+                 */
+                case XMRInstState.IDLE: {
+                    lock (m_QueueLock) {
+                        if (m_IState == XMRInstState.IDLE) {
+                            m_IState = XMRInstState.RESETTING;
+                            break;
+                        }
+                    }
+                    goto checkstate;
+                }
+
+                /*
+                 * If it's on the start queue, that means it is about to dequeue an
+                 * event and start processing it.  So we lock the start queue so it
+                 * can't be started and transition it to resetting so no other thread
+                 * will touch it.
+                 */
+                case XMRInstState.ONSTARTQ: {
+                    lock (m_Engine.m_StartQueue) {
+                        if (m_IState == XMRInstState.ONSTARTQ) {
+                            m_Engine.m_StartQueue.Remove(this);
+                            m_IState = XMRInstState.RESETTING;
+                            break;
+                        }
+                    }
+                    goto checkstate;
+                }
+
+                /*
+                 * If it's running, tell CheckRun() to suspend the thread then go back
+                 * to see what it got transitioned to.
+                 */
+                case XMRInstState.RUNNING: {
+                    suspendOnCheckRunHold = true;
+                    lock (m_QueueLock) { }
+                    goto checkstate;
+                }
+
+                /*
+                 * If it's sleeping, remove it from sleep queue and transition it to
+                 * resetting so no other thread will touch it.
+                 */
+                case XMRInstState.ONSLEEPQ: {
+                    lock (m_Engine.m_SleepQueue) {
+                        if (m_IState == XMRInstState.ONSLEEPQ) {
+                            m_Engine.m_SleepQueue.Remove(this);
+                            m_IState = XMRInstState.RESETTING;
+                            break;
+                        }
+                    }
+                    goto checkstate;
+                }
+
+                /*
+                 * It was just removed from the sleep queue and is about to be put
+                 * on the yield queue (ie, is being woken up).
+                 * Let that thread complete transition and try again.
+                 */
+                case XMRInstState.REMDFROMSLPQ: {
+                    Sleep (10);
+                    goto checkstate;
+                }
+
+                /*
+                 * If it's yielding, remove it from yield queue and transition it to
+                 * resetting so no other thread will touch it.
+                 */
+                case XMRInstState.ONYIELDQ: {
+                    lock (m_Engine.m_YieldQueue) {
+                        if (m_IState == XMRInstState.ONYIELDQ) {
+                            m_Engine.m_YieldQueue.Remove(this);
+                            m_IState = XMRInstState.RESETTING;
+                            break;
+                        }
+                    }
+                    goto checkstate;
+                }
+
+                /*
+                 * If it just finished running something, let that thread transition it
+                 * to its next state then check again.
+                 */
+                case XMRInstState.FINISHED: {
+                    Sleep (10);
+                    goto checkstate;
+                }
+
+                /*
+                 * If it's disposed, that's about as reset as it gets.
+                 */
+                case XMRInstState.DISPOSED: {
+                    return;
+                }
+
+                /*
+                 * Some other thread is already resetting it, let it finish.
+                 */
+                case XMRInstState.RESETTING: {
+                    return;
+                }
+
+                default: throw new Exception("bad state");
+            }
+
+            /*
+             * This thread transitioned the instance to RESETTING so reset it.
+             */
+            lock (m_RunLock) {
+                CheckRunLockInvariants(true);
+
+                /*
+                 * No other thread should have transitioned it from RESETTING.
+                 */
+                if (m_IState != XMRInstState.RESETTING) throw new Exception("bad state");
+
+                /*
+                 * If the microthread is active, that means it has call frame
+                 * context that we don't want.  Throw it out and get a fresh one.
+                 */
+                if (microthread.Active () < 0) {
+                    microthread.Dispose ();
+                    microthread = (IScriptUThread)m_Engine.uThreadCtor.Invoke (new object[] { this });
+                }
+
+                /*
+                 * Mark it idle now so it can get queued to process new stuff.
+                 */
+                m_IState = XMRInstState.IDLE;
+
+                /*
+                 * Reset everything and queue up default's start_entry() event.
+                 */
+                ClearQueue();
+                ResetLocked("external Reset");
+
+                CheckRunLockInvariants(true);
+            }
+        }
+
+        private void ClearQueueExceptLinkMessages()
+        {
+            lock (m_QueueLock) {
+                EventParams[] linkMessages = new EventParams[m_EventQueue.Count];
+                int n = 0;
+                foreach (EventParams evt2 in m_EventQueue) {
+                    if (evt2.EventName == "link_message") {
+                        linkMessages[n++] = evt2;
+                    }
+                }
+
+                m_EventQueue.Clear();
+                for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
+
+                for (int i = 0; i < n; i ++) {
+                    m_EventQueue.AddLast(linkMessages[i]);
+                }
+
+                m_EventCounts[(int)ScriptEventCode.link_message] = n;
+            }
+        }
+
+        private void ClearQueue()
+        {
+            lock (m_QueueLock)
+            {
+                m_EventQueue.Clear();               // no events queued
+                for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
+            }
+        }
+
+        /**
+         * @brief The script called llResetScript() while it was running and
+         *        has suspended.  We want to reset the script to a never-has-
+         *        ever-run-before state.
+         *
+         *        Caller must have m_RunLock locked so we know script isn't
+         *        running.
+         */
+        private void ResetLocked(string from)
+        {
+            m_RunOnePhase = "ResetLocked: releasing controls";
+            ReleaseControls();
+
+            m_RunOnePhase = "ResetLocked: removing script";
+            m_Part.Inventory.GetInventoryItem(m_ItemID).PermsMask = 0;
+            m_Part.Inventory.GetInventoryItem(m_ItemID).PermsGranter = UUID.Zero;
+            IUrlModule urlModule = m_Engine.World.RequestModuleInterface<IUrlModule>();
+            if (urlModule != null)
+                urlModule.ScriptRemoved(m_ItemID);
+
+            AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
+
+            m_RunOnePhase = "ResetLocked: clearing current event";
+            this.eventCode = ScriptEventCode.None;  // not processing an event
+            m_DetectParams = null;                  // not processing an event
+            m_SleepUntil   = DateTime.MinValue;     // not doing llSleep()
+            m_ResetCount ++;                        // has been reset once more
+
+            /*
+             * Tell next call to 'default state_entry()' to reset all global
+             * vars to their initial values.
+             */
+            doGblInit = true;
+
+            /*
+             * Set script to 'default' state and queue call to its 
+             * 'state_entry()' event handler.
+             */
+            m_RunOnePhase = "ResetLocked: posting default:state_entry() event";
+            stateCode = 0;
+            m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(0));
+            PostEvent(new EventParams("state_entry", 
+                                      zeroObjectArray, 
+                                      zeroDetectParams));
+
+            /*
+             * Tell CheckRun() to let script run.
+             */
+            suspendOnCheckRunHold = false;
+            suspendOnCheckRunTemp = false;
+            m_RunOnePhase = "ResetLocked: reset complete";
+        }
+
+        private void ReleaseControls()
+        {
+            if (m_Part != null)
+            {
+                bool found;
+                int permsMask;
+                UUID permsGranter;
+
+                try {
+                    permsGranter = m_Part.TaskInventory[m_ItemID].PermsGranter;
+                    permsMask = m_Part.TaskInventory[m_ItemID].PermsMask;
+                    found = true;
+                } catch {
+                    permsGranter = UUID.Zero;
+                    permsMask = 0;
+                    found = false;
+                }
+
+                if (found && ((permsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)) {
+                    ScenePresence presence = m_Engine.World.GetScenePresence(permsGranter);
+                    if (presence != null) {
+                        presence.UnRegisterControlEventsToScript(m_LocalID, m_ItemID);
+                    }
+                }
+            }
+        }
+
+        /**
+         * @brief The script code should call this routine whenever it is
+         *        convenient to perform a migation or switch microthreads.
+         */
+        public override void CheckRunWork ()
+        {
+            m_CheckRunPhase = "entered";
+
+            /*
+             * Stay stuck in this loop as long as something wants us suspended.
+             */
+            while (suspendOnCheckRunHold || suspendOnCheckRunTemp) {
+                m_CheckRunPhase = "top of while";
+
+                /*
+                 * See if MigrateOutEventHandler() has been called.
+                 * If so, dump our stack to stackFrames and unwind.
+                 */
+                if (this.captureStackFrames) {
+
+                    /*
+                     * Puque our stack to the output stream.
+                     * But otherwise, our state remains intact.
+                     */
+                    m_CheckRunPhase = "saving";
+                    this.callMode = CallMode_SAVE;
+                    this.stackFrames = null;
+                    throw new StackCaptureException ();
+                }
+
+                /*
+                 * We get here when the script state has been read in by MigrateInEventHandler().
+                 * Since the stack is completely restored at this point, any subsequent calls
+                 * within the functions should do their normal processing instead of trying to 
+                 * restore their state.
+                 */
+                if (this.callMode == CallMode_RESTORE) {
+                    stackFramesRestored = true;
+                    this.callMode = CallMode_NORMAL;
+                }
+
+                /*
+                 * Now we are ready to suspend the microthread.
+                 * This is like a longjmp() to the most recent StartEx() or ResumeEx()
+                 * with a simultaneous setjmp() so ResumeEx() can longjmp() back here.
+                 */
+                m_CheckRunPhase = "suspending";
+                suspendOnCheckRunTemp = false;
+                microthread.Hiber ();
+                m_CheckRunPhase = "resumed";
+            }
+
+            m_CheckRunPhase = "returning";
+
+            /*
+             * Upon return from CheckRun() it should always be the case that the script is
+             * going to process calls normally, neither saving nor restoring stack frame state.
+             */
+            if (callMode != CallMode_NORMAL) throw new Exception ("bad callMode " + callMode);
+        }
+
+        /**
+         * @brief Allow script to dequeue events.
+         */
+        public void ResumeIt()
+        {
+            lock (m_QueueLock)
+            {
+                m_Suspended = false;
+                if ((m_EventQueue != null) &&
+                    (m_EventQueue.First != null) &&
+                    (m_IState == XMRInstState.IDLE)) {
+                    m_IState = XMRInstState.ONSTARTQ;
+                    m_Engine.QueueToStart(this);
+                }
+                m_HasRun = true;
+            }
+        }
+
+        /**
+         * @brief Block script from dequeuing events.
+         */
+        public void SuspendIt()
+        {
+            lock (m_QueueLock)
+            {
+                m_Suspended = true;
+            }
+        }
+    }
+
+    /**
+     * @brief Thrown by CheckRun() to unwind the script stack, capturing frames to
+     *        instance.stackFrames as it unwinds.  We don't want scripts to be able
+     *        to intercept this exception as it would block the stack capture
+     *        functionality.
+     */
+    public class StackCaptureException : Exception, IXMRUncatchable { }
+}

+ 76 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRInstSorpra.cs

@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.ScriptEngine.Shared;
+using System;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public partial class XMRInstance
+    {
+        /**
+         * @brief If RegionCrossing trapping is enabled, any attempt to move the object
+         *        outside its current region will cause the event to fire and the object
+         *        will remain in its current region.
+         */
+        public override void xmrTrapRegionCrossing (int en)
+        { }
+
+        /**
+         * @brief Move object to new position and rotation asynchronously.
+         *        Can move object across region boundary.
+         * @param pos     = new position within current region (same coords as llGetPos())
+         * @param rot     = new rotation within current region (same coords as llGetRot())
+         * @param options = not used
+         * @param evcode  = not used
+         * @param evargs  = arguments to pass to event handler
+         * @returns false: completed synchronously, no event will be queued
+         */
+        public const double Sorpra_MIN_CROSS  = 1.0 / 512.0;  // ie, ~2mm
+        public const int    Sorpra_TIMEOUT_MS = 30000;        // ie, 30sec
+        public override bool xmrSetObjRegPosRotAsync (LSL_Vector pos, LSL_Rotation rot, int options, int evcode, LSL_List evargs)
+        {
+            // do the move
+            SceneObjectGroup sog = m_Part.ParentGroup;
+            sog.UpdateGroupRotationPR (pos, rot);
+
+            // it is always synchronous
+            return false;
+        }
+    }
+}

+ 5476 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRObjectTokens.cs

@@ -0,0 +1,5476 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+/**
+ * Contains classes that disassemble or decompile an xmrobj file.
+ * See xmrengcomp.cx utility program.
+ */
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    /*
+     * Encapsulate object code for a method.
+     */
+    public abstract class ObjectTokens {
+        public ScriptObjCode scriptObjCode;
+
+        public ObjectTokens (ScriptObjCode scriptObjCode)
+        {
+            this.scriptObjCode = scriptObjCode;
+        }
+
+        public abstract void Close ();
+        public abstract void BegMethod (DynamicMethod method);
+        public abstract void EndMethod ();
+        public abstract void DefineLabel (int number, string name);
+        public abstract void DefineLocal (int number, string name, string type, Type syType);
+        public abstract void DefineMethod (string methName, Type retType, Type[] argTypes, string[] argNames);
+        public abstract void MarkLabel (int offset, int number);
+        public abstract void BegExcBlk (int offset);
+        public abstract void BegCatBlk (int offset, Type excType);
+        public abstract void BegFinBlk (int offset);
+        public abstract void EndExcBlk (int offset);
+        public abstract void EmitNull (int offset, OpCode opCode);
+        public abstract void EmitField (int offset, OpCode opCode, FieldInfo field);
+        public abstract void EmitLocal (int offset, OpCode opCode, int number);
+        public abstract void EmitType (int offset, OpCode opCode, Type type);
+        public abstract void EmitLabel (int offset, OpCode opCode, int number);
+        public abstract void EmitLabels (int offset, OpCode opCode, int[] numbers);
+        public abstract void EmitMethod (int offset, OpCode opCode, MethodInfo method);
+        public abstract void EmitCtor (int offset, OpCode opCode, ConstructorInfo ctor);
+        public abstract void EmitDouble (int offset, OpCode opCode, double value);
+        public abstract void EmitFloat (int offset, OpCode opCode, float value);
+        public abstract void EmitInteger (int offset, OpCode opCode, int value);
+        public abstract void EmitString (int offset, OpCode opCode, string value);
+    }
+
+    /******************\
+     *  DISASSEMBLER  *
+    \******************/
+
+    public class OTDisassemble : ObjectTokens {
+        private static readonly int OPCSTRWIDTH = 12;
+
+        private Dictionary<int,string> labelNames;
+        private Dictionary<int,string> localNames;
+        private StringBuilder lbuf = new StringBuilder ();
+        private TextWriter twout;
+
+        public OTDisassemble (ScriptObjCode scriptObjCode, TextWriter twout) : base (scriptObjCode)
+        {
+            this.twout = twout;
+        }
+
+        public override void Close ()
+        {
+            twout.WriteLine ("TheEnd.");
+        }
+
+        /**
+         * About to generate object code for this method.
+         */
+        public override void BegMethod (DynamicMethod method)
+        {
+            labelNames = new Dictionary<int,string> ();
+            localNames = new Dictionary<int,string> ();
+
+            twout.WriteLine ("");
+
+            lbuf.Append (method.ReturnType.Name);
+            lbuf.Append (' ');
+            lbuf.Append (method.Name);
+
+            ParameterInfo[] parms = method.GetParameters ();
+            int nArgs = parms.Length;
+            lbuf.Append (" (");
+            for (int i = 0; i < nArgs; i ++) {
+                if (i > 0) lbuf.Append (", ");
+                lbuf.Append (parms[i].ParameterType.Name);
+            }
+            lbuf.Append (')');
+            FlushLine ();
+
+            lbuf.Append ('{');
+            FlushLine ();
+        }
+
+        /**
+         * Dump out reconstructed source for this method.
+         */
+        public override void EndMethod ()
+        {
+            lbuf.Append ('}');
+            FlushLine ();
+        }
+
+        /**
+         * Add instructions to stream.
+         */
+        public override void DefineLabel (int number, string name)
+        {
+            labelNames[number] = name + "$" + number;
+        }
+
+        public override void DefineLocal (int number, string name, string type, Type syType)
+        {
+            localNames[number] = name + "$" + number;
+
+            lbuf.Append ("          ");
+            lbuf.Append (type.PadRight (OPCSTRWIDTH - 1));
+            lbuf.Append (' ');
+            lbuf.Append (localNames[number]);
+            FlushLine ();
+        }
+
+        public override void DefineMethod (string methName, Type retType, Type[] argTypes, string[] argNames)
+        { }
+
+        public override void MarkLabel (int offset, int number)
+        {
+            LinePrefix (offset);
+            lbuf.Append (labelNames[number]);
+            lbuf.Append (":");
+            FlushLine ();
+        }
+
+        public override void BegExcBlk (int offset)
+        {
+            LinePrefix (offset);
+            lbuf.Append (" BeginExceptionBlock");
+            FlushLine ();
+        }
+
+        public override void BegCatBlk (int offset, Type excType)
+        {
+            LinePrefix (offset);
+            lbuf.Append (" BeginCatchBlock ");
+            lbuf.Append (excType.Name);
+            FlushLine ();
+        }
+
+        public override void BegFinBlk (int offset)
+        {
+            LinePrefix (offset);
+            lbuf.Append (" BeginFinallyBlock");
+            FlushLine ();
+        }
+
+        public override void EndExcBlk (int offset)
+        {
+            LinePrefix (offset);
+            lbuf.Append (" EndExceptionBlock");
+            FlushLine ();
+        }
+
+        public override void EmitNull (int offset, OpCode opCode)
+        {
+            LinePrefix (offset, opCode);
+            FlushLine ();
+        }
+
+        public override void EmitField (int offset, OpCode opCode, FieldInfo field)
+        {
+            LinePrefix (offset, opCode);
+            lbuf.Append (field.DeclaringType.Name);
+            lbuf.Append (':');
+            lbuf.Append (field.Name);
+            lbuf.Append (" -> ");
+            lbuf.Append (field.FieldType.Name);
+            lbuf.Append ("   (field)");
+            FlushLine ();
+        }
+
+        public override void EmitLocal (int offset, OpCode opCode, int number)
+        {
+            LinePrefix (offset, opCode);
+            lbuf.Append (localNames[number]);
+            lbuf.Append ("   (local)");
+            FlushLine ();
+        }
+
+        public override void EmitType (int offset, OpCode opCode, Type type)
+        {
+            LinePrefix (offset, opCode);
+            lbuf.Append (type.Name);
+            lbuf.Append ("   (type)");
+            FlushLine ();
+        }
+
+        public override void EmitLabel (int offset, OpCode opCode, int number)
+        {
+            LinePrefix (offset, opCode);
+            lbuf.Append (labelNames[number]);
+            lbuf.Append ("   (label)");
+            FlushLine ();
+        }
+
+        public override void EmitLabels (int offset, OpCode opCode, int[] numbers)
+        {
+            LinePrefix (offset, opCode);
+
+            int lineLen = lbuf.Length;
+            int nLabels = numbers.Length;
+            for (int i = 0; i < nLabels; i ++) {
+                if (i > 0) {
+                    lbuf.AppendLine ();
+                    lbuf.Append (",".PadLeft (lineLen));
+                }
+                lbuf.Append (labelNames[numbers[i]]);
+            }
+
+            FlushLine ();
+        }
+
+        public override void EmitMethod (int offset, OpCode opCode, MethodInfo method)
+        {
+            LinePrefix (offset, opCode);
+
+            ParameterInfo[] parms = method.GetParameters ();
+            int nArgs = parms.Length;
+            if (method.DeclaringType != null) {
+                lbuf.Append (method.DeclaringType.Name);
+                lbuf.Append (':');
+            }
+            lbuf.Append (method.Name);
+            lbuf.Append ('(');
+            for (int i = 0; i < nArgs; i ++) {
+                if (i > 0) lbuf.Append (",");
+                lbuf.Append (parms[i].ParameterType.Name);
+            }
+            lbuf.Append (") -> ");
+            lbuf.Append (method.ReturnType.Name);
+
+            FlushLine ();
+        }
+
+        public override void EmitCtor (int offset, OpCode opCode, ConstructorInfo ctor)
+        {
+            LinePrefix (offset, opCode);
+
+            ParameterInfo[] parms = ctor.GetParameters ();
+            int nArgs = parms.Length;
+            lbuf.Append (ctor.DeclaringType.Name);
+            lbuf.Append (":(");
+            for (int i = 0; i < nArgs; i ++) {
+                if (i > 0) lbuf.Append (",");
+                lbuf.Append (parms[i].ParameterType.Name);
+            }
+            lbuf.Append (")");
+
+            FlushLine ();
+        }
+
+        public override void EmitDouble (int offset, OpCode opCode, double value)
+        {
+            LinePrefix (offset, opCode);
+            lbuf.Append (value.ToString ());
+            lbuf.Append ("   (double)");
+            FlushLine ();
+        }
+
+        public override void EmitFloat (int offset, OpCode opCode, float value)
+        {
+            LinePrefix (offset, opCode);
+            lbuf.Append (value.ToString ());
+            lbuf.Append ("   (float)");
+            FlushLine ();
+        }
+
+        public override void EmitInteger (int offset, OpCode opCode, int value)
+        {
+            LinePrefix (offset, opCode);
+            lbuf.Append (value.ToString ());
+            lbuf.Append ("   (int)");
+            FlushLine ();
+        }
+
+        public override void EmitString (int offset, OpCode opCode, string value)
+        {
+            LinePrefix (offset, opCode);
+            lbuf.Append ("\"");
+            lbuf.Append (value);
+            lbuf.Append ("\"   (string)");
+            FlushLine ();
+        }
+
+        /**
+         * Put offset and opcode at beginning of line.
+         */
+        private void LinePrefix (int offset, OpCode opCode)
+        {
+            LinePrefix (offset);
+            lbuf.Append ("  ");
+            lbuf.Append (opCode.ToString ().PadRight (OPCSTRWIDTH - 1));
+            lbuf.Append (' ');
+        }
+
+        private void LinePrefix (int offset)
+        {
+            lbuf.Append ("  ");
+            lbuf.Append (offset.ToString ("X4"));
+            lbuf.Append ("  ");
+        }
+
+        /**
+         * Flush line buffer to output file.
+         */
+        private void FlushLine ()
+        {
+            if (lbuf.Length > 0) {
+                twout.WriteLine (lbuf.ToString ());
+                lbuf.Remove (0, lbuf.Length);
+            }
+        }
+    }
+
+    /****************\
+     *  DECOMPILER  *
+    \****************/
+
+    /**
+     * Note:  The decompiler does not handle any xmroption extensions
+     *        such as &&&, |||, ? operators and switch statements, as
+     *        they do branches with a non-empty stack, which is way
+     *        beyond this code's ability to analyze.
+     */
+
+    public class OTDecompile : ObjectTokens {
+        public const string _mainCallNo      = "__mainCallNo$";
+        public const string _callLabel       = "__call_";
+        public const string _callMode        = "callMode";
+        public const string _checkRunQuick   = "CheckRunQuick";
+        public const string _checkRunStack   = "CheckRunStack";
+        public const string _cmRestore       = "__cmRestore";
+        public const string _doBreak         = "dobreak_";
+        public const string _doCont          = "docont_";
+        public const string _doGblInit       = "doGblInit";
+        public const string _doLoop          = "doloop_";
+        public const string _ehArgs          = "ehArgs";
+        public const string _forBreak        = "forbreak_";
+        public const string _forCont         = "forcont_";
+        public const string _forLoop         = "forloop_";
+        public const string _globalvarinit   = "$globalvarinit()";
+        public const string _heapTrackerPop  = "Pop";
+        public const string _heapTrackerPush = "Push";
+        public const string _ifDone          = "ifdone_";
+        public const string _ifElse          = "ifelse_";
+        public const string _llAbstemp       = "llAbstemp";
+        public const string _retlbl          = "__retlbl";
+        public const string _retval          = "__retval$";
+        public const string _whileBreak      = "whilebreak_";
+        public const string _whileCont       = "whilecont_";
+        public const string _whileLoop       = "whileloop_";
+        public const string _xmrinst         = "__xmrinst";
+        public const string _xmrinstlocal    = "__xmrinst$";
+
+        private const string INDENT = "    ";
+        private const string LABELINDENT = "  ";
+
+        private static Dictionary<string,string> typeTranslator = InitTypeTranslator ();
+        private static Dictionary<string,string> InitTypeTranslator ()
+        {
+            Dictionary<string,string> d = new Dictionary<string,string> ();
+            d["Boolean"]    = "integer";
+            d["bool"]       = "integer";
+            d["Double"]     = "float";
+            d["double"]     = "float";
+            d["Int32"]      = "integer";
+            d["int"]        = "integer";
+            d["htlist"]     = "list";
+            d["htobject"]   = "object";
+            d["htstring"]   = "string";
+            d["lslfloat"]   = "float";
+            d["lslint"]     = "integer";
+            d["lsllist"]    = "list";
+            d["lslrot"]     = "rotation";
+            d["lslstr"]     = "string";
+            d["lslvec"]     = "vector";
+            d["Quaternion"] = "rotation";
+            d["String"]     = "string";
+            d["Vector3"]    = "vector";
+            return d;
+        }
+
+        private Dictionary<int,OTLocal> eharglist;
+        private Dictionary<int,OTLabel> labels;
+        private Dictionary<int,OTLocal> locals;
+        private Dictionary<string,string[]> methargnames;
+        private LinkedList<OTCilInstr> cilinstrs;
+        private OTStmtBlock topBlock;
+        private Stack<OTOpnd> opstack;
+        private Stack<OTStmtBegExcBlk> trystack;
+        private Stack<OTStmtBlock> blockstack;
+
+        private int dupNo;
+        private DynamicMethod method;
+        private string laststate;
+        private TextWriter twout;
+
+        public OTDecompile (ScriptObjCode scriptObjCode, TextWriter twout) : base (scriptObjCode)
+        {
+            this.twout = twout;
+            twout.Write ("xmroption dollarsigns;");
+            methargnames = new Dictionary<string,string[]> ();
+        }
+
+        public override void Close ()
+        {
+            if (laststate != null) {
+                twout.Write ("\n}");
+                laststate = null;
+            }
+            twout.Write ('\n');
+        }
+
+        /**
+         * About to generate object code for this method.
+         */
+        public override void BegMethod (DynamicMethod method)
+        {
+            this.method = method;
+
+            eharglist  = new Dictionary<int,OTLocal> ();
+            labels     = new Dictionary<int,OTLabel> ();
+            locals     = new Dictionary<int,OTLocal> ();
+            cilinstrs  = new LinkedList<OTCilInstr> ();
+            opstack    = new Stack<OTOpnd> ();
+            trystack   = new Stack<OTStmtBegExcBlk> ();
+            blockstack = new Stack<OTStmtBlock> ();
+
+            dupNo = 0;
+        }
+
+        /**
+         * Dump out reconstructed source for this method.
+         */
+        public override void EndMethod ()
+        {
+            /*
+             * Convert CIL code to primitive statements.
+             * There are a bunch of labels and internal code such as call stack save restore.
+             */
+            topBlock = new OTStmtBlock ();
+            blockstack.Push (topBlock);
+            for (LinkedListNode<OTCilInstr> link = cilinstrs.First; link != null; link = link.Next) {
+                link.Value.BuildStatements (this, link);
+            }
+
+            /*
+             * Strip out stuff we don't want, such as references to callMode.
+             * This strips out stack frame capture and restore code.
+             */
+            topBlock.StripStuff (null);
+
+            // including a possible final return statement
+            // - delete if void return value
+            // - delete if returning __retval cuz we converted all __retval assignments to return statements
+            if ((topBlock.blkstmts.Last != null) && (topBlock.blkstmts.Last.Value is OTStmtRet)) {
+                OTStmtRet finalret = (OTStmtRet) topBlock.blkstmts.Last.Value;
+                if ((finalret.value == null) ||
+                        ((finalret.value is OTOpndLocal) &&
+                                ((OTOpndLocal) finalret.value).local.name.StartsWith (_retval))) {
+                    topBlock.blkstmts.RemoveLast ();
+                }
+            }
+
+            /**
+             * At this point, all behind-the-scenes references are removed except
+             * that the do/for/if/while blocks are represented by OTStmtCont-style
+             * if/jumps.  So try to convert them to the higher-level structures.
+             */
+            topBlock.DetectDoForIfWhile (null);
+
+            /*
+             * Final strip to get rid of unneeded @forbreak_<suffix>; labels and the like.
+             */
+            topBlock.StripStuff (null);
+
+            /*
+             * Build reference counts so we don't output unneeded declarations,
+             * especially temps and internal variables.
+             */
+            foreach (OTLocal local in locals.Values) {
+                local.nlclreads  = 0;
+                local.nlclwrites = 0;
+            }
+            topBlock.CountRefs ();
+            for (IEnumerator<int> localenum = locals.Keys.GetEnumerator (); localenum.MoveNext ();) {
+                OTLocal local = locals[localenum.Current];
+                if (((local.nlclreads | local.nlclwrites) == 0) || local.name.StartsWith (_xmrinstlocal)) {
+                    locals.Remove (localenum.Current);
+                    localenum = locals.Keys.GetEnumerator ();
+                }
+            }
+
+            /*
+             * Strip the $n off of local vars that are not ambiguous.
+             * Make sure they don't mask globals and arguments as well.
+             */
+            Dictionary<string,int> namecounts = new Dictionary<string,int> ();
+            foreach (Dictionary<int,string> varnames in scriptObjCode.globalVarNames.Values) {
+                foreach (string varname in varnames.Values) {
+                    int count;
+                    if (!namecounts.TryGetValue (varname, out count)) count = 0;
+                    namecounts[varname] = count + 1;
+                }
+            }
+            if (methargnames.ContainsKey (method.Name)) {
+                foreach (string argname in methargnames[method.Name]) {
+                    int count;
+                    if (!namecounts.TryGetValue (argname, out count)) count = 0;
+                    namecounts[argname] = count + 1;
+                }
+            }
+            foreach (OTLocal local in locals.Values) {
+                int i = local.name.LastIndexOf ('$');
+                string name = local.name.Substring (0, i);
+                int count;
+                if (!namecounts.TryGetValue (name, out count)) count = 0;
+                namecounts[name] = count + 1;
+            }
+            foreach (OTLocal local in locals.Values) {
+                int i = local.name.LastIndexOf ('$');
+                string name = local.name.Substring (0, i);
+                int count = namecounts[name];
+                if (count == 1) local.name = name;
+            }
+
+            /*
+             * Print out result.
+             */
+            if (method.Name == _globalvarinit) {
+                GlobalsDump ();
+            } else {
+                MethodDump ();
+            }
+        }
+
+        /**
+         * Add instructions to stream.
+         */
+        public override void DefineLabel (int number, string name)
+        {
+            labels.Add (number, new OTLabel (number, name));
+        }
+        public override void DefineLocal (int number, string name, string type, Type syType)
+        {
+            locals.Add (number, new OTLocal (number, name, type));
+        }
+        public override void DefineMethod (string methName, Type retType, Type[] argTypes, string[] argNames)
+        {
+            methargnames[methName] = argNames;
+        }
+        public override void MarkLabel (int offset, int number)
+        {
+            OTCilInstr label = labels[number];
+            label.offset = offset;
+            cilinstrs.AddLast (label);
+        }
+        public override void BegExcBlk (int offset)
+        {
+            cilinstrs.AddLast (new OTCilBegExcBlk (offset));
+        }
+        public override void BegCatBlk (int offset, Type excType)
+        {
+            cilinstrs.AddLast (new OTCilBegCatBlk (offset, excType));
+        }
+        public override void BegFinBlk (int offset)
+        {
+            cilinstrs.AddLast (new OTCilBegFinBlk (offset));
+        }
+        public override void EndExcBlk (int offset)
+        {
+            cilinstrs.AddLast (new OTCilEndExcBlk (offset));
+        }
+        public override void EmitNull (int offset, OpCode opCode)
+        {
+            cilinstrs.AddLast (new OTCilNull (offset, opCode));
+        }
+        public override void EmitField (int offset, OpCode opCode, FieldInfo field)
+        {
+            cilinstrs.AddLast (new OTCilField (offset, opCode, field));
+        }
+        public override void EmitLocal (int offset, OpCode opCode, int number)
+        {
+            cilinstrs.AddLast (new OTCilLocal (offset, opCode, locals[number]));
+        }
+        public override void EmitType (int offset, OpCode opCode, Type type)
+        {
+            cilinstrs.AddLast (new OTCilType (offset, opCode, type));
+        }
+        public override void EmitLabel (int offset, OpCode opCode, int number)
+        {
+            cilinstrs.AddLast (new OTCilLabel (offset, opCode, labels[number]));
+        }
+        public override void EmitLabels (int offset, OpCode opCode, int[] numbers)
+        {
+            OTLabel[] labelarray = new OTLabel[numbers.Length];
+            for (int i = 0; i < numbers.Length; i ++) {
+                labelarray[i] = labels[numbers[i]];
+            }
+            cilinstrs.AddLast (new OTCilLabels (offset, opCode, labelarray));
+        }
+        public override void EmitMethod (int offset, OpCode opCode, MethodInfo method)
+        {
+            cilinstrs.AddLast (new OTCilMethod (offset, opCode, method));
+        }
+        public override void EmitCtor (int offset, OpCode opCode, ConstructorInfo ctor)
+        {
+            cilinstrs.AddLast (new OTCilCtor (offset, opCode, ctor));
+        }
+        public override void EmitDouble (int offset, OpCode opCode, double value)
+        {
+            cilinstrs.AddLast (new OTCilDouble (offset, opCode, value));
+        }
+        public override void EmitFloat (int offset, OpCode opCode, float value)
+        {
+            cilinstrs.AddLast (new OTCilFloat (offset, opCode, value));
+        }
+        public override void EmitInteger (int offset, OpCode opCode, int value)
+        {
+            cilinstrs.AddLast (new OTCilInteger (offset, opCode, value));
+        }
+        public override void EmitString (int offset, OpCode opCode, string value)
+        {
+            cilinstrs.AddLast (new OTCilString (offset, opCode, value));
+        }
+
+        /**
+         * Add the given statement to the end of the currently open block.
+         */
+        public void AddLastStmt (OTStmt stmt)
+        {
+            blockstack.Peek ().blkstmts.AddLast (stmt);
+        }
+
+        /**
+         * Generate output for $globalvarinit() function.
+         * Also outputs declarations for global variables.
+         */
+        private void GlobalsDump ()
+        {
+            /*
+             * Scan $globalvarinit().  It should only have global var assignments in it.
+             * Also gather up list of variables it initializes.
+             */
+            bool badinit = false;
+            Dictionary<string,string> inittypes = new Dictionary<string,string> ();
+            foreach (OTStmt stmt in topBlock.blkstmts) {
+                if (!(stmt is OTStmtStore)) {
+                    badinit = true;
+                    break;
+                }
+                OTStmtStore store = (OTStmtStore) stmt;
+                if (!(store.varwr is OTOpndGlobal)) {
+                    badinit = true;
+                    break;
+                }
+                OTOpndGlobal globalop = (OTOpndGlobal) store.varwr;
+                inittypes[globalop.PrintableString] = "";
+            }
+
+            /*
+             * Scan through list of all global variables in the script.
+             * Output declarations for those what don't have any init statement for them.
+             * Save the type for those that do have init statements.
+             */
+            bool first = true;
+            foreach (string iartypename in scriptObjCode.globalVarNames.Keys) {
+                Dictionary<int,string> varnames = scriptObjCode.globalVarNames[iartypename];
+                string typename = iartypename.ToLowerInvariant ();
+                if (typename.StartsWith ("iar")) typename = typename.Substring (3);
+                if (typename.EndsWith ("s")) typename = typename.Substring (0, typename.Length - 1);
+                foreach (string varname in varnames.Values) {
+                    if (!badinit && inittypes.ContainsKey (varname)) {
+                        inittypes[varname] = typename;
+                    } else {
+                        if (first) twout.Write ('\n');
+                        twout.Write ('\n' + typename + ' ' + varname + ';');
+                        first = false;
+                    }
+                }
+            }
+
+            /*
+             * If $globalvarinit() has anything bad in it, output it as a function.
+             * Otherwise, output it as a series of global declarations with init values.
+             */
+            if (badinit) {
+                MethodDump ();
+            } else {
+                foreach (OTStmt stmt in topBlock.blkstmts) {
+                    OTStmtStore store = (OTStmtStore) stmt;
+                    OTOpndGlobal globalop = (OTOpndGlobal) store.varwr;
+                    string name = globalop.PrintableString;
+                    if (first) twout.Write ('\n');
+                    twout.Write ('\n' + inittypes[name] + ' ');
+                    store.PrintStmt (twout, "");
+                    first = false;
+                }
+            }
+        }
+
+        /**
+         * Generate output for other functions.
+         */
+        private void MethodDump ()
+        {
+            string indent;
+
+            /*
+             * Event handlers don't have an argument list as such in the original
+             * code.  Instead they have a series of assignments from ehargs[] to
+             * local variables.  So make those local variables look like they are
+             * an argument list.
+             */
+            int i = method.Name.IndexOf (' ');
+            if (i >= 0) {
+
+                /*
+                 * Maybe we have to output the state name.
+                 */
+                string statename = method.Name.Substring (0, i);
+                string eventname = method.Name.Substring (++ i);
+
+                if (laststate != statename) {
+                    if (laststate != null) twout.Write ("\n}");
+                    if (statename == "default") {
+                        twout.Write ("\n\ndefault {");
+                    } else {
+                        twout.Write ("\n\nstate " + statename + " {");
+                    }
+                    laststate = statename;
+                } else {
+                    twout.Write ('\n');
+                }
+
+                /*
+                 * Output event name and argument list.
+                 * Remove from locals list so they don't print below.
+                 */
+                twout.Write ('\n' + INDENT + eventname + " (");
+                MethodInfo meth = typeof (IEventHandlers).GetMethod (eventname);
+                i = 0;
+                foreach (ParameterInfo pi in meth.GetParameters ()) {
+                    // skip the first param cuz it's the XMRInstance arg
+                    if (i > 0) twout.Write (", ");
+                    OTLocal local;
+                    if (eharglist.TryGetValue (i, out local) && locals.ContainsKey (local.number)) {
+                        twout.Write (local.DumpString ());
+                        locals.Remove (local.number);
+                    } else {
+                        // maybe the assignment was removed
+                        // eg, because the local was write-only (not referenced)
+                        // so substitute in placeholder that won't be referenced
+                        twout.Write (AbbrType (pi.ParameterType) + " arg$" + (i + 1));
+                    }
+                    i ++;
+                }
+                twout.Write (')');
+
+                /*
+                 * Indent method body by 4 spaces.
+                 */
+                indent = INDENT;
+            } else {
+
+                /*
+                 * Maybe need to close out previous state.
+                 */
+                if (laststate != null) {
+                    twout.Write ("\n}");
+                    laststate = null;
+                }
+
+                /*
+                 * Output blank line and return type (if any).
+                 */
+                twout.Write ("\n\n");
+                if (method.ReturnType != typeof (void)) {
+                    twout.Write (AbbrType (method.ReturnType) + ' ');
+                }
+
+                /*
+                 * Output method name and argument list.
+                 */
+                int j = method.Name.IndexOf ('(');
+                if (j < 0) {
+                    twout.Write (method.Name);
+                } else {
+                    twout.Write (method.Name.Substring (0, j) + " (");
+                    bool first = true;
+                    j = 0;
+                    foreach (ParameterInfo pi in method.GetParameters ()) {
+                        if (j > 0) {  // skip the XMRInstance arg$0 parameter
+                            if (!first) twout.Write (", ");
+                            twout.Write (AbbrType (pi.ParameterType) + ' ' + MethArgName (j));
+                            first = false;
+                        }
+                        j ++;
+                    }
+                    twout.Write (')');
+                }
+
+                /*
+                 * Don't indent method body at all.
+                 */
+                indent = "";
+            }
+
+            /*
+             * Output local variable declarations.
+             */
+            twout.Write ('\n' + indent + '{');
+            bool didOne = false;
+            foreach (OTLocal local in locals.Values) {
+                twout.Write ('\n' + indent + INDENT + local.DumpString () + ";  // r:" + local.nlclreads + " w:" + local.nlclwrites);
+                didOne = true;
+            }
+            if (didOne) twout.Write ('\n');
+
+            /*
+             * Output statements.
+             */
+            if (topBlock.blkstmts.Count == 0) {
+                twout.Write (" }");
+            } else {
+                topBlock.PrintBodyAndEnd (twout, indent);
+            }
+        }
+
+        /**
+         * Get abbreviated type string.
+         */
+        public static string AbbrType (Type type)
+        {
+            if (type == null) return "null";
+            return AbbrType (type.Name);
+        }
+        public static string AbbrType (string type)
+        {
+            if (type.StartsWith ("OpenSim.Region.ScriptEngine.XMREngine.")) {
+                type = type.Substring (38);
+                int i = type.IndexOf (',');
+                if (i > 0) type = type.Substring (0, i);
+            }
+            if (typeTranslator.ContainsKey (type)) {
+                type = typeTranslator[type];
+            }
+            return type;
+        }
+
+        /**
+         * Get current method's argument name.
+         */
+        public string MethArgName (int index)
+        {
+            string[] argnames;
+            if (methargnames.TryGetValue (method.Name, out argnames) && (index < argnames.Length)) {
+                return argnames[index];
+            }
+            return "arg$" + index;
+        }
+
+        /**
+         * Strip svperflvovs (float) cast from rotation/vector values.
+         */
+        public static OTOpnd StripFloatCast (OTOpnd op)
+        {
+            if (op is OTOpndCast) {
+                OTOpndCast opcast = (OTOpndCast) op;
+                if ((opcast.type == typeof (double)) && (opcast.value is OTOpndInt)) {
+                    return opcast.value;
+                }
+            }
+            return op;
+        }
+
+        /**
+         * Strip svperflvovs Brtrues so we don't end up with stuff like 'if (!! someint) ...'.
+         */
+        public static OTOpnd StripBrtrue (OTOpnd op)
+        {
+            if (op is OTOpndUnOp) {
+                OTOpndUnOp opunop = (OTOpndUnOp) op;
+                if (opunop.opCode == MyOp.Brtrue) return opunop.value;
+            }
+            return op;
+        }
+
+        /*
+         * Local variable declaration.
+         */
+        private class OTLocal {
+            public int number;
+            public string name;
+            public string type;
+
+            public int nlclreads;
+            public int nlclwrites;
+
+            public OTLocal (int number, string name, string type)
+            {
+                this.number = number;
+                this.name   = name.StartsWith ("tmp$") ? name : name + "$" + number;
+                this.type   = type;
+            }
+
+            public string DumpString ()
+            {
+                return AbbrType (type) + ' ' + name;
+            }
+        }
+
+        /***********************************************\
+         *  Tokens that are one-for-one with CIL code  *
+        \***********************************************/
+
+        /*
+         * Part of instruction stream.
+         */
+        public abstract class OTCilInstr {
+            public int offset;     // cil offset
+
+            public OTCilInstr (int offset)
+            {
+                this.offset = offset;
+            }
+
+            public abstract string DumpString ();
+            public abstract void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link);
+
+            protected void CheckEmptyStack (OTDecompile decompile, string opMnemonic)
+            {
+                if (decompile.opstack.Count > 0) {
+                    Console.Error.WriteLine ("CheckEmptyStack: " + decompile.method.Name + " 0x" + offset.ToString ("X") + ": " +
+                            opMnemonic + " stack depth " + decompile.opstack.Count);
+                }
+            }
+        }
+
+        /*
+         * Label mark point.
+         */
+        private class OTLabel : OTCilInstr {
+            public int number;
+            public string name;
+
+            public int lbljumps;
+
+            public OTLabel (int number, string name) : base (-1)
+            {
+                this.number = number;
+                this.name   = name;
+            }
+
+            public string PrintableName {
+                get {
+                    if (name.StartsWith (_doBreak))    return _doBreak    + "$" + number;
+                    if (name.StartsWith (_doCont))     return _doCont     + "$" + number;
+                    if (name.StartsWith (_forBreak))   return _forBreak   + "$" + number;
+                    if (name.StartsWith (_forCont))    return _forCont    + "$" + number;
+                    if (name.StartsWith (_whileBreak)) return _whileBreak + "$" + number;
+                    if (name.StartsWith (_whileCont))  return _whileCont  + "$" + number;
+                    return name;
+                }
+            }
+
+            public override string DumpString ()
+            {
+                return name + ":";
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                OTStmtLabel.AddLast (decompile, this);
+            }
+        }
+
+        /*
+         * 'try {'
+         */
+        private class OTCilBegExcBlk : OTCilInstr {
+            public LinkedList<OTCilBegCatBlk> catches = new LinkedList<OTCilBegCatBlk> ();
+
+            public OTCilBegExcBlk (int offset) : base (offset)
+            { }
+
+            public override string DumpString ()
+            {
+                return "try {";
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                CheckEmptyStack (decompile, "try");
+
+                // link the try itself onto outer block
+                OTStmtBegExcBlk trystmt = new OTStmtBegExcBlk ();
+                decompile.AddLastStmt (trystmt);
+
+                // subsequent statements go to the try block
+                trystmt.tryblock = new OTStmtBlock ();
+                decompile.trystack.Push (trystmt);
+                decompile.blockstack.Push (trystmt.tryblock);
+            }
+        }
+
+        /*
+         * '} catch (...) {'
+         */
+        private class OTCilBegCatBlk : OTCilInstr {
+            public Type excType;
+
+            public OTCilBegCatBlk (int offset, Type excType) : base (offset)
+            {
+                this.excType = excType;
+            }
+
+            public override string DumpString ()
+            {
+                return "} catch (" + AbbrType (excType) + ") {";
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                CheckEmptyStack (decompile, "catch");
+
+                // link the catch itself onto the try statement
+                OTStmtBegExcBlk trystmt = decompile.trystack.Peek ();
+                OTStmtBegCatBlk catstmt = new OTStmtBegCatBlk (excType);
+                trystmt.catches.AddLast (catstmt);
+
+                // start capturing statements into the catch block
+                catstmt.tryblock   = trystmt;
+                catstmt.catchblock = new OTStmtBlock ();
+                decompile.blockstack.Pop ();
+                decompile.blockstack.Push (catstmt.catchblock);
+
+                // fill the stack slot with something for the exception argument
+                OTOpndDup dup = new OTOpndDup (++ decompile.dupNo);
+                decompile.opstack.Push (dup);
+            }
+        }
+
+        /*
+         * '} finally {'
+         */
+        private class OTCilBegFinBlk : OTCilInstr {
+            public OTCilBegFinBlk (int offset) : base (offset)
+            { }
+
+            public override string DumpString ()
+            {
+                return "} finally {";
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                CheckEmptyStack (decompile, "finally");
+
+                // link the finally itself to the try statement
+                OTStmtBegExcBlk trystmt = decompile.trystack.Peek ();
+                OTStmtBegFinBlk finstmt = new OTStmtBegFinBlk ();
+                trystmt.finblock = finstmt;
+
+                // start capturing statements into the finally block
+                finstmt.tryblock = trystmt;
+                finstmt.finblock = new OTStmtBlock ();
+                decompile.blockstack.Pop ();
+                decompile.blockstack.Push (finstmt.finblock);
+            }
+        }
+
+        /*
+         * '}' end of try
+         */
+        private class OTCilEndExcBlk : OTCilInstr {
+            public OTCilEndExcBlk (int offset) : base (offset)
+            { }
+
+            public override string DumpString ()
+            {
+                return "} // end try";
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                CheckEmptyStack (decompile, "endtry");
+
+                // pop the try/catch/finally blocks from stacks
+                decompile.blockstack.Pop ();
+                decompile.trystack.Pop ();
+
+                // subsequent statements collect following the try
+            }
+        }
+
+        /*
+         * Actual opcodes (instructions).
+         */
+        private class OTCilNull : OTCilInstr {
+            public MyOp opCode;
+
+            public OTCilNull (int offset, OpCode opCode) : base (offset)
+            {
+                this.opCode = MyOp.GetByName (opCode.Name);
+            }
+
+            public override string DumpString ()
+            {
+                return opCode.ToString ();
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "conv.i1":
+                    case "conv.i2":
+                    case "conv.i4":
+                    case "conv.i8": {
+                        OTOpnd value = decompile.opstack.Pop ();
+                        decompile.opstack.Push (new OTOpndCast (typeof (int), value));
+                        break;
+                    }
+                    case "conv.r4":
+                    case "conv.r8": {
+                        OTOpnd value = decompile.opstack.Pop ();
+                        decompile.opstack.Push (new OTOpndCast (typeof (double), value));
+                        break;
+                    }
+                    case "dup": {
+                        OTOpnd value = decompile.opstack.Pop ();
+                        if (!(value is OTOpndDup)) {
+                            OTOpndDup dup = new OTOpndDup (++ decompile.dupNo);
+                            OTStmtStore.AddLast (decompile, dup, value);
+                            value = dup;
+                        }
+                        decompile.opstack.Push (value);
+                        decompile.opstack.Push (value);
+                        break;
+                    }
+                    case "endfinally": break;
+                    case "ldarg.0": { decompile.opstack.Push (new OTOpndArg (0, false, decompile)); break; }
+                    case "ldarg.1": { decompile.opstack.Push (new OTOpndArg (1, false, decompile)); break; }
+                    case "ldarg.2": { decompile.opstack.Push (new OTOpndArg (2, false, decompile)); break; }
+                    case "ldarg.3": { decompile.opstack.Push (new OTOpndArg (3, false, decompile)); break; }
+                    case "ldc.i4.0":  { decompile.opstack.Push (new OTOpndInt  (0)); break; }
+                    case "ldc.i4.1":  { decompile.opstack.Push (new OTOpndInt  (1)); break; }
+                    case "ldc.i4.2":  { decompile.opstack.Push (new OTOpndInt  (2)); break; }
+                    case "ldc.i4.3":  { decompile.opstack.Push (new OTOpndInt  (3)); break; }
+                    case "ldc.i4.4":  { decompile.opstack.Push (new OTOpndInt  (4)); break; }
+                    case "ldc.i4.5":  { decompile.opstack.Push (new OTOpndInt  (5)); break; }
+                    case "ldc.i4.6":  { decompile.opstack.Push (new OTOpndInt  (6)); break; }
+                    case "ldc.i4.7":  { decompile.opstack.Push (new OTOpndInt  (7)); break; }
+                    case "ldc.i4.8":  { decompile.opstack.Push (new OTOpndInt  (8)); break; }
+                    case "ldc.i4.m1": { decompile.opstack.Push (new OTOpndInt (-1)); break; }
+                    case "ldelem.i4":
+                    case "ldelem.r4":
+                    case "ldelem.r8":
+                    case "ldelem.ref": {
+                        OTOpnd index = decompile.opstack.Pop ();
+                        OTOpnd array = decompile.opstack.Pop ();
+                        decompile.opstack.Push (OTOpndArrayElem.Make (array, index, false, decompile));
+                        break;
+                    }
+                    case "ldnull": {
+                        decompile.opstack.Push (new OTOpndNull ());
+                        break;
+                    }
+                    case "neg":
+                    case "not": {
+                        OTOpnd value = decompile.opstack.Pop ();
+                        decompile.opstack.Push (OTOpndUnOp.Make (opCode, value));
+                        break;
+                    }
+                    case "pop": {
+                        OTStmtVoid.AddLast (decompile, decompile.opstack.Pop ());
+                        break;
+                    }
+                    case "ret": {
+                        OTOpnd value = null;
+                        if (decompile.method.ReturnType != typeof (void)) {
+                            value = decompile.opstack.Pop ();
+                        }
+                        CheckEmptyStack (decompile);
+                        decompile.AddLastStmt (new OTStmtRet (value));
+                        break;
+                    }
+                    case "stelem.i4":
+                    case "stelem.r8":
+                    case "stelem.ref": {
+                        OTOpnd value = decompile.opstack.Pop ();
+                        OTOpnd index = decompile.opstack.Pop ();
+                        OTOpnd array = decompile.opstack.Pop ();
+                        OTStmtStore.AddLast (decompile, OTOpndArrayElem.Make (array, index, false, decompile), value);
+                        break;
+                    }
+                    case "throw": {
+                        OTOpnd value = decompile.opstack.Pop ();
+                        CheckEmptyStack (decompile);
+                        decompile.AddLastStmt (new OTStmtThrow (value, decompile));
+                        break;
+                    }
+                    case "add":
+                    case "and":
+                    case "ceq":
+                    case "cgt":
+                    case "cgt.un":
+                    case "clt":
+                    case "clt.un":
+                    case "div":
+                    case "div.un":
+                    case "mul":
+                    case "or":
+                    case "rem":
+                    case "rem.un":
+                    case "shl":
+                    case "shr":
+                    case "shr.un":
+                    case "sub":
+                    case "xor": {
+                        OTOpnd rite = decompile.opstack.Pop ();
+                        OTOpnd left = decompile.opstack.Pop ();
+                        decompile.opstack.Push (OTOpndBinOp.Make (left, opCode, rite));
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+
+            protected void CheckEmptyStack (OTDecompile decompile)
+            {
+                CheckEmptyStack (decompile, opCode.ToString ());
+            }
+        }
+
+        private class OTCilField : OTCilNull {
+            public FieldInfo field;
+
+            public OTCilField (int offset, OpCode opCode, FieldInfo field) : base (offset, opCode)
+            {
+                this.field = field;
+            }
+
+            public override string DumpString ()
+            {
+                return opCode.ToString () + ' ' + field.Name;
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "ldfld": {
+                        OTOpnd obj = decompile.opstack.Pop ();
+                        decompile.opstack.Push (OTOpndField.Make (obj, field));
+                        break;
+                    }
+                    case "ldsfld": {
+                        decompile.opstack.Push (new OTOpndSField (field));
+                        break;
+                    }
+                    case "stfld": {
+                        OTOpnd val = decompile.opstack.Pop ();
+                        OTOpnd obj = decompile.opstack.Pop ();
+                        OTStmtStore.AddLast (decompile, OTOpndField.Make (obj, field), val);
+                        break;
+                    }
+                    case "stsfld": {
+                        OTOpnd val = decompile.opstack.Pop ();
+                        OTStmtStore.AddLast (decompile, new OTOpndSField (field), val);
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        private class OTCilLocal : OTCilNull {
+            public OTLocal local;
+
+            public OTCilLocal (int offset, OpCode opCode, OTLocal local) : base (offset, opCode)
+            {
+                this.local = local;
+            }
+
+            public override string DumpString ()
+            {
+                return opCode.ToString () + ' ' + local.name;
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "ldloc": {
+                        decompile.opstack.Push (new OTOpndLocal (local));
+                        break;
+                    }
+                    case "ldloca": {
+                        decompile.opstack.Push (new OTOpndLocalRef (local));
+                        break;
+                    }
+                    case "stloc": {
+                        OTOpnd val = decompile.opstack.Pop ();
+                        OTStmtStore.AddLast (decompile, new OTOpndLocal (local), val);
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        private class OTCilType : OTCilNull {
+            public Type type;
+
+            public OTCilType (int offset, OpCode opCode, Type type) : base (offset, opCode)
+            {
+                this.type = type;
+            }
+
+            public override string DumpString ()
+            {
+                return opCode.ToString () + ' ' + AbbrType (type);
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "box": {
+                        break;
+                    }
+                    case "castclass":
+                    case "unbox.any": {
+                        OTOpnd value = decompile.opstack.Pop ();
+                        decompile.opstack.Push (new OTOpndCast (type, value));
+                        break;
+                    }
+                    case "ldelem": {
+                        OTOpnd index = decompile.opstack.Pop ();
+                        OTOpnd array = decompile.opstack.Pop ();
+                        decompile.opstack.Push (OTOpndArrayElem.Make (array, index, false, decompile));
+                        break;
+                    }
+                    case "ldelema": {
+                        OTOpnd index = decompile.opstack.Pop ();
+                        OTOpnd array = decompile.opstack.Pop ();
+                        decompile.opstack.Push (OTOpndArrayElem.Make (array, index, true, decompile));
+                        break;
+                    }
+                    case "newarr": {
+                        OTOpnd index = decompile.opstack.Pop ();
+                        decompile.opstack.Push (new OTOpndNewarr (type, index));
+                        break;
+                    }
+                    case "stelem": {
+                        OTOpnd value = decompile.opstack.Pop ();
+                        OTOpnd index = decompile.opstack.Pop ();
+                        OTOpnd array = decompile.opstack.Pop ();
+                        OTStmtStore.AddLast (decompile, OTOpndArrayElem.Make (array, index, false, decompile), value);
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        private class OTCilLabel : OTCilNull {
+            public OTLabel label;
+
+            public OTCilLabel (int offset, OpCode opCode, OTLabel label) : base (offset, opCode)
+            {
+                this.label = label;
+            }
+
+            public override string DumpString ()
+            {
+                return opCode.ToString () + ' ' + label.name;
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+
+                    /*
+                     * We don't handle non-empty stack at branch points.
+                     *
+                     * So handle this case specially:
+                     *
+                     *    dup
+                     *    ldc.i4.0
+                     *    bge.s  llAbstemp  << we are here
+                     *    neg
+                     *  llAbstemp:
+                     *
+                     * becomes:
+                     *
+                     *    call llAbs
+                     */
+                    case "bge.s": {
+                        OTOpnd rite = decompile.opstack.Pop ();  // alleged zero
+                        OTOpnd left = decompile.opstack.Pop ();  // alleged dup
+
+                        if ((label.name == _llAbstemp) && (decompile.opstack.Count > 0)) {
+                            LinkedListNode<OTCilInstr> linkneg = link.Next;
+                            if ((left is OTOpndDup) && (rite is OTOpndInt) &&
+                                    (linkneg != null) && (linkneg.Value is OTCilNull) &&
+                                    (((OTCilNull) linkneg.Value).opCode == MyOp.Neg)) {
+                                OTOpndInt riteint = (OTOpndInt) rite;
+                                LinkedListNode<OTCilInstr> linklbl = linkneg.Next;
+                                if ((riteint.value == 0) && (linklbl != null) && (linklbl.Value is OTLabel) &&
+                                        (((OTLabel) linklbl.Value) == label)) {
+                                    linkneg.List.Remove (linkneg);
+                                    linklbl.List.Remove (linklbl);
+                                    MethodInfo method = typeof (ScriptBaseClass).GetMethod ("llAbs");
+                                    OTOpnd[] args = new OTOpnd[] { new OTOpndNull (), decompile.opstack.Pop () };
+                                    OTOpndCall.AddLast (decompile, method, args);
+                                    break;
+                                }
+                            }
+                        }
+
+                        CheckEmptyStack (decompile);
+                        OTOpnd valu = OTOpndBinOp.Make (left, opCode, rite);
+                        OTStmt jump = OTStmtJump.Make (label);
+                        decompile.AddLastStmt (new OTStmtCond (valu, jump));
+                        break;
+                    }
+
+                    case "beq":
+                    case "bge":
+                    case "bgt":
+                    case "ble":
+                    case "blt":
+                    case "bne.un":
+                    case "beq.s":
+                    case "bgt.s":
+                    case "ble.s":
+                    case "blt.s":
+                    case "bne.un.s": {
+                        OTOpnd rite = decompile.opstack.Pop ();
+                        OTOpnd left = decompile.opstack.Pop ();
+                        CheckEmptyStack (decompile);
+                        OTOpnd valu = OTOpndBinOp.Make (left, opCode, rite);
+                        OTStmt jump = OTStmtJump.Make (label);
+                        decompile.AddLastStmt (new OTStmtCond (valu, jump));
+                        break;
+                    }
+                    case "brfalse":
+                    case "brfalse.s":
+                    case "brtrue":
+                    case "brtrue.s": {
+                        OTOpnd value = decompile.opstack.Pop ();
+                        CheckEmptyStack (decompile);
+                        OTOpnd valu = OTOpndUnOp.Make (opCode, value);
+                        OTStmt jump = OTStmtJump.Make (label);
+                        decompile.AddLastStmt (new OTStmtCond (valu, jump));
+                        break;
+                    }
+                    case "br":
+                    case "br.s":
+                    case "leave": {
+                        CheckEmptyStack (decompile);
+                        OTStmt jump = OTStmtJump.Make (label);
+                        decompile.AddLastStmt (jump);
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        private class OTCilLabels : OTCilNull {
+            public OTLabel[] labels;
+
+            public OTCilLabels (int offset, OpCode opCode, OTLabel[] labels) : base (offset, opCode)
+            {
+                this.labels = labels;
+            }
+
+            public override string DumpString ()
+            {
+                StringBuilder sb = new StringBuilder ();
+                sb.Append (opCode.ToString ());
+                foreach (OTLabel label in labels) {
+                    sb.Append (' ');
+                    sb.Append (label.name);
+                }
+                return sb.ToString ();
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "switch": {
+                        OTOpnd value = decompile.opstack.Pop ();
+                        CheckEmptyStack (decompile);
+                        decompile.AddLastStmt (new OTStmtSwitch (value, labels));
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        private class OTCilMethod : OTCilNull {
+            public MethodInfo method;
+
+            public OTCilMethod (int offset, OpCode opCode, MethodInfo method) : base (offset, opCode)
+            {
+                this.method = method;
+            }
+
+            public override string DumpString ()
+            {
+                return opCode.ToString () + ' ' + method.Name;
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "call":
+                    case "callvirt": {
+                        int nargs = method.GetParameters ().Length;
+                        if (!method.IsStatic) nargs ++;
+                        OTOpnd[] args = new OTOpnd[nargs];
+                        for (int i = nargs; -- i >= 0;) {
+                            args[i] = decompile.opstack.Pop ();
+                        }
+                        OTOpndCall.AddLast (decompile, method, args);
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        private class OTCilCtor : OTCilNull {
+            public ConstructorInfo ctor;
+
+            public OTCilCtor (int offset, OpCode opCode, ConstructorInfo ctor) : base (offset, opCode)
+            {
+                this.ctor = ctor;
+            }
+
+            public override string DumpString ()
+            {
+                return opCode.ToString () + ' ' + AbbrType (ctor.DeclaringType);
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "newobj": {
+                        int nargs = ctor.GetParameters ().Length;
+                        OTOpnd[] args = new OTOpnd[nargs];
+                        for (int i = nargs; -- i >= 0;) {
+                            args[i] = decompile.opstack.Pop ();
+                        }
+                        decompile.opstack.Push (OTOpndNewobj.Make (ctor, args));
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        private class OTCilDouble : OTCilNull {
+            public double value;
+
+            public OTCilDouble (int offset, OpCode opCode, double value) : base (offset, opCode)
+            {
+                this.value = value;
+            }
+
+            public override string DumpString ()
+            {
+                return opCode.ToString () + ' ' + value;
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "ldc.r8": {
+                        decompile.opstack.Push (new OTOpndDouble (value));
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        private class OTCilFloat : OTCilNull {
+            public float value;
+
+            public OTCilFloat (int offset, OpCode opCode, float value) : base (offset, opCode)
+            {
+                this.value = value;
+            }
+
+            public override string DumpString ()
+            {
+                return opCode.ToString () + ' ' + value;
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "ldc.r4": {
+                        decompile.opstack.Push (new OTOpndFloat (value));
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        private class OTCilInteger : OTCilNull {
+            public int value;
+
+            public OTCilInteger (int offset, OpCode opCode, int value) : base (offset, opCode)
+            {
+                this.value = value;
+            }
+
+            public override string DumpString ()
+            {
+                return opCode.ToString () + ' ' + value;
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "ldarg":
+                    case "ldarg.s": {
+                        decompile.opstack.Push (new OTOpndArg (value, false, decompile));
+                        break;
+                    }
+                    case "ldarga":
+                    case "ldarga.s": {
+                        decompile.opstack.Push (new OTOpndArg (value, true, decompile));
+                        break;
+                    }
+                    case "ldc.i4":
+                    case "ldc.i4.s": {
+                        decompile.opstack.Push (new OTOpndInt (value));
+                        break;
+                    }
+                    case "starg": {
+                        OTOpnd val = decompile.opstack.Pop ();
+                        OTStmtStore.AddLast (decompile, new OTOpndArg (value, false, decompile), val);
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        private class OTCilString : OTCilNull {
+            public string value;
+
+            public OTCilString (int offset, OpCode opCode, string value) : base (offset, opCode)
+            {
+                this.value = value;
+            }
+
+            public override string DumpString ()
+            {
+                StringBuilder sb = new StringBuilder ();
+                sb.Append (opCode.ToString ());
+                sb.Append (' ');
+                TokenDeclInline.PrintParamString (sb, value);
+                return sb.ToString ();
+            }
+
+            public override void BuildStatements (OTDecompile decompile, LinkedListNode<OTCilInstr> link)
+            {
+                switch (opCode.ToString ()) {
+                    case "ldstr": {
+                        decompile.opstack.Push (new OTOpndString (value));
+                        break;
+                    }
+                    default: throw new Exception ("unknown opcode " + opCode.ToString ());
+                }
+            }
+        }
+
+        /***************************************\
+         *  Tokens what are on operand stack.  *
+        \***************************************/
+
+        public abstract class OTOpnd {
+
+            /**
+             * See if it possibly has any side effects.
+             */
+            public abstract bool HasSideEffects { get; }
+
+            /**
+             * Increment reference counts.
+             */
+            public virtual void CountRefs (bool writing)
+            { }
+
+            /**
+             * If this operand is a 'by reference' operand,
+             * return the corresponding 'by value' operand.
+             */
+            public virtual OTOpnd GetNonByRefOpnd ()
+            {
+                return this;
+            }
+
+            /**
+             * If this operand is same as oldopnd, replace it with newopnd.
+             *
+             * This default just does a shallow search which is ok if this operand does not have any sub-operands.
+             * But it must be overridden for a deep search if this operand has any sub-operands.
+             */
+            public virtual OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                return this;
+            }
+
+            /**
+             * See if the two operands are the same value.
+             * Note that calls might have side-effects so are never the same.
+             */
+            public abstract bool SameAs (OTOpnd other);
+
+            /**
+             * Get a printable string representation of the operand.
+             */
+            public abstract string PrintableString { get; }
+        }
+
+        /**
+         * Argument variable.
+         */
+        private class OTOpndArg : OTOpnd {
+            public int index;
+            public bool byref;
+
+            private OTDecompile decompile;
+
+            public OTOpndArg (int index, bool byref, OTDecompile decompile)
+            {
+                this.index = index;
+                this.byref = byref;
+                this.decompile = decompile;
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return false;
+                }
+            }
+
+            public override OTOpnd GetNonByRefOpnd ()
+            {
+                if (!byref) return this;
+                return new OTOpndArg (index, false, decompile);
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndArg)) return false;
+                return (((OTOpndArg) other).byref == byref) && (((OTOpndArg) other).index == index);
+            }
+
+            public override string PrintableString {
+                get {
+                    string argname = decompile.MethArgName (index);
+                    return byref ? ("ref " + argname) : argname;
+                }
+            }
+        }
+
+        /**
+         * Element of an array.
+         */
+        private class OTOpndArrayElem : OTOpnd {
+            public bool byref;
+            public OTOpnd array;
+            public OTOpnd index;
+
+            public static OTOpnd Make (OTOpnd array, OTOpnd index, bool byref, OTDecompile decompile)
+            {
+                /*
+                 * arg$0.glblVars.iar<type>[<intconst>] is a reference to a global variable
+                 * likewise so is __xmrinst.glblVars.iar<type>[<intconst>]
+                 */
+                if ((array is OTOpndField) && (index is OTOpndInt)) {
+
+                    /*
+                     * arrayfield = (arg$0.glblVars).iar<type>
+                     * arrayfieldobj = arg$0.glblVars
+                     * iartypename = iar<type>
+                     */
+                    OTOpndField arrayfield = (OTOpndField) array;
+                    OTOpnd arrayfieldobj = arrayfield.obj;
+                    string iartypename = arrayfield.field.Name;
+
+                    /*
+                     * See if they are what they are supposed to be.
+                     */
+                    if ((arrayfieldobj is OTOpndField) && iartypename.StartsWith ("iar")) {
+
+                        /*
+                         * arrayfieldobjfield = arg$0.glblVars
+                         */
+                        OTOpndField arrayfieldobjfield = (OTOpndField) arrayfieldobj;
+
+                        /*
+                         * See if the parts are what they are supposed to be.
+                         */
+                        if (IsArg0OrXMRInst (arrayfieldobjfield.obj) && (arrayfieldobjfield.field.Name == "glblVars")) {
+
+                            /*
+                             * Everything matches up, make a global variable instead of an array reference.
+                             */
+                            return new OTOpndGlobal (iartypename, ((OTOpndInt) index).value, byref, decompile.scriptObjCode);
+                        }
+                    }
+                }
+
+                /*
+                 * Other array reference.
+                 */
+                OTOpndArrayElem it = new OTOpndArrayElem ();
+                it.array = array;
+                it.index = index;
+                it.byref = byref;
+                return it;
+            }
+
+            private OTOpndArrayElem () { }
+
+            public override bool HasSideEffects {
+                get {
+                    return array.HasSideEffects || index.HasSideEffects;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                array.CountRefs (false);
+                index.CountRefs (false);
+            }
+
+            public override OTOpnd GetNonByRefOpnd ()
+            {
+                if (!byref) return this;
+                OTOpndArrayElem it = new OTOpndArrayElem ();
+                it.array = array;
+                it.index = index;
+                return it;
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                array = array.ReplaceOperand (oldopnd, newopnd, ref rc);
+                index = index.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndArrayElem)) return false;
+                OTOpndArrayElem otherae = (OTOpndArrayElem) other;
+                return array.SameAs (otherae.array) && index.SameAs (otherae.index);
+            }
+
+            public override string PrintableString {
+                get {
+                    return (byref ? "ref " : "") + array.PrintableString + "[" + index.PrintableString + "]";
+                }
+            }
+
+            /**
+             * See if the argument is a reference to arg$0 or __xmrinst
+             */
+            public static bool IsArg0OrXMRInst (OTOpnd obj)
+            {
+                if (obj is OTOpndArg) {
+                    OTOpndArg objarg = (OTOpndArg) obj;
+                    return objarg.index == 0;
+                }
+                if (obj is OTOpndLocal) {
+                    OTOpndLocal objlcl = (OTOpndLocal) obj;
+                    return objlcl.local.name.StartsWith (_xmrinstlocal);
+                }
+                return false;
+            }
+        }
+
+        /**
+         * Binary operator.
+         */
+        private class OTOpndBinOp : OTOpnd {
+            public OTOpnd left;
+            public MyOp opCode;
+            public OTOpnd rite;
+
+            private static Dictionary<string,string> xor1ops = InitXor1Ops ();
+
+            private static Dictionary<string,string> InitXor1Ops ()
+            {
+                Dictionary<string,string> d = new Dictionary<string,string> ();
+                d["ceq"] = "cne";
+                d["cge"] = "clt";
+                d["cgt"] = "cle";
+                d["cle"] = "cgt";
+                d["clt"] = "cge";
+                d["cne"] = "ceq";
+                return d;
+            }
+
+            public static OTOpnd Make (OTOpnd left, MyOp opCode, OTOpnd rite)
+            {
+                // ((x clt y) xor 1)  =>  (x cge y)  etc
+                string xor1op;
+                if ((left is OTOpndBinOp) && xor1ops.TryGetValue (((OTOpndBinOp) left).opCode.name, out xor1op) &&
+                    (opCode == MyOp.Xor) &&
+                    (rite is OTOpndInt) && (((OTOpndInt) rite).value == 1)) {
+                    opCode = MyOp.GetByName (xor1op);
+                }
+
+                // handle strcmp() cases (see OTOpndStrCmp)
+                if (left is OTOpndStrCmp) {
+                    OTOpnd strcmp = ((OTOpndStrCmp) left).MakeBinOp (opCode, rite);
+                    if (strcmp != null) return strcmp;
+                }
+
+                // nothing special, make as is
+                OTOpndBinOp it = new OTOpndBinOp ();
+                it.left   = left;
+                it.opCode = opCode;
+                it.rite   = rite;
+                return it;
+            }
+
+            private OTOpndBinOp () { }
+
+            public override bool HasSideEffects {
+                get {
+                    return left.HasSideEffects || rite.HasSideEffects;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                left.CountRefs (false);
+                rite.CountRefs (false);
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                left = left.ReplaceOperand (oldopnd, newopnd, ref rc);
+                rite = rite.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndBinOp)) return false;
+                OTOpndBinOp otherbo = (OTOpndBinOp) other;
+                return left.SameAs (otherbo.left) && (opCode.ToString () == otherbo.opCode.ToString ()) && rite.SameAs (otherbo.rite);
+            }
+
+            public override string PrintableString {
+                get {
+                    StringBuilder sb = new StringBuilder ();
+
+                    bool leftneedsparen = ItNeedsParentheses (left, true);
+                    if (leftneedsparen) sb.Append ('(');
+                    sb.Append (left.PrintableString);
+                    if (leftneedsparen) sb.Append (')');
+
+                    sb.Append (' ');
+                    sb.Append (opCode.source);
+                    sb.Append (' ');
+
+                    bool riteneedsparen = ItNeedsParentheses (rite, false);
+                    if (riteneedsparen) sb.Append ('(');
+                    sb.Append (rite.PrintableString);
+                    if (riteneedsparen) sb.Append (')');
+
+                    return sb.ToString ();
+                }
+            }
+
+            /**
+             * See if source code representation requires parentheses around the given operand.
+             * @param it = the other operand to decide about
+             * @param itleft = true: 'it' is on the left of this operand   (A $ B) # C
+             *                false: 'it' is on the right of this operand  A $ (B # C)
+             */
+            private bool ItNeedsParentheses (OTOpnd it, bool itleft)
+            {
+                if (!(it is OTOpndBinOp)) return false;
+                string itop = ((OTOpndBinOp) it).opCode.source;
+                string myop = opCode.source;
+
+                // find them in table.  higher number is for *, lower is for +.
+                int itpi, mypi;
+                if (!precedence.TryGetValue (itop, out itpi)) return true;
+                if (!precedence.TryGetValue (myop, out mypi)) return true;
+                int itpiabs = Math.Abs (itpi);
+                int mypiabs = Math.Abs (mypi);
+
+                // if its precedence is lower (eg +) than my precedence (eg *), it needs parentheses
+                if (itpiabs < mypiabs) return true;
+
+                // if its precedence is higher (eg *) than my precedence (eg +), it doesn't needs parentheses
+                if (itpiabs > mypiabs) return false;
+
+                // if (A $ B) # C, we can safely go without the parentheses
+                if (itleft) return false;
+
+                //  my   it
+                // A $ (B # C) only works without parentheses for commutative $
+                // A - (B + C) and A - (B - C) require parentheses
+                // A + (B - C) does not
+                return mypi < 0;  // neg: things like -, /, etc require parentheses
+                                  // pos: things like +, *, etc do not need parens
+            }
+
+            // see MMRScriptReduce.PrecedenceInit()
+            private static Dictionary<string,int> precedence = InitPrecedence ();
+            private static Dictionary<string,int> InitPrecedence ()
+            {
+                Dictionary<string,int> d = new Dictionary<string,int> ();
+                d["|"]  =  140;
+                d["^"]  =  160;
+                d["&"]  =  180;
+                d["<<"] = -260;
+                d[">>"] = -260;
+                d["+"]  =  280;
+                d["-"]  = -280;
+                d["*"]  =  320;
+                d["/"]  = -320;
+                d["%"]  = -320;
+                return d;
+            }
+        }
+
+        /**
+         * Call with or without return value.
+         */
+        private class OTOpndCall : OTOpnd {
+            private static Dictionary<string,MethodInfo> mathmeths = InitMathMeths ();
+            private static Dictionary<string,MethodInfo> InitMathMeths ()
+            {
+                Dictionary<string,MethodInfo> d = new Dictionary<string,MethodInfo> ();
+                d["Acos"]  = typeof (ScriptBaseClass).GetMethod ("llAcos");
+                d["Asin"]  = typeof (ScriptBaseClass).GetMethod ("llAsin");
+                d["Atan"]  = typeof (ScriptBaseClass).GetMethod ("llAtan");
+                d["Cos"]   = typeof (ScriptBaseClass).GetMethod ("llCos");
+                d["Abs"]   = typeof (ScriptBaseClass).GetMethod ("llFabs");
+                d["Log"]   = typeof (ScriptBaseClass).GetMethod ("llLog");
+                d["Log10"] = typeof (ScriptBaseClass).GetMethod ("llLog10");
+                d["Round"] = typeof (ScriptBaseClass).GetMethod ("llRound");
+                d["Sin"]   = typeof (ScriptBaseClass).GetMethod ("llSin");
+                d["Sqrt"]  = typeof (ScriptBaseClass).GetMethod ("llSqrt");
+                d["Tan"]   = typeof (ScriptBaseClass).GetMethod ("llTan");
+                return d;
+            }
+
+            public MethodInfo method;
+            public OTOpnd[] args;
+
+            // pushes on stack for return-value functions
+            // pushes to end of instruction stream for return-void functions
+            public static void AddLast (OTDecompile decompile, MethodInfo method, OTOpnd[] args)
+            {
+                int nargs = args.Length;
+
+                // heap tracker push is just the single arg value as far as we're concerned
+                if ((nargs == 1) && (method.Name == _heapTrackerPush) && method.DeclaringType.Name.StartsWith ("HeapTracker")) {
+                    decompile.opstack.Push (args[0]);
+                    return;
+                }
+
+                // heap tracker pop is just a store as far as we're concerned
+                if ((nargs == 2) && (method.Name == _heapTrackerPop) && method.DeclaringType.Name.StartsWith ("HeapTracker")) {
+                    OTStmtStore.AddLast (decompile, args[0], args[1]);
+                    return;
+                }
+
+                // string.Compare() is its own thing cuz it has to decompile many ways
+                if ((nargs == 2) && (method.DeclaringType == typeof (string)) && (method.Name == "Compare")) {
+                    decompile.opstack.Push (new OTOpndStrCmp (args[0], args[1]));
+                    return;
+                }
+
+                // ObjectToString, etc, should appear as casts
+                if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToBool")) {
+                    MethodInfo meth = typeof (XMRInstAbstract).GetMethod ("xmr" + method.Name);
+                    AddLast (decompile, meth, new OTOpnd[] { new OTOpndNull (), args[0] });
+                    return;
+                }
+                if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToFloat")) {
+                    decompile.opstack.Push (new OTOpndCast (typeof (double), args[0]));
+                    return;
+                }
+                if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToInteger")) {
+                    decompile.opstack.Push (new OTOpndCast (typeof (int), args[0]));
+                    return;
+                }
+                if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToList")) {
+                    decompile.opstack.Push (new OTOpndCast (typeof (LSL_List), args[0]));
+                    return;
+                }
+                if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToRotation")) {
+                    decompile.opstack.Push (new OTOpndCast (typeof (LSL_Rotation), args[0]));
+                    return;
+                }
+                if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToString")) {
+                    decompile.opstack.Push (new OTOpndCast (typeof (string), args[0]));
+                    return;
+                }
+                if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.EndsWith ("ToVector")) {
+                    decompile.opstack.Push (new OTOpndCast (typeof (LSL_Vector), args[0]));
+                    return;
+                }
+
+                if ((method.DeclaringType == typeof (XMRInstAbstract)) && (method.Name == "xmrHeapLeft")) {
+                    AddLast (decompile, typeof (ScriptBaseClass).GetMethod ("llGetFreeMemory"), new OTOpnd[] { new OTOpndNull () });
+                    return;
+                }
+
+                // pop to entry in the list/object/string array
+                if (PopToGlobalArray (decompile, method, args)) return;
+
+                // strip off event handler argument unwrapper calls
+                if ((nargs == 1) && (method.DeclaringType == typeof (TypeCast)) && method.Name.StartsWith ("EHArgUnwrap")) {
+                    decompile.opstack.Push (args[0]);
+                    return;
+                }
+
+                // translate Math method to ll method
+                MethodInfo mathmeth;
+                if ((method.DeclaringType == typeof (Math)) && mathmeths.TryGetValue (method.Name, out mathmeth)) {
+                    AddLast (decompile, mathmeth, new OTOpnd[] { new OTOpndNull (), args[0] });
+                    return;
+                }
+                if ((method.DeclaringType == typeof (Math)) && (method.Name == "Atan2")) {
+                    AddLast (decompile, typeof (ScriptBaseClass).GetMethod ("llAtan2"), new OTOpnd[] { new OTOpndNull (), args[0], args[1] });
+                    return;
+                }
+                if ((method.DeclaringType == typeof (Math)) && (method.Name == "Pow")) {
+                    AddLast (decompile, typeof (ScriptBaseClass).GetMethod ("llPow"), new OTOpnd[] { new OTOpndNull (), args[0], args[1] });
+                    return;
+                }
+
+                // string concat should be a bunch of adds
+                if ((method.Name == "Concat") && (method.DeclaringType == typeof (string))) {
+                    int k = args.Length;
+                    while (k > 1) {
+                        int j = 0;
+                        int i;
+                        for (i = 0; i + 2 <= k; i += 2) {
+                            args[j++] = OTOpndBinOp.Make (args[i+0], MyOp.Add, args[i+1]);
+                        }
+                        while (i < k) args[j++] = args[i++];
+                        k = j;
+                    }
+                    if (k > 0) decompile.opstack.Push (args[0]);
+                    return;
+                }
+
+                // bunch of calls for rotation and vector arithmetic
+                if ((method.DeclaringType == typeof (BinOpStr)) && BinOpStrCall (decompile, method, args)) return;
+                if ((method.DeclaringType == typeof (ScriptCodeGen)) && (method.Name == "LSLRotationNegate")) {
+                    decompile.opstack.Push (OTOpndUnOp.Make (MyOp.Neg, args[0]));
+                    return;
+                }
+                if ((method.DeclaringType == typeof (ScriptCodeGen)) && (method.Name == "LSLVectorNegate")) {
+                    decompile.opstack.Push (OTOpndUnOp.Make (MyOp.Neg, args[0]));
+                    return;
+                }
+
+                // otherwise process it as a call
+                OTOpndCall call = new OTOpndCall ();
+                call.method = method;
+                call.args   = args;
+                if (method.ReturnType == typeof (void)) {
+                    OTStmtVoid.AddLast (decompile, call);
+                } else {
+                    decompile.opstack.Push (call);
+                }
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return true;
+                }
+            }
+
+            /**
+             * Handle a call to XMRInstArrays.Pop<List,Object,String>
+             * by converting it to a store directly into the array.
+             */
+            private static bool PopToGlobalArray (OTDecompile decompile, MethodInfo method, OTOpnd[] args)
+            {
+                if (method.DeclaringType != typeof (XMRInstArrays)) return false;
+                if (args.Length != 3) return false;
+
+                string array = null;
+                if (method.Name == "PopList")   array = "iarLists";
+                if (method.Name == "PopObject") array = "iarObjects";
+                if (method.Name == "PopString") array = "iarStrings";
+                if (array == null) return false;
+
+                // make token that points to the iar<whatever> array
+                FieldInfo field = typeof (XMRInstArrays).GetField (array);
+                OTOpnd arrayfield = OTOpndField.Make (args[0], field);
+
+                // make token that points to the element to be popped to
+                OTOpnd element = OTOpndArrayElem.Make (arrayfield, args[1], false, decompile);
+
+                // make a statement to store value in that element
+                OTStmtStore.AddLast (decompile, element, args[2]);
+
+                return true;
+            }
+
+            /**
+             * BinOpStr has a bunch of calls to do funky arithmetic.
+             * Instead of generating a call, put back the original source.
+             */
+            private static bool BinOpStrCall (OTDecompile decompile, MethodInfo method, OTOpnd[] args)
+            {
+                switch (method.Name) {
+                    case "MethFloatAddList":
+                    case "MethIntAddList":
+                    case "MethKeyAddList":
+                    case "MethListAddFloat":
+                    case "MethListAddInt":
+                    case "MethListAddKey":
+                    case "MethListAddList":
+                    case "MethListAddObj":
+                    case "MethListAddRot":
+                    case "MethListAddStr":
+                    case "MethListAddVec":
+                    case "MethObjAddList":
+                    case "MethRotAddList":
+                    case "MethRotAddRot":
+                    case "MethStrAddList":
+                    case "MethVecAddList":
+                    case "MethVecAddVec": {
+                        decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Add, args[1]));
+                        return true;
+                    }
+
+                    case "MethListEqList":
+                    case "MethRotEqRot":
+                    case "MethVecEqVec": {
+                        decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Ceq, args[1]));
+                        return true;
+                    }
+
+                    case "MethListNeList":
+                    case "MethRotNeRot":
+                    case "MethVecNeVec": {
+                        decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Cne, args[1]));
+                        return true;
+                    }
+
+                    case "MethRotSubRot":
+                    case "MethVecSubVec": {
+                        decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Sub, args[1]));
+                        return true;
+                    }
+
+                    case "MethFloatMulVec":
+                    case "MethIntMulVec":
+                    case "MethRotMulRot":
+                    case "MethVecMulFloat":
+                    case "MethVecMulInt":
+                    case "MethVecMulRot":
+                    case "MethVecMulVec": {
+                        decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Mul, args[1]));
+                        return true;
+                    }
+
+                    case "MethRotDivRot":
+                    case "MethVecDivFloat":
+                    case "MethVecDivInt":
+                    case "MethVecDivRot": {
+                        decompile.opstack.Push (OTOpndBinOp.Make (args[0], MyOp.Div, args[1]));
+                        return true;
+                    }
+
+                    default: return false;
+                }
+            }
+
+            private OTOpndCall () { }
+
+            public override void CountRefs (bool writing)
+            {
+                foreach (OTOpnd arg in args) {
+                    arg.CountRefs (false);
+                }
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                for (int i = 0; i < args.Length; i ++) {
+                    args[i] = args[i].ReplaceOperand (oldopnd, newopnd, ref rc);
+                }
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                return false;
+            }
+
+            public override string PrintableString {
+                get {
+                    StringBuilder sb = new StringBuilder ();
+
+                    // GetByKey(a,i) => a[i]
+                    if ((method.DeclaringType == typeof (XMR_Array)) && (method.Name == "GetByKey") && (args.Length == 2)) {
+                        sb.Append (args[0].PrintableString);
+                        sb.Append ('[');
+                        sb.Append (args[1].PrintableString);
+                        sb.Append (']');
+                        return sb.ToString ();
+                    }
+
+                    // SetByKey(a,i,v) => a[i] = v
+                    if ((method.DeclaringType == typeof (XMR_Array)) && (method.Name == "SetByKey") && (args.Length == 3)) {
+                        sb.Append (args[0].PrintableString);
+                        sb.Append ('[');
+                        sb.Append (args[1].PrintableString);
+                        sb.Append ("] = ");
+                        sb.Append (args[2].PrintableString);
+                        return sb.ToString ();
+                    }
+
+                    // CompValuListEl.GetElementFromList accesses list elements like an array.
+                    if ((method.DeclaringType == typeof (CompValuListEl)) && (method.Name == "GetElementFromList")) {
+                        sb.Append (args[0].PrintableString);
+                        sb.Append ('[');
+                        sb.Append (args[1].PrintableString);
+                        sb.Append (']');
+                        return sb.ToString ();
+                    }
+
+                    // methods that are part of ScriptBaseClass are LSL functions such as llSay()
+                    // so we want to skip outputting "arg$0," as it is the hidden "this" argument.
+                    // and there are also XMRInstAbstract functions such as xmrEventDequeue().
+                    int starti = 0;
+                    if ((method.DeclaringType == typeof (ScriptBaseClass)) && !method.IsStatic) starti = 1;
+                    if ((method.DeclaringType == typeof (XMRInstAbstract)) && !method.IsStatic) starti = 1;
+
+                    // likewise, method that have null as the declaring type are script-defined
+                    // dynamic methods which have a hidden "this" argument passed as "arg$0".
+                    if (method.DeclaringType == null) starti = 1;
+
+                    // all others we want to show the type name (such as Math.Abs, String.Compare, etc)
+                    if (starti == 0) {
+                        sb.Append (AbbrType (method.DeclaringType));
+                        sb.Append ('.');
+                    }
+
+                    // script-defined functions have the param types as part of their name
+                    // so strip them off here so they don't clutter things up
+                    int i = method.Name.IndexOf ('(');
+                    if (i < 0) sb.Append (method.Name);
+                    else sb.Append (method.Name.Substring (0, i));
+
+                    // now add the call arguments
+                    sb.Append (" (");
+                    bool first = true;
+                    foreach (OTOpnd arg in args) {
+                        if (-- starti < 0) {
+                            if (!first) sb.Append (", ");
+                            sb.Append (arg.PrintableString);
+                            first = false;
+                        }
+                    }
+                    sb.Append (')');
+                    return sb.ToString ();
+                }
+            }
+        }
+
+        /**
+         * Cast value to the given type.
+         */
+        private class OTOpndCast : OTOpnd {
+            public Type type;
+            public OTOpnd value;
+
+            public OTOpndCast (Type type, OTOpnd value)
+            {
+                this.type  = type;
+                this.value = value;
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return value.HasSideEffects;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                value.CountRefs (false);
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndCast)) return false;
+                OTOpndCast othercast = (OTOpndCast) other;
+                return (type == othercast.type) && value.SameAs (othercast.value);
+            }
+
+            public override string PrintableString {
+                get {
+                    StringBuilder sb = new StringBuilder ();
+                    sb.Append ('(');
+                    sb.Append (AbbrType (type));
+                    sb.Append (") ");
+                    if (value is OTOpndBinOp) sb.Append ('(');
+                    sb.Append (value.PrintableString);
+                    if (value is OTOpndBinOp) sb.Append (')');
+                    return sb.ToString ();
+                }
+            }
+        }
+
+        /**
+         * Duplicate stack value without re-performing computation.
+         * Semantics just like local var except it doesn't have a declaration.
+         */
+        private class OTOpndDup : OTOpnd {
+            public int index;
+            public int ndupreads;
+
+            public OTOpndDup (int index)
+            {
+                this.index = index;
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return false;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                if (!writing) ndupreads ++;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndDup)) return false;
+                return ((OTOpndDup) other).index == index;
+            }
+
+            public override string PrintableString { get { return "dup$" + index; } }
+        }
+
+        /**
+         * Field of an object.
+         */
+        private class OTOpndField : OTOpnd {
+            public OTOpnd obj;
+            public FieldInfo field;
+
+            public static OTOpnd Make (OTOpnd obj, FieldInfo field)
+            {
+                //  LSL_Float.value => the object itself
+                if ((field.DeclaringType == typeof (LSL_Float)) && (field.Name == "value")) {
+                    return obj;
+                }
+
+                //  LSL_Integer.value => the object itself
+                if ((field.DeclaringType == typeof (LSL_Integer)) && (field.Name == "value")) {
+                    return obj;
+                }
+
+                // LSL_String.m_string => the object itself
+                if ((field.DeclaringType == typeof (LSL_String)) && (field.Name == "m_string")) {
+                    return obj;
+                }
+
+                // some other field, output code to access it
+                // sometimes the object comes as by reference (value types), so we might need to deref it first
+                OTOpndField it = new OTOpndField ();
+                it.obj   = obj.GetNonByRefOpnd ();
+                it.field = field;
+                return it;
+            }
+
+            private OTOpndField () { }
+
+            public override bool HasSideEffects {
+                get {
+                    return obj.HasSideEffects;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                // the field may be getting written to, but the object is being read
+                obj.CountRefs (false);
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                obj = obj.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndField)) return false;
+                OTOpndField otherfield = (OTOpndField) other;
+                return (field.Name == otherfield.field.Name) && obj.SameAs (otherfield.obj);
+            }
+
+            public override string PrintableString {
+                get {
+                    StringBuilder sb = new StringBuilder ();
+                    if (obj is OTOpndBinOp) sb.Append ('(');
+                    sb.Append (obj.PrintableString);
+                    if (obj is OTOpndBinOp) sb.Append (')');
+                    sb.Append ('.');
+                    sb.Append (field.Name);
+                    return sb.ToString ();
+                }
+            }
+        }
+
+        /**
+         * Script-level global variable.
+         */
+        private class OTOpndGlobal : OTOpnd {
+            public string iartypename;
+            public int iararrayidx;
+            public bool byref;
+            public ScriptObjCode scriptObjCode;
+
+            public OTOpndGlobal (string iartypename, int iararrayidx, bool byref, ScriptObjCode scriptObjCode)
+            {
+                this.iartypename   = iartypename;
+                this.iararrayidx   = iararrayidx;
+                this.byref         = byref;
+                this.scriptObjCode = scriptObjCode;
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return false;
+                }
+            }
+
+            public override OTOpnd GetNonByRefOpnd ()
+            {
+                if (!byref) return this;
+                return new OTOpndGlobal (iartypename, iararrayidx, false, scriptObjCode);
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndGlobal)) return false;
+                OTOpndGlobal otherglobal = (OTOpndGlobal) other;
+                return (iartypename == otherglobal.iartypename) && (iararrayidx == otherglobal.iararrayidx);
+            }
+
+            public override string PrintableString {
+                get {
+                    return (byref ? "ref " : "") + scriptObjCode.globalVarNames[iartypename][iararrayidx];
+                }
+            }
+        }
+
+        /**
+         * List initialization.
+         */
+        private class OTOpndListIni : OTOpnd {
+            public OTOpnd[] values;
+
+            /**
+             * Try to detect list initialization building idiom:
+             *    dup$<n> = newarr object[<m>]      << link points here
+             *    dup$<n>[0] = bla
+             *    dup$<n>[1] = bla
+             *        ...
+             *    ... newobj list (dup$<n>) ...
+             */
+            public static bool Detect (LinkedListNode<OTStmt> link)
+            {
+                if (link == null) return false;
+
+                /*
+                 * Check for 'dup$<n> = newarr object[<m>]' and get listsize from <m>.
+                 */
+                OTStmtStore store = (OTStmtStore) link.Value;
+                if (!(store.varwr is OTOpndDup)) return false;
+                if (!(store.value is OTOpndNewarr)) return false;
+                OTOpndDup    storevar = (OTOpndDup) store.varwr;
+                OTOpndNewarr storeval = (OTOpndNewarr) store.value;
+                if (storeval.type != typeof (object)) return false;
+                if (!(storeval.index is OTOpndInt)) return false;
+                int listsize = ((OTOpndInt) storeval.index).value;
+
+                /*
+                 * Good chance of having list initializer, malloc an object to hold it.
+                 */
+                OTOpndListIni it = new OTOpndListIni ();
+                it.values = new OTOpnd[listsize];
+
+                /*
+                 * There should be exactly listsize statements following that of the form:
+                 *    dup$<n>[<i>] = bla
+                 * If so, save the bla values in the values[] array.
+                 */
+                LinkedListNode<OTStmt> vallink = link;
+                for (int i = 0; i < listsize; i ++) {
+                    vallink = vallink.Next;
+                    if (vallink == null) return false;
+                    if (!(vallink.Value is OTStmtStore)) return false;
+                    OTStmtStore valstore = (OTStmtStore) vallink.Value;
+                    if (!(valstore.varwr is OTOpndArrayElem)) return false;
+                    OTOpndArrayElem varelem = (OTOpndArrayElem) valstore.varwr;
+                    if (varelem.array != storevar) return false;
+                    if (!(varelem.index is OTOpndInt)) return false;
+                    if (((OTOpndInt) varelem.index).value != i) return false;
+                    it.values[i] = valstore.value;
+                }
+
+                /*
+                 * The next statement should have a 'newobj list (dup$<n>)' in it somewhere
+                 * that we want to replace with 'it'.
+                 */
+                ConstructorInfo protoctor = typeof (LSL_List).GetConstructor (new Type[] { typeof (object[]) });
+                OTOpnd[] protoargs = new OTOpnd[] { storevar };
+                OTOpnd proto = OTOpndNewobj.Make (protoctor, protoargs);
+
+                vallink = vallink.Next;
+                bool rc = vallink.Value.ReplaceOperand (proto, it);
+
+                /*
+                 * If successful, delete 'dup$n =' and all 'dup$n[i] =' statements.
+                 */
+                if (rc) {
+                    do {
+                        LinkedListNode<OTStmt> nextlink = link.Next;
+                        link.List.Remove (link);
+                        link = nextlink;
+                    } while (link != vallink);
+                }
+
+                return rc;
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    foreach (OTOpnd value in values) {
+                        if (value.HasSideEffects) return true;
+                    }
+                    return false;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                foreach (OTOpnd value in values) {
+                    value.CountRefs (false);
+                }
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                for (int i = 0; i < values.Length; i ++) {
+                    values[i] = values[i].ReplaceOperand (oldopnd, newopnd, ref rc);
+                }
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndListIni)) return false;
+                OTOpndListIni otherli = (OTOpndListIni) other;
+                if (otherli.values.Length != values.Length) return false;
+                for (int i = 0; i < values.Length; i ++) {
+                    if (!values[i].SameAs (otherli.values[i])) return false;
+                }
+                return true;
+            }
+
+            public override string PrintableString {
+                get {
+                    StringBuilder sb = new StringBuilder ();
+                    sb.Append ('[');
+                    for (int i = 0; i < values.Length; i ++) {
+                        if (i > 0) sb.Append (',');
+                        sb.Append (' ');
+                        sb.Append (values[i].PrintableString);
+                    }
+                    sb.Append (" ]");
+                    return sb.ToString ();
+                }
+            }
+        }
+
+        /**
+         * Local variable.
+         */
+        private class OTOpndLocal : OTOpnd {
+            public OTLocal local;
+
+            public OTOpndLocal (OTLocal local)
+            {
+                this.local = local;
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return false;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                if (writing) local.nlclwrites ++;
+                        else local.nlclreads ++;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndLocal)) return false;
+                OTOpndLocal otherlocal = (OTOpndLocal) other;
+                return local == otherlocal.local;
+            }
+
+            public override string PrintableString {
+                get {
+                    return local.name;
+                }
+            }
+        }
+        private class OTOpndLocalRef : OTOpnd {
+            public OTLocal local;
+
+            public OTOpndLocalRef (OTLocal local)
+            {
+                this.local = local;
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return true;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                local.nlclreads  ++;
+                local.nlclwrites ++;
+            }
+
+            public override OTOpnd GetNonByRefOpnd ()
+            {
+                return new OTOpndLocal (local);
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndLocal)) return false;
+                OTOpndLocal otherlocal = (OTOpndLocal) other;
+                return local == otherlocal.local;
+            }
+
+            public override string PrintableString { get { return "ref " + local.name; } }
+        }
+
+        /**
+         * New C#-level array.
+         */
+        private class OTOpndNewarr : OTOpnd {
+            public Type type;
+            public OTOpnd index;
+
+            public OTOpndNewarr (Type type, OTOpnd index)
+            {
+                this.type  = type;
+                this.index = index;
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return index.HasSideEffects;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                index.CountRefs (false);
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                index = index.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                return false;
+            }
+
+            public override string PrintableString { get { return "newarr " + type.Name + "[" + index.PrintableString + "]"; } }
+        }
+
+        /**
+         * New C#-level object.
+         */
+        private class OTOpndNewobj : OTOpnd {
+            public ConstructorInfo ctor;
+            public OTOpnd[] args;
+
+            public static OTOpnd Make (ConstructorInfo ctor, OTOpnd[] args)
+            {
+                // newobj LSL_Float (x)  =>  x
+                if ((ctor.DeclaringType == typeof (LSL_Float)) && (args.Length == 1)) {
+                    Type ptype = ctor.GetParameters ()[0].ParameterType;
+                    if (ptype == typeof (string)) {
+                        return new OTOpndCast (typeof (double), args[0]);
+                    }
+                    return args[0];
+                }
+
+                // newobj LSL_Integer (x)  =>  x
+                if ((ctor.DeclaringType == typeof (LSL_Integer)) && (args.Length == 1)) {
+                    Type ptype = ctor.GetParameters ()[0].ParameterType;
+                    if (ptype == typeof (string)) {
+                        return new OTOpndCast (typeof (int), args[0]);
+                    }
+                    return args[0];
+                }
+
+                // newobj LSL_String (x)  =>  x
+                if ((ctor.DeclaringType == typeof (LSL_String)) && (args.Length == 1)) {
+                    return args[0];
+                }
+
+                // newobj LSL_Rotation (x, y, z, w)  =>  <x, y, z, w>
+                if ((ctor.DeclaringType == typeof (LSL_Rotation)) && (args.Length == 4)) {
+                    return new OTOpndRot (args[0], args[1], args[2], args[3]);
+                }
+
+                // newobj LSL_Vector (x, y, z)  =>  <x, y, z>
+                if ((ctor.DeclaringType == typeof (LSL_Vector)) && (args.Length == 3)) {
+                    return new OTOpndVec (args[0], args[1], args[2]);
+                }
+
+                // newobj LSL_Rotation (string)  => (rotation) string
+                if ((ctor.DeclaringType == typeof (LSL_Rotation)) && (args.Length == 1)) {
+                    return new OTOpndCast (typeof (LSL_Rotation), args[0]);
+                }
+
+                // newobj LSL_Vector (string)  => (rotation) string
+                if ((ctor.DeclaringType == typeof (LSL_Vector)) && (args.Length == 1)) {
+                    return new OTOpndCast (typeof (LSL_Vector), args[0]);
+                }
+
+                // newobj LSL_List (newarr object[0])  =>  [ ]
+                if ((ctor.DeclaringType == typeof (LSL_List)) && (args.Length == 1) && (args[0] is OTOpndNewarr)) {
+                    OTOpndNewarr arg0 = (OTOpndNewarr) args[0];
+                    if ((arg0.type == typeof (object)) && (arg0.index is OTOpndInt) && (((OTOpndInt) arg0.index).value == 0)) {
+                        OTOpndListIni listini = new OTOpndListIni ();
+                        listini.values = new OTOpnd[0];
+                        return listini;
+                    }
+                }
+
+                // something else, output as is
+                OTOpndNewobj it = new OTOpndNewobj ();
+                it.ctor = ctor;
+                it.args = args;
+                return it;
+            }
+
+            private OTOpndNewobj () { }
+
+            public override bool HasSideEffects {
+                get {
+                    foreach (OTOpnd arg in args) {
+                        if (arg.HasSideEffects) return true;
+                    }
+                    return false;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                foreach (OTOpnd arg in args) {
+                    arg.CountRefs (false);
+                }
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                for (int i = 0; i < args.Length; i ++) {
+                    args[i] = args[i].ReplaceOperand (oldopnd, newopnd, ref rc);
+                }
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndNewobj)) return false;
+                OTOpndNewobj otherno = (OTOpndNewobj) other;
+                if (otherno.ctor.DeclaringType != ctor.DeclaringType) return false;
+                if (otherno.args.Length != args.Length) return false;
+                for (int i = 0; i < args.Length; i ++) {
+                    if (!args[i].SameAs (otherno.args[i])) return false;
+                }
+                return true;
+            }
+
+            public override string PrintableString {
+                get {
+                    StringBuilder sb = new StringBuilder ();
+                    sb.Append ("newobj ");
+                    sb.Append (ctor.DeclaringType.Name);
+                    sb.Append (" (");
+                    bool first = true;
+                    foreach (OTOpnd arg in args) {
+                        if (!first) sb.Append (", ");
+                        sb.Append (arg.PrintableString);
+                        first = false;
+                    }
+                    sb.Append (')');
+                    return sb.ToString ();
+                }
+            }
+        }
+
+        /**
+         * Rotation value.
+         */
+        private class OTOpndRot : OTOpnd {
+            private OTOpnd x, y, z, w;
+
+            public OTOpndRot (OTOpnd x, OTOpnd y, OTOpnd z, OTOpnd w)
+            {
+                this.x = StripFloatCast (x);
+                this.y = StripFloatCast (y);
+                this.z = StripFloatCast (z);
+                this.w = StripFloatCast (w);
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return x.HasSideEffects || y.HasSideEffects || z.HasSideEffects || w.HasSideEffects;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                x.CountRefs (false);
+                y.CountRefs (false);
+                z.CountRefs (false);
+                w.CountRefs (false);
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                x = x.ReplaceOperand (oldopnd, newopnd, ref rc);
+                y = y.ReplaceOperand (oldopnd, newopnd, ref rc);
+                z = z.ReplaceOperand (oldopnd, newopnd, ref rc);
+                w = w.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndRot)) return false;
+                OTOpndRot otherv = (OTOpndRot) other;
+                return otherv.x.SameAs (x) && otherv.y.SameAs (y) && otherv.z.SameAs (z) && otherv.w.SameAs (w);
+            }
+
+            public override string PrintableString {
+                get {
+                    return "<" + x.PrintableString + ", " + y.PrintableString + ", " + z.PrintableString + ", " + w.PrintableString + ">";
+                }
+            }
+        }
+
+        /**
+         * Static field.
+         */
+        private class OTOpndSField : OTOpnd {
+            private FieldInfo field;
+
+            public OTOpndSField (FieldInfo field)
+            {
+                this.field = field;
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return false;
+                }
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndSField)) return false;
+                OTOpndSField othersfield = (OTOpndSField) other;
+                return (field.Name == othersfield.field.Name) && (field.DeclaringType == othersfield.field.DeclaringType);
+            }
+
+            public override string PrintableString {
+                get {
+                    if (field.DeclaringType == typeof (ScriptBaseClass)) return field.Name;
+                    return field.DeclaringType.Name + "." + field.Name;
+                }
+            }
+        }
+
+        /**
+         * Call to string.Compare().
+         * See use cases in BinOpStr:
+         *   strcmp (a, b) ceq  0
+         *   (strcmp (a, b) ceq 0) xor 1  =>  we translate to:  strcmp (a, b) cne 0
+         *   strcmp (a, b) clt  0
+         *   strcmp (a, b) clt  1  // <=
+         *   strcmp (a, b) cgt  0
+         *   strcmp (a, b) cgt -1  // >=
+         * ...but then optimized by ScriptCollector if followed by br{false,true}:
+         *   ceq + xor 1 + brtrue  => bne.un
+         *   ceq + xor 1 + brfalse => beq
+         *   ceq + brtrue  => beq
+         *   ceq + brfalse => bne.un
+         *   cgt + brtrue  => bgt
+         *   cgt + brfalse => ble
+         *   clt + brtrue  => blt
+         *   clt + brfalse => bge
+         * So we end up with these cases:
+         *   strcmp (a, b) ceq  0
+         *   strcmp (a, b) cne  0
+         *   strcmp (a, b) clt  0
+         *   strcmp (a, b) clt  1
+         *   strcmp (a, b) cgt  0
+         *   strcmp (a, b) cgt -1
+         *   strcmp (a, b) beq    0
+         *   strcmp (a, b) bne.un 0
+         *   strcmp (a, b) bgt  0
+         *   strcmp (a, b) ble  0
+         *   strcmp (a, b) bgt -1
+         *   strcmp (a, b) ble -1
+         *   strcmp (a, b) blt  0
+         *   strcmp (a, b) bge  0
+         *   strcmp (a, b) blt  1
+         *   strcmp (a, b) bge  1
+         * ... so we pretty them up in OTOpndBinOp
+         */
+        private class OTOpndStrCmp : OTOpnd {
+            private static Dictionary<string,string> binops = InitBinops ();
+            private static Dictionary<string,string> InitBinops ()
+            {
+                Dictionary<string,string> d = new Dictionary<string,string> ();
+                d["ceq 0"]    = "ceq";
+                d["cne 0"]    = "cne";
+                d["clt 0"]    = "clt";
+                d["clt 1"]    = "cle";
+                d["cgt 0"]    = "cgt";
+                d["cgt -1"]   = "cge";
+                d["beq 0"]    = "ceq";
+                d["bne.un 0"] = "cne";
+                d["bgt 0"]    = "cgt";
+                d["ble 0"]    = "cle";
+                d["bgt -1"]   = "cge";
+                d["ble -1"]   = "clt";
+                d["blt 0"]    = "clt";
+                d["bge 0"]    = "cge";
+                d["blt 1"]    = "cle";
+                d["bge 1"]    = "cgt";
+                return d;
+            }
+
+            private OTOpnd arg0;
+            private OTOpnd arg1;
+
+            public OTOpndStrCmp (OTOpnd arg0, OTOpnd arg1)
+            {
+                this.arg0 = arg0;
+                this.arg1 = arg1;
+            }
+
+            /**
+             * Try to make something a script writer would recognize.
+             * If we can't, then we leave it as a call to xmrStringCompare().
+             *    this   = some strcmp(a,b)
+             *    opCode = hopefully some cxx or bxx from above table
+             *    rite   = hopefully some constant from above table
+             */
+            public OTOpnd MakeBinOp (MyOp opCode, OTOpnd rite)
+            {
+                if (!(rite is OTOpndInt)) return null;
+                int riteint = ((OTOpndInt) rite).value;
+                string key = opCode.name + ' ' + riteint;
+                string cxxopname;
+                if (!binops.TryGetValue (key, out cxxopname)) return null;
+                return OTOpndBinOp.Make (arg0, MyOp.GetByName (cxxopname), arg1);
+            }
+            public OTOpnd MakeUnOp (MyOp opCode)
+            {
+                if (opCode == MyOp.Brfalse) return OTOpndBinOp.Make (arg0, MyOp.Ceq, arg1);
+                if (opCode == MyOp.Brtrue)  return OTOpndBinOp.Make (arg0, MyOp.Cne, arg1);
+                return null;
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return false;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                arg0.CountRefs (writing);
+                arg1.CountRefs (writing);
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                arg0 = arg0.ReplaceOperand (oldopnd, newopnd, ref rc);
+                arg1 = arg1.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndStrCmp)) return false;
+                return arg0.SameAs (((OTOpndStrCmp) other).arg0) && arg1.SameAs (((OTOpndStrCmp) other).arg1);
+            }
+
+            public override string PrintableString {
+                get {
+                    return "xmrStringCompare (" + arg0.PrintableString + ", " + arg1.PrintableString + ")";
+                }
+            }
+        }
+
+        /**
+         * Unary operator.
+         */
+        private class OTOpndUnOp : OTOpnd {
+            public MyOp opCode;
+            public OTOpnd value;
+
+            private static Dictionary<string,string> brfops = InitBrfOps ();
+            private static Dictionary<string,string> InitBrfOps ()
+            {
+                Dictionary<string,string> d = new Dictionary<string,string> ();
+                d["beq"]    = "cne";
+                d["bge"]    = "clt";
+                d["bgt"]    = "cle";
+                d["ble"]    = "cgt";
+                d["blt"]    = "cge";
+                d["bne.un"] = "ceq";
+                d["ceq"]    = "cne";
+                d["cge"]    = "clt";
+                d["cgt"]    = "cle";
+                d["cle"]    = "cgt";
+                d["clt"]    = "cge";
+                d["cne"]    = "ceq";
+                return d;
+            }
+
+            public static OTOpnd Make (MyOp opCode, OTOpnd value)
+            {
+                // (brfalse (brfalse (x)))  =>  (brtrue (x))
+                if ((opCode == MyOp.Brfalse) && (value is OTOpndUnOp) && (((OTOpndUnOp) value).opCode == MyOp.Brfalse)) {
+                    ((OTOpndUnOp) value).opCode = MyOp.Brtrue;
+                    return value;
+                }
+
+                // (brfalse (brtrue (x)))  =>  (brfalse (x))
+                if ((opCode == MyOp.Brfalse) && (value is OTOpndUnOp) && (((OTOpndUnOp) value).opCode == MyOp.Brtrue)) {
+                    ((OTOpndUnOp) value).opCode = MyOp.Brfalse;
+                    return value;
+                }
+
+                // (brtrue (brfalse (x)))  =>  (brfalse (x))
+                if ((opCode == MyOp.Brtrue) && (value is OTOpndUnOp) && (((OTOpndUnOp) value).opCode == MyOp.Brfalse)) {
+                    return value;
+                }
+
+                // (brtrue (brtrue (x)))  =>  (brtrue (x))
+                if ((opCode == MyOp.Brtrue) && (value is OTOpndUnOp) && (((OTOpndUnOp) value).opCode == MyOp.Brtrue)) {
+                    return value;
+                }
+
+                // (brfalse (x beq y))  =>  (x bne y)  etc
+                string brfop;
+                if ((opCode == MyOp.Brfalse) && (value is OTOpndBinOp) && brfops.TryGetValue (((OTOpndBinOp) value).opCode.name, out brfop)) {
+                    ((OTOpndBinOp) value).opCode = MyOp.GetByName (brfop);
+                    return value;
+                }
+
+                // (brtrue  (x beq y))  =>  (x beq y)  etc
+                if ((opCode == MyOp.Brtrue) && (value is OTOpndBinOp) && brfops.ContainsKey (((OTOpndBinOp) value).opCode.name)) {
+                    return value;
+                }
+
+                // strcmp() can be a special case
+                if (value is OTOpndStrCmp) {
+                    OTOpnd strcmp = ((OTOpndStrCmp) value).MakeUnOp (opCode);
+                    if (strcmp != null) return strcmp;
+                }
+
+                // nothing special, save opcode and value
+                OTOpndUnOp it = new OTOpndUnOp ();
+                it.opCode = opCode;
+                it.value  = value;
+                return it;
+            }
+
+            private OTOpndUnOp () { }
+
+            public override bool HasSideEffects {
+                get {
+                    return value.HasSideEffects;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                value.CountRefs (false);
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndUnOp)) return false;
+                OTOpndUnOp otherop = (OTOpndUnOp) other;
+                return (opCode.ToString () == otherop.opCode.ToString ()) && value.SameAs (otherop.value);
+            }
+
+            public override string PrintableString {
+                get {
+                    StringBuilder sb = new StringBuilder ();
+                    sb.Append (opCode.source);
+                    sb.Append (' ');
+                    if (value is OTOpndBinOp) sb.Append ('(');
+                    sb.Append (value.PrintableString);
+                    if (value is OTOpndBinOp) sb.Append (')');
+                    return sb.ToString ();
+                }
+            }
+        }
+
+        /**
+         * Vector value.
+         */
+        private class OTOpndVec : OTOpnd {
+            private OTOpnd x, y, z;
+
+            public OTOpndVec (OTOpnd x, OTOpnd y, OTOpnd z)
+            {
+                this.x = StripFloatCast (x);
+                this.y = StripFloatCast (y);
+                this.z = StripFloatCast (z);
+            }
+
+            public override bool HasSideEffects {
+                get {
+                    return x.HasSideEffects || y.HasSideEffects || z.HasSideEffects;
+                }
+            }
+
+            public override void CountRefs (bool writing)
+            {
+                x.CountRefs (false);
+                y.CountRefs (false);
+                z.CountRefs (false);
+            }
+
+            public override OTOpnd ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd, ref bool rc)
+            {
+                if (SameAs (oldopnd)) {
+                    rc = true;
+                    return newopnd;
+                }
+                x = x.ReplaceOperand (oldopnd, newopnd, ref rc);
+                y = y.ReplaceOperand (oldopnd, newopnd, ref rc);
+                z = z.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return this;
+            }
+
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndVec)) return false;
+                OTOpndVec otherv = (OTOpndVec) other;
+                return otherv.x.SameAs (x) && otherv.y.SameAs (y) && otherv.z.SameAs (z);
+            }
+
+            public override string PrintableString {
+                get {
+                    return "<" + x.PrintableString + ", " + y.PrintableString + ", " + z.PrintableString + ">";
+                }
+            }
+        }
+
+        /**
+         * Constants.
+         */
+        private class OTOpndDouble : OTOpnd {
+            public double value;
+            public OTOpndDouble (double value) { this.value = value; }
+            public override bool HasSideEffects { get { return false; } }
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndDouble)) return false;
+                return ((OTOpndDouble) other).value == value;
+            }
+            public override string PrintableString {
+                get {
+                    string s = value.ToString ();
+                    long i;
+                    if (long.TryParse (s, out i)) {
+                        s += ".0";
+                    }
+                    return s;
+                }
+            }
+        }
+        private class OTOpndFloat : OTOpnd {
+            public float value;
+            public OTOpndFloat (float value) { this.value = value; }
+            public override bool HasSideEffects { get { return false; } }
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndFloat)) return false;
+                return ((OTOpndFloat) other).value == value;
+            }
+            public override string PrintableString {
+                get {
+                    string s = value.ToString ();
+                    long i;
+                    if (long.TryParse (s, out i)) {
+                        s += ".0";
+                    }
+                    return s;
+                }
+            }
+        }
+        private class OTOpndInt : OTOpnd {
+            public int value;
+            public OTOpndInt (int value) { this.value = value; }
+            public override bool HasSideEffects { get { return false; } }
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndInt)) return false;
+                return ((OTOpndInt) other).value == value;
+            }
+            public override string PrintableString { get { return value.ToString (); } }
+        }
+        private class OTOpndNull : OTOpnd {
+            public override bool HasSideEffects { get { return false; } }
+            public override bool SameAs (OTOpnd other)
+            {
+                return other is OTOpndNull;
+            }
+            public override string PrintableString { get { return "undef"; } }
+        }
+        private class OTOpndString : OTOpnd {
+            public string value;
+            public OTOpndString (string value) { this.value = value; }
+            public override bool HasSideEffects { get { return false; } }
+            public override bool SameAs (OTOpnd other)
+            {
+                if (!(other is OTOpndString)) return false;
+                return ((OTOpndString) other).value == value;
+            }
+            public override string PrintableString {
+                get {
+                    StringBuilder sb = new StringBuilder ();
+                    TokenDeclInline.PrintParamString (sb, value);
+                    return sb.ToString ();
+                }
+            }
+        }
+
+        /****************************************\
+         *  Tokens what are in statement list.  *
+        \****************************************/
+
+        public abstract class OTStmt {
+
+            /**
+             * Increment reference counts.
+             */
+            public abstract void CountRefs ();
+
+            /**
+             * Strip out any of the behind-the-scenes code such as stack capture/restore.
+             * By default, there is no change.
+             */
+            public virtual bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                return false;
+            }
+
+            /**
+             * Replace the oldopnd operand with the newopnd operand if it is present.
+             * Return whether or not it was found and replaced.
+             */
+            public abstract bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd);
+
+            /**
+             * Detect and modify for do/for/if/while structures.
+             */
+            public virtual bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
+            {
+                return false;
+            }
+
+            /**
+             * If this statement is the old statement, replace it with the given new statement.
+             * Also search any sub-ordinate statements.
+             * **NOTE**: minimally implemented to replace a Jump with a Break or Continue
+             */
+            public abstract OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt);
+
+            /**
+             * Print the statement out on the given printer with the given indenting.
+             * The first line is already indented, subsequent lines must be indented as given.
+             * This method should leave the printer at the end of the line.
+             */
+            public abstract void PrintStmt (TextWriter twout, string indent);
+
+            /**
+             * Strip all statements following this statement
+             * because this statement jumps somewhere.
+             */
+            protected bool StripStuffForTerminal (LinkedListNode<OTStmt> link)
+            {
+                // strip all statements following jump until seeing some label
+                bool rc = false;
+                if (link != null) {
+                    LinkedListNode<OTStmt> nextlink;
+                    while ((nextlink = link.Next) != null) {
+                        if (nextlink.Value is OTStmtLabel) break;
+                        nextlink.List.Remove (nextlink);
+                        rc = true;
+                    }
+                }
+                return rc;
+            }
+        }
+
+        /**************************\
+         *  Primitive statements  *
+        \**************************/
+
+        /**
+         * Begin catch block (catch).
+         */
+        private class OTStmtBegCatBlk : OTStmt {
+            public OTStmtBegExcBlk tryblock;
+            public OTStmtBlock catchblock;
+
+            private Type excType;
+
+            public OTStmtBegCatBlk (Type excType)
+            {
+                this.excType = excType;
+            }
+
+            public override void CountRefs ()
+            {
+                catchblock.CountRefs ();
+            }
+
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                return catchblock.StripStuff (null);
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                return catchblock.ReplaceOperand (oldopnd, newopnd);
+            }
+
+            public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
+            {
+                return catchblock.DetectDoForIfWhile (link);
+            }
+
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                catchblock = (OTStmtBlock) catchblock.ReplaceStatement (oldstmt, newstmt);
+                return this;
+            }
+
+            /**
+             * Print out the catch block including its enclosed statements.
+             */
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                twout.Write ("catch (" + excType.Name + ") ");
+                catchblock.PrintStmt (twout, indent);
+            }
+        }
+
+        /**
+         * Begin exception block (try).
+         */
+        private class OTStmtBegExcBlk : OTStmt {
+
+            // statements within the try { } not including any catch or finally
+            public OTStmtBlock tryblock;
+
+            // list of all catch { } blocks associated with this try { }
+            public LinkedList<OTStmtBegCatBlk> catches = new LinkedList<OTStmtBegCatBlk> ();
+
+            // possible single finally { } associated with this try
+            public OTStmtBegFinBlk finblock;  // might be null
+
+            public override void CountRefs ()
+            {
+                tryblock.CountRefs ();
+                foreach (OTStmtBegCatBlk catblock in catches) {
+                    catblock.CountRefs ();
+                }
+                if (finblock != null) finblock.CountRefs ();
+            }
+
+            /**
+             * Strip behind-the-scenes info from all the sub-blocks.
+             */
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                // strip behind-the-scenes info from all the sub-blocks.
+                bool rc = tryblock.StripStuff (null);
+                foreach (OTStmtBegCatBlk catblk in catches) {
+                    rc |= catblk.StripStuff (null);
+                }
+                if (finblock != null) rc |= finblock.StripStuff (null);
+                if (rc) return true;
+
+                // change:
+                //    try {
+                //       ...
+                //    }
+                // to:
+                //    {
+                //       ...
+                //    }
+                // note that an empty catch () { } has meaning so can't be stripped
+                // empty finally { } blocks strips itself from the try
+                if ((catches.Count == 0) && (finblock == null) && (link != null)) {
+                    link.List.AddAfter (link, tryblock);
+                    tryblock = null;
+                    link.List.Remove (link);
+                    return true;
+                }
+
+                return false;
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                bool rc = tryblock.ReplaceOperand (oldopnd, newopnd);
+                foreach (OTStmtBegCatBlk catblk in catches) {
+                    rc |= catblk.ReplaceOperand (oldopnd, newopnd);
+                }
+                if (finblock != null) rc |= finblock.ReplaceOperand (oldopnd, newopnd);
+                return rc;
+            }
+
+            public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
+            {
+                bool rc = tryblock.DetectDoForIfWhile (link);
+                foreach (OTStmtBegCatBlk catblk in catches) {
+                    rc |= catblk.DetectDoForIfWhile (link);
+                }
+                if (finblock != null) rc |= finblock.DetectDoForIfWhile (link);
+                return rc;
+            }
+
+            /**
+             * Assume we will never try to replace the try block itself.
+             * But go through all our sub-ordinates statements.
+             */
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                tryblock = (OTStmtBlock) tryblock.ReplaceStatement (oldstmt, newstmt);
+                for (LinkedListNode<OTStmtBegCatBlk> catlink = catches.First; catlink != null; catlink = catlink.Next) {
+                    catlink.Value = (OTStmtBegCatBlk) catlink.Value.ReplaceStatement (oldstmt, newstmt);
+                }
+                if (finblock != null) finblock = (OTStmtBegFinBlk) finblock.ReplaceStatement (oldstmt, newstmt);
+                return this;
+            }
+
+            /**
+             * Print out the try block including its enclosed statements.
+             * And since the try is the only thing pushed to the outer block,
+             * we also print out all the catch and finally blocks.
+             */
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                twout.Write ("try ");
+                tryblock.PrintStmt (twout, indent);
+                foreach (OTStmtBegCatBlk catblk in catches) {
+                    twout.Write (' ');
+                    catblk.PrintStmt (twout, indent);
+                }
+                if (finblock != null) {
+                    twout.Write (' ');
+                    finblock.PrintStmt (twout, indent);
+                }
+            }
+        }
+
+        /**
+         * Begin finally block (finally).
+         */
+        private class OTStmtBegFinBlk : OTStmt {
+            public OTStmtBegExcBlk tryblock;
+            public OTStmtBlock finblock;
+
+            public override void CountRefs ()
+            {
+                finblock.CountRefs ();
+            }
+
+            /**
+             * Strip behind-the-scene parts from the finally block.
+             */
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                // strip behind-the-scenes parts from finally block itself
+                if (finblock.StripStuff (null)) return true;
+
+                // if finblock is empty, delete the finally from the try
+                if (finblock.blkstmts.Count == 0) {
+                    tryblock.finblock = null;
+                    return true;
+                }
+
+                return false;
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                return finblock.ReplaceOperand (oldopnd, newopnd);
+            }
+
+            public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
+            {
+                return finblock.DetectDoForIfWhile (link);
+            }
+
+            /**
+             * Assume we will never try to replace the finally block itself.
+             * But go through all our sub-ordinates statements.
+             */
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                finblock = (OTStmtBlock) finblock.ReplaceStatement (oldstmt, newstmt);
+                return this;
+            }
+
+            /**
+             * Print out the finally block including its enclosed statements.
+             */
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                twout.Write ("finally ");
+                finblock.PrintStmt (twout, indent);
+            }
+        }
+
+        /**
+         * Simple if jump/break/continue statement.
+         */
+        private class OTStmtCond : OTStmt {
+            public OTOpnd valu;
+            public OTStmt stmt;  // jump, break, continue only
+
+            public OTStmtCond (OTOpnd valu, OTStmt stmt)
+            {
+                this.valu = valu;
+                this.stmt = stmt;
+            }
+
+            public override void CountRefs ()
+            {
+                valu.CountRefs (false);
+                stmt.CountRefs ();
+            }
+
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                // we assume that callMode is always CallMode_NORMAL, ie, not doing a stack capture or restore
+                // so the 'if (arg$0.callMode bne.un 0) ...' is deleted
+                // and the 'if (arg$0.callMode bne.un 1) ...' becomes unconditional
+                // it can also be __xmrinst.callMode instead of arg$0
+                if (valu is OTOpndBinOp) {
+                    OTOpndBinOp binop = (OTOpndBinOp) valu;
+                    if ((binop.left is OTOpndField) && (binop.opCode.ToString () == "bne.un") && (binop.rite is OTOpndInt)) {
+                        OTOpndField leftfield = (OTOpndField) binop.left;
+                        if (leftfield.field.Name == _callMode) {
+                            bool ok = false;
+                            if (leftfield.obj is OTOpndArg) {
+                                ok = ((OTOpndArg) leftfield.obj).index == 0;
+                            }
+                            if (leftfield.obj is OTOpndLocal) {
+                                ok = ((OTOpndLocal) leftfield.obj).local.name.StartsWith (_xmrinstlocal);
+                            }
+                            if (ok) {
+                                OTOpndInt riteint = (OTOpndInt) binop.rite;
+
+                                // delete 'if ((arg$0).callMode bne.un 0) ...'
+                                if (riteint.value == XMRInstAbstract.CallMode_NORMAL) {
+                                    link.List.Remove (link);
+                                    return true;
+                                }
+
+                                // make 'if ((arg$0).callMode bne.un 1) ...' unconditional
+                                if (riteint.value == XMRInstAbstract.CallMode_SAVE) {
+                                    link.Value = stmt;
+                                    return true;
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // similarly we assume that doGblInit is always 0 to eliminate the code at beginning of default state_entry()
+                // so the 'if (brfalse __xmrinst.doGblInit) ...' is made unconditional
+                if (valu is OTOpndUnOp) {
+                    OTOpndUnOp unop = (OTOpndUnOp) valu;
+                    if ((unop.opCode == MyOp.Brfalse) && (unop.value is OTOpndField)) {
+                        OTOpndField valuefield = (OTOpndField) unop.value;
+                        if (valuefield.field.Name == _doGblInit) {
+                            bool ok = false;
+                            if (valuefield.obj is OTOpndLocal) {
+                                ok = ((OTOpndLocal) valuefield.obj).local.name.StartsWith (_xmrinstlocal);
+                            }
+                            if (ok) {
+
+                                // make 'if (brfalse __xmrinst.doGblInit) ...' unconditional
+                                link.Value = stmt;
+                                return true;
+                            }
+                        }
+                    }
+                }
+
+                return false;
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                bool rc = stmt.ReplaceOperand (oldopnd, newopnd);
+                valu = valu.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return rc;
+            }
+
+            /**
+             * Maybe this simple if statement is part of a script-level if/then/else statement.
+             */
+            public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
+            {
+                return OTStmtIf.Detect (link);
+            }
+
+            /**
+             * Assume we won't replace the if statement itself.
+             * But search all our sub-ordinate statements.
+             */
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                stmt = stmt.ReplaceStatement (oldstmt, newstmt);
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                twout.Write ("if (" + StripBrtrue (valu).PrintableString + ") ");
+                stmt.PrintStmt (twout, indent);
+            }
+
+            /**
+             * Scan forward for a given label definition.
+             * Put intervening statements in a statement block.
+             * @param link = start scanning after this statement
+             * @param label = look for this label definition
+             * @param block = where to return intervening statement block
+             * @returns null: label definition not found
+             *          else: label definition statement
+             */
+            private static LinkedListNode<OTStmt> ScanForLabel (LinkedListNode<OTStmt> link,
+                        OTLabel label, out OTStmtBlock block)
+            {
+                block = new OTStmtBlock ();
+                while ((link = link.Next) != null) {
+                    if (link.Value is OTStmtLabel) {
+                        if (((OTStmtLabel) link.Value).label == label) break;
+                    }
+                    block.blkstmts.AddLast (link.Value);
+                }
+                return link;
+            }
+
+            /**
+             * Strip statements after link up to and including donelink.
+             */
+            private static void StripInterveningStatements (LinkedListNode<OTStmt> link, LinkedListNode<OTStmt> donelink)
+            {
+                LinkedListNode<OTStmt> striplink;
+                do {
+                    striplink = link.Next;
+                    striplink.List.Remove (striplink);
+                } while (striplink != donelink);
+            }
+        }
+
+        /**
+         * Jump to a label.
+         */
+        private class OTStmtJump : OTStmt {
+            public OTLabel label;
+
+            public static OTStmt Make (OTLabel label)
+            {
+                // jumps to __retlbl are return statements
+                // note that is is safe to say it is a valueless return because
+                // valued returns are done with this construct:
+                //    __retval = ....;
+                //    jump __retlbl;
+                // and those __retval = statements have been changed to return statements already
+                if (label.name.StartsWith (_retlbl)) return new OTStmtRet (null);
+
+                // other jumps are really jumps
+                OTStmtJump it = new OTStmtJump ();
+                it.label = label;
+                return it;
+            }
+
+            private OTStmtJump () { }
+
+            public override void CountRefs ()
+            {
+                label.lbljumps ++;
+            }
+
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                if (link == null) return false;
+
+                // strip statements following unconditional jump until next label
+                bool rc = StripStuffForTerminal (link);
+
+                // if we (now) have:
+                //      jump label;
+                //   @label;
+                // ... delete this jump
+                if (link.Next != null) {
+                    OTStmtLabel nextlabel = (OTStmtLabel) link.Next.Value;
+                    if (nextlabel.label == label) {
+                        link.List.Remove (link);
+                        rc = true;
+                    }
+                }
+
+                return rc;
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                return false;
+            }
+
+            /**
+             * This is actually what ReplaceStatement() is currently used for.
+             * It replaces a jump with a break or a continue.
+             */
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                if ((oldstmt is OTStmtJump) && (((OTStmtJump) oldstmt).label == label)) return newstmt;
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                twout.Write ("jump " + label.PrintableName + ';');
+            }
+        }
+
+        /**
+         * Label definition point.
+         */
+        private class OTStmtLabel : OTStmt {
+            public OTLabel label;
+
+            private OTDecompile decompile;
+
+            public static void AddLast (OTDecompile decompile, OTLabel label)
+            {
+                OTStmtLabel it = new OTStmtLabel ();
+                it.label = label;
+                it.decompile = decompile;
+                decompile.AddLastStmt (it);
+            }
+
+            private OTStmtLabel () { }
+
+            public override void CountRefs ()
+            {
+                // don't increment label.lbljumps
+                // cuz we don't want the positioning
+                // to count as a reference, only jumps
+                // to the label should count
+            }
+
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                // if label has nothing jumping to it, remove the label
+                if (link != null) {
+                    label.lbljumps = 0;
+                    decompile.topBlock.CountRefs ();
+                    if (label.lbljumps == 0) {
+                        link.List.Remove (link);
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                return false;
+            }
+
+            public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
+            {
+                if (OTStmtDo.Detect  (link)) return true;
+                if (OTStmtFor.Detect (link, true)) return true;
+                if (OTStmtFor.Detect (link, false)) return true;
+                return false;
+            }
+
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                twout.Write ("@" + label.PrintableName + ';');
+            }
+        }
+
+        /**
+         * Return with or without value.
+         */
+        private class OTStmtRet : OTStmt {
+            public OTOpnd value;  // might be null
+
+            public OTStmtRet (OTOpnd value)
+            {
+                this.value = value;
+            }
+
+            public override void CountRefs ()
+            {
+                if (value != null) value.CountRefs (false);
+            }
+
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                return StripStuffForTerminal (link);
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                bool rc = false;
+                if (value != null) value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return rc;
+            }
+
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                if (value == null) {
+                    twout.Write ("return;");
+                } else {
+                    twout.Write ("return " + value.PrintableString + ';');
+                }
+            }
+        }
+
+        /**
+         * Store value in variable.
+         */
+        private class OTStmtStore : OTStmt {
+            public OTOpnd varwr;
+            public OTOpnd value;
+
+            private OTDecompile decompile;
+
+            public static void AddLast (OTDecompile decompile, OTOpnd varwr, OTOpnd value)
+            {
+                OTStmtStore it = new OTStmtStore (varwr, value, decompile);
+                decompile.AddLastStmt (it);
+            }
+
+            public OTStmtStore (OTOpnd varwr, OTOpnd value, OTDecompile decompile)
+            {
+                this.varwr     = varwr;
+                this.value     = value;
+                this.decompile = decompile;
+            }
+
+            public override void CountRefs ()
+            {
+                varwr.CountRefs (true);
+                value.CountRefs (false);
+            }
+
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                // strip out stores to __mainCallNo
+                if (varwr is OTOpndLocal) {
+                    OTOpndLocal local = (OTOpndLocal) varwr;
+                    if (local.local.name.StartsWith (_mainCallNo)) {
+                        link.List.Remove (link);
+                        return true;
+                    }
+                }
+
+                // strip out stores to local vars where the var is not read
+                // but convert the value to an OTStmtVoid in case it is a call
+                if (varwr is OTOpndLocal) {
+                    OTOpndLocal local = (OTOpndLocal) varwr;
+                    local.local.nlclreads = 0;
+                    decompile.topBlock.CountRefs ();
+                    if (local.local.nlclreads == 0) {
+                        OTStmt voidstmt = OTStmtVoid.Make (value);
+                        if (voidstmt == null) link.List.Remove (link);
+                                         else link.Value = voidstmt;
+                        return true;
+                    }
+                }
+
+                // strip out bla = newobj HeapTrackerList (...);
+                if (value is OTOpndNewobj) {
+                    OTOpndNewobj valueno = (OTOpndNewobj) value;
+                    if (valueno.ctor.DeclaringType == typeof (HeapTrackerList)) {
+                        link.List.Remove (link);
+                        return true;
+                    }
+                }
+
+                // strip out bla = newobj HeapTrackerObject (...);
+                if (value is OTOpndNewobj) {
+                    OTOpndNewobj valueno = (OTOpndNewobj) value;
+                    if (valueno.ctor.DeclaringType == typeof (HeapTrackerObject)) {
+                        link.List.Remove (link);
+                        return true;
+                    }
+                }
+
+                // strip out bla = newobj HeapTrackerString (...);
+                if (value is OTOpndNewobj) {
+                    OTOpndNewobj valueno = (OTOpndNewobj) value;
+                    if (valueno.ctor.DeclaringType == typeof (HeapTrackerString)) {
+                        link.List.Remove (link);
+                        return true;
+                    }
+                }
+
+                // convert tmp$n = bla bla;
+                //         ....  tmp$n  ....;
+                // to
+                //         ....  bla bla  ....;
+                // gets rid of vast majority of temps
+                if (varwr is OTOpndLocal) {
+                    OTOpndLocal temp = (OTOpndLocal) varwr;
+                    if (temp.local.name.StartsWith ("tmp$")) {
+                        temp.local.nlclreads  = 0;
+                        temp.local.nlclwrites = 0;
+                        decompile.topBlock.CountRefs ();
+                        if ((temp.local.nlclreads == 1) && (temp.local.nlclwrites == 1) && (link.Next != null)) {
+                            OTStmt nextstmt = link.Next.Value;
+                            if (!(nextstmt is OTStmtBlock)) {
+                                if (nextstmt.ReplaceOperand (varwr, value)) {
+                                    link.List.Remove (link);
+                                    return true;
+                                }
+                            }
+                        }
+
+                        // also try to convert:
+                        //    tmp$n = ... asdf ...  << we are here (link)
+                        //    lcl = tmp$n;          << nextstore
+                        //    ... qwer tmp$n ...
+                        //    ... no further references to tmp$n
+                        // to:
+                        //    lcl = ... asdf ...
+                        //    ... qwer lcl ...
+                        if ((temp.local.nlclreads == 2) && (temp.local.nlclwrites == 1) &&
+                                (link.Next != null) && (link.Next.Value is OTStmtStore)) {
+                            OTStmtStore nextstore = (OTStmtStore) link.Next.Value;
+                            if ((nextstore.varwr is OTOpndLocal) && (nextstore.value is OTOpndLocal) && (link.Next.Next != null)) {
+                                OTOpndLocal localopnd = (OTOpndLocal) nextstore.varwr;
+                                OTOpndLocal tempopnd  = (OTOpndLocal) nextstore.value;
+                                if (tempopnd.local == temp.local) {
+                                    OTStmt finalstmt = link.Next.Next.Value;
+                                    if (finalstmt.ReplaceOperand (tempopnd, localopnd)) {
+                                        nextstore.value = value;
+                                        link.List.Remove (link);
+                                        return true;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // convert:
+                //    dup$n = ... asdf ...  << we are here
+                //    lcl = dup$n;
+                //    ... qwer dup$n ...
+                //    ... no further references to dup$n
+                // to:
+                //    lcl = ... asdf ...
+                //    ... qwer lcl ...
+                if ((varwr is OTOpndDup) && (link != null)) {
+                    OTOpndDup vardup = (OTOpndDup) varwr;
+                    LinkedListNode<OTStmt> nextlink = link.Next;
+                    vardup.ndupreads = 0;
+                    decompile.topBlock.CountRefs ();
+                    if ((vardup.ndupreads == 2) && (nextlink != null) && (nextlink.Value is OTStmtStore)) {
+
+                        // point to the supposed lcl = dup$n statement
+                        OTStmtStore nextstore = (OTStmtStore) nextlink.Value;
+                        LinkedListNode<OTStmt> nextlink2 = nextlink.Next;
+                        if ((nextstore.varwr is OTOpndLocal) && (nextstore.value == vardup) && (nextlink2 != null)) {
+
+                            // get the local var being written and point to the ... qwer dup$n ... statement
+                            OTOpndLocal varlcl = (OTOpndLocal) nextstore.varwr;
+                            OTStmt nextstmt2 = nextlink2.Value;
+
+                            // try to replace dup$n in qwer with lcl
+                            if (nextstmt2.ReplaceOperand (vardup, varlcl)) {
+
+                                // successful, replace dup$n in asdf with lcl
+                                // and delete the lcl = dup$n statement
+                                varwr = varlcl;
+                                nextlink.List.Remove (nextlink);
+                                return true;
+                            }
+                        }
+                    }
+                }
+
+                // convert:
+                //    dup$n = ... asdf ...  << we are here
+                //    ... qwer dup$n ...
+                //    ... no further references to dup$n
+                // to:
+                //    ... qwer ... asdf ... ...
+                if ((varwr is OTOpndDup) && (link != null)) {
+                    OTOpndDup vardup = (OTOpndDup) varwr;
+                    LinkedListNode<OTStmt> nextlink = link.Next;
+                    vardup.ndupreads = 0;
+                    decompile.topBlock.CountRefs ();
+                    if ((vardup.ndupreads == 1) && (nextlink != null)) {
+
+                        // point to the ... qwer dup$n ... statement
+                        OTStmt nextstmt = nextlink.Value;
+
+                        // try to replace dup$n in qwer with ... asdf ...
+                        if (nextstmt.ReplaceOperand (vardup, value)) {
+
+                            // successful, delete the dup$n = ... asdf ... statement
+                            link.List.Remove (link);
+                            return true;
+                        }
+                    }
+                }
+
+                // look for list initialization [ ... ]
+                if (OTOpndListIni.Detect (link)) return true;
+
+                // __xmrinst = (XMRInstAbstract) arg$0 indicates this is an event handler
+                // so strip it out and set the flag
+                if ((varwr is OTOpndLocal) && (value is OTOpndCast)) {
+                    OTOpndLocal lcl  = (OTOpndLocal) varwr;
+                    OTOpndCast  cast = (OTOpndCast) value;
+                    if (lcl.local.name.StartsWith (_xmrinstlocal) && (cast.value is OTOpndArg)) {
+                        link.List.Remove (link);
+                        return true;
+                    }
+                }
+
+                // local = [ (optional cast) ] __xmrinst.ehArgs[n] is a definition of event handler arg #n
+                // if found, make it event handler arg list definition
+                OTOpnd valuenocast = value;
+                if (valuenocast is OTOpndCast) valuenocast = ((OTOpndCast) value).value;
+                if ((varwr is OTOpndLocal) && (valuenocast is OTOpndArrayElem)) {
+                    OTOpndArrayElem array = (OTOpndArrayElem) valuenocast;
+                    if ((array.array is OTOpndField) && (array.index is OTOpndInt)) {
+                        OTOpndField arrayfield = (OTOpndField) array.array;
+                        if ((arrayfield.obj is OTOpndLocal) &&
+                                ((OTOpndLocal) arrayfield.obj).local.name.StartsWith (_xmrinstlocal) &&
+                                (arrayfield.field.Name == _ehArgs)) {
+                            int index = ((OTOpndInt) array.index).value;
+                            decompile.eharglist[index] = ((OTOpndLocal) varwr).local;
+                            link.List.Remove (link);
+                            return true;
+                        }
+                    }
+                }
+
+                // __retval$n = ...;  =>  return ...;
+                if (varwr is OTOpndLocal) {
+                    OTOpndLocal lcl = (OTOpndLocal) varwr;
+                    if (lcl.local.name.StartsWith (_retval)) {
+                        link.Value = new OTStmtRet (value);
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                bool rc = false;
+                if (value != null) value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return rc;
+            }
+
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                // print x = x + 1 as x += 1, but don't print x = x < 3 as x <= 3
+                if (value is OTOpndBinOp) {
+                    OTOpndBinOp valuebo = (OTOpndBinOp) value;
+                    if (varwr.SameAs (valuebo.left) && " add and div mul or rem shl shr sub xor ".Contains (' ' + valuebo.opCode.name + ' ')) {
+                        twout.Write (varwr.PrintableString + ' ' + valuebo.opCode.source + "= " + valuebo.rite.PrintableString + ';');
+                        return;
+                    }
+                }
+
+                twout.Write (varwr.PrintableString + " = " + value.PrintableString + ';');
+            }
+        }
+
+        /**
+         * Dispatch to a table of labels.
+         */
+        private class OTStmtSwitch : OTStmt {
+            private OTOpnd index;
+            private OTLabel[] labels;
+
+            public OTStmtSwitch (OTOpnd index, OTLabel[] labels)
+            {
+                this.index  = index;
+                this.labels = labels;
+            }
+
+            public override void CountRefs ()
+            {
+                index.CountRefs (false);
+                foreach (OTLabel label in labels) {
+                    label.lbljumps ++;
+                }
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                bool rc = false;
+                if (index != null) index = index.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return rc;
+            }
+
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                twout.Write ("switch (" + index.PrintableString + ") {\n");
+                for (int i = 0; i < labels.Length; i ++) {
+                    twout.Write (indent + INDENT + "case " + i + ": jump " + labels[i].name + ";\n");
+                }
+                twout.Write (indent + '}');
+            }
+        }
+
+        /**
+         * Throw an exception.
+         */
+        private class OTStmtThrow : OTStmt {
+            private OTOpnd value;
+            private OTDecompile decompile;
+
+            public OTStmtThrow (OTOpnd value, OTDecompile decompile)
+            {
+                this.value = value;
+                this.decompile = decompile;
+            }
+
+            public override void CountRefs ()
+            {
+                value.CountRefs (false);
+            }
+
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                return StripStuffForTerminal (link);
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                bool rc = false;
+                if (value != null) value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return rc;
+            }
+
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                // throw newobj ScriptUndefinedStateException ("x")  =>  state x
+                if (value is OTOpndNewobj) {
+                    OTOpndNewobj valueno = (OTOpndNewobj) value;
+                    if ((valueno.ctor.DeclaringType == typeof (ScriptUndefinedStateException)) &&
+                        (valueno.args.Length == 1) && (valueno.args[0] is OTOpndString)) {
+                        OTOpndString arg0 = (OTOpndString) valueno.args[0];
+                        twout.Write ("state " + arg0.value + ";  /* throws undefined state exception */");
+                        return;
+                    }
+                }
+
+                // throw newobj ScriptChangeStateException (n)  =>  state n
+                if (value is OTOpndNewobj) {
+                    OTOpndNewobj valueno = (OTOpndNewobj) value;
+                    if ((valueno.ctor.DeclaringType == typeof (ScriptChangeStateException)) &&
+                        (valueno.args.Length == 1) && (valueno.args[0] is OTOpndInt)) {
+                        OTOpndInt arg0 = (OTOpndInt) valueno.args[0];
+                        twout.Write ("state " + decompile.scriptObjCode.stateNames[arg0.value] + ';');
+                        return;
+                    }
+                }
+
+                // throwing something else, output as is
+                twout.Write ("throw " + value.PrintableString + ';');
+            }
+        }
+
+        /**
+         * Call with void return, or really anything that we discard the value of after computing it.
+         */
+        private class OTStmtVoid : OTStmt {
+            private OTOpnd value;
+
+            public static void AddLast (OTDecompile decompile, OTOpnd value)
+            {
+                OTStmt it = OTStmtVoid.Make (value);
+                if (it != null) decompile.AddLastStmt (it);
+            }
+
+            public static OTStmt Make (OTOpnd value)
+            {
+                if (!value.HasSideEffects) return null;
+                OTStmtVoid it = new OTStmtVoid ();
+                it.value = value;
+                return it;
+            }
+
+            private OTStmtVoid () { }
+
+            public override void CountRefs ()
+            {
+                value.CountRefs (false);
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                bool rc = false;
+                value = value.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return rc;
+            }
+
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                // strip out calls to CheckRunQuick() and CheckRunStack()
+                if (value is OTOpndCall) {
+                    OTOpndCall call = (OTOpndCall) value;
+                    MethodInfo method = call.method;
+                    if ((method.Name == _checkRunQuick) || (method.Name == _checkRunStack)) {
+                        link.List.Remove (link);
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                twout.Write (value.PrintableString + ';');
+            }
+        }
+
+        /***************************\
+         *  Structured statements  *
+        \***************************/
+
+        /**
+         * Block of statements.
+         */
+        private class OTStmtBlock : OTStmt {
+            public LinkedList<OTStmt> blkstmts = new LinkedList<OTStmt> ();
+
+            public override void CountRefs ()
+            {
+                foreach (OTStmt stmt in blkstmts) {
+                    stmt.CountRefs ();
+                }
+            }
+
+            /**
+             * Scrub out all references to behind-the-scenes parts and simplify.
+             */
+            public override bool StripStuff (LinkedListNode<OTStmt> link)
+            {
+                // loop through all sub-statements to strip out behind-the-scenes references
+                bool rc = false;
+            loop:
+                for (LinkedListNode<OTStmt> stmtlink = blkstmts.First; stmtlink != null; stmtlink = stmtlink.Next) {
+                    if (stmtlink.Value.StripStuff (stmtlink)) {
+                        rc = true;
+                        goto loop;
+                    }
+                }
+                if (rc) return true;
+
+                // try to merge this block into outer block
+                // change:
+                //   {
+                //       ...
+                //       {          << link points here
+                //           ...
+                //       }
+                //       ...
+                //   }
+                // to:
+                //   {
+                //       ...
+                //       ...
+                //       ...
+                //   }
+                if (link != null) {
+                    LinkedListNode<OTStmt> nextlink;
+                    while ((nextlink = blkstmts.Last) != null) {
+                        nextlink.List.Remove (nextlink);
+                        link.List.AddAfter (link, nextlink);
+                    }
+                    link.List.Remove (link);
+                    return true;
+                }
+
+                return rc;
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                bool rc = false;
+                foreach (OTStmt stmt in blkstmts) {
+                    rc |= stmt.ReplaceOperand (oldopnd, newopnd);
+                }
+                return rc;
+            }
+
+            /**
+             * Check each statement in the block to see if it starts a do/for/if/while statement.
+             */
+            public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
+            {
+                bool rc = false;
+            loop:
+                for (link = blkstmts.First; link != null; link = link.Next) {
+                    if (link.Value.DetectDoForIfWhile (link)) {
+                        rc = true;
+                        goto loop;
+                    }
+                }
+                return rc;
+            }
+
+            /**
+             * Assume we will never try to replace the block itself.
+             * But go through all our sub-ordinates statements.
+             */
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                for (LinkedListNode<OTStmt> childlink = blkstmts.First; childlink != null; childlink = childlink.Next) {
+                    childlink.Value = childlink.Value.ReplaceStatement (oldstmt, newstmt);
+                }
+                return this;
+            }
+
+            /**
+             * Print out the block including its enclosed statements.
+             */
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                switch (blkstmts.Count) {
+                    case 0: {
+                        twout.Write ("{ }");
+                        break;
+                    }
+                    ////case 1: {
+                    ////    blkstmts.First.Value.PrintStmt (twout, indent);
+                    ////    break;
+                    ////}
+                    default: {
+                        twout.Write ('{');
+                        PrintBodyAndEnd (twout, indent);
+                        break;
+                    }
+                }
+            }
+
+            public void PrintBodyAndEnd (TextWriter twout, string indent)
+            {
+                string newindent = indent + INDENT;
+                foreach (OTStmt stmt in blkstmts) {
+                    twout.Write ('\n' + indent);
+                    if (!(stmt is OTStmtLabel)) twout.Write (INDENT);
+                                      else twout.Write (LABELINDENT);
+                    stmt.PrintStmt (twout, newindent);
+                }
+                twout.Write ('\n' + indent + '}');
+            }
+        }
+
+        /**
+         * 'do' statement.
+         */
+        private class OTStmtDo : OTStmt {
+            private OTOpnd      dotest;
+            private OTStmtBlock dobody;
+
+            /**
+             * See if we have a do loop...
+             *   @doloop_<suffix>;     << link points here
+             *     ... <dobody> ...
+             *     [ if (dotest) ] jump doloop_<suffix>;
+             */
+            public static bool Detect (LinkedListNode<OTStmt> link)
+            {
+                // see if we have label starting with 'doloop_'
+                OTLabel looplabel = ((OTStmtLabel) link.Value).label;
+                if (!looplabel.name.StartsWith (_doLoop)) return false;
+
+                // good chance we have a do loop
+                OTStmtDo it = new OTStmtDo ();
+
+                // scan ahead looking for the terminating cond/jump loop
+                // also gather up the statements for the do body block
+                it.dobody = new OTStmtBlock ();
+                LinkedListNode<OTStmt> nextlink;
+                for (nextlink = link.Next; nextlink != null; nextlink = nextlink.Next) {
+                    OTStmt nextstmt = nextlink.Value;
+
+                    // add statement to do body
+                    it.dobody.blkstmts.AddLast (nextlink.Value);
+
+                    // check for something what jumps to loop label
+                    // that gives us the end of the loop
+                    OTStmt maybejump = nextstmt;
+                    if (nextstmt is OTStmtCond) {
+                        maybejump = ((OTStmtCond) nextstmt).stmt;
+                    }
+                    if ((maybejump is OTStmtJump) && (((OTStmtJump) maybejump).label == looplabel)) {
+                        break;
+                    }
+                }
+
+                // make sure we found the jump back to the loop label
+                if (nextlink == null) return false;
+
+                // remove all statements from caller's block including the continue label if any
+                // but leave the break label alone it will be removed later if unreferenced
+                // and leave the initial loop label intact for now
+                for (LinkedListNode<OTStmt> remlink = null; (remlink = link.Next) != null;) {
+                    link.List.Remove (remlink);
+                    if (remlink == nextlink) break;
+                }
+
+                // take test condition from last statement of body
+                // it should be an cond/jump or just a jump to the loop label
+                LinkedListNode<OTStmt> lastlink = it.dobody.blkstmts.Last;
+                OTStmt laststmt = lastlink.Value;
+                if (laststmt is OTStmtCond) {
+                    it.dotest = ((OTStmtCond) laststmt).valu;
+                } else {
+                    it.dotest = new OTOpndInt (1);
+                }
+                lastlink.List.Remove (lastlink);
+
+                // finally replace the loop label with the whole do statement
+                link.Value = it;
+
+                // tell caller we made a change
+                return true;
+            }
+
+            public override void CountRefs ()
+            {
+                if (dotest != null) dotest.CountRefs (false);
+                if (dobody != null) dobody.CountRefs ();
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                return dobody.ReplaceOperand (oldopnd, newopnd);
+            }
+
+            public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
+            {
+                return dobody.DetectDoForIfWhile (link);
+            }
+
+            /**
+             * Assume we won't replace the do statement itself.
+             * But search all our sub-ordinate statements.
+             */
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                dobody = (OTStmtBlock) dobody.ReplaceStatement (oldstmt, newstmt);
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                // output do body
+                twout.Write ("do ");
+                dobody.PrintStmt (twout, indent);
+
+                // output while part
+                twout.Write (" while (" + StripBrtrue (dotest).PrintableString + ");");
+            }
+        }
+
+        /**
+         * 'for' or 'while' statement.
+         */
+        private class OTStmtFor : OTStmt {
+            private bool        iswhile;
+            private OTOpnd      fortest;
+            private OTStmtBlock forbody;
+            private OTStmt      forinit;
+            private OTStmt      forstep;
+
+            /**
+             * See if we have a for or while loop...
+             *     <forinit>
+             *   @forloop_<suffix>;     << link points here
+             *     [ if (<fortest>) jump forbreak_<suffix>; ]
+             *     ... <forbody> ...
+             *     jump forloop_<suffix>;
+             *   [ @forbreak_<suffix>; ]
+             */
+            public static bool Detect (LinkedListNode<OTStmt> link, bool iswhile)
+            {
+                string loopname  = iswhile ? _whileLoop  : _forLoop;
+                string breakname = iswhile ? _whileBreak : _forBreak;
+
+                // see if we have label starting with 'forloop_'
+                OTLabel looplabel = ((OTStmtLabel) link.Value).label;
+                if (!looplabel.name.StartsWith (loopname)) return false;
+
+                // good chance we have a for loop
+                OTStmtFor it = new OTStmtFor ();
+                it.iswhile = iswhile;
+
+                // all labels end with this suffix
+                string suffix = looplabel.name.Substring (loopname.Length);
+
+                // scan ahead looking for the 'jump forloop_<suffix>;' statement
+                // also gather up the statements for the for body block
+                it.forbody = new OTStmtBlock ();
+                LinkedListNode<OTStmt> lastlink;
+                for (lastlink = link; (lastlink = lastlink.Next) != null;) {
+
+                    // check for jump forloop that tells us where loop ends
+                    if (lastlink.Value is OTStmtJump) {
+                        OTStmtJump lastjump = (OTStmtJump) lastlink.Value;
+                        if (lastjump.label == looplabel) break;
+                    }
+
+                    // add to body block
+                    it.forbody.blkstmts.AddLast (lastlink.Value);
+                }
+
+                // make sure we found the 'jump forloop' where the for loop ends
+                if (lastlink == null) return false;
+
+                // remove all statements from caller's block including final jump
+                // but leave the loop label in place
+                for (LinkedListNode<OTStmt> nextlink = null; (nextlink = link.Next) != null;) {
+                    link.List.Remove (nextlink);
+                    if (nextlink == lastlink) break;
+                }
+
+                // if statement before loop label is an assignment, use it for the init statement
+                if (!iswhile && (link.Previous != null) && (link.Previous.Value is OTStmtStore)) {
+                    it.forinit = link.Previous.Value;
+                    link.List.Remove (link.Previous);
+                }
+
+                // if first statement of for body is 'if (...) jump breaklabel' use it for the test value
+                if ((it.forbody.blkstmts.First != null) && (it.forbody.blkstmts.First.Value is OTStmtCond)) {
+                    OTStmtCond condstmt = (OTStmtCond) it.forbody.blkstmts.First.Value;
+                    if ((condstmt.stmt is OTStmtJump) && (((OTStmtJump) condstmt.stmt).label.name == breakname + suffix)) {
+                        it.fortest = OTOpndUnOp.Make (MyOp.Brfalse, condstmt.valu);
+                        it.forbody.blkstmts.RemoveFirst ();
+                    }
+                }
+
+                // if last statement of body is an assigment,
+                // use the assignment as the step statement
+                if (!iswhile && (it.forbody.blkstmts.Last != null) &&
+                        (it.forbody.blkstmts.Last.Value is OTStmtStore)) {
+                    LinkedListNode<OTStmt> storelink = it.forbody.blkstmts.Last;
+                    storelink.List.Remove (storelink);
+                    it.forstep = storelink.Value;
+                }
+
+                // finally replace the loop label with the whole for statement
+                link.Value = it;
+
+                // tell caller we made a change
+                return true;
+            }
+
+            public override void CountRefs ()
+            {
+                if (fortest != null) fortest.CountRefs (false);
+                if (forbody != null) forbody.CountRefs ();
+                if (forinit != null) forinit.CountRefs ();
+                if (forstep != null) forstep.CountRefs ();
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                return forbody.ReplaceOperand (oldopnd, newopnd) |
+                        ((forinit != null) && forinit.ReplaceOperand (oldopnd, newopnd)) |
+                        ((forstep != null) && forstep.ReplaceOperand (oldopnd, newopnd));
+            }
+
+            public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
+            {
+                return forbody.DetectDoForIfWhile (link) |
+                            ((forinit != null) && forinit.DetectDoForIfWhile (link)) |
+                            ((forstep != null) && forstep.DetectDoForIfWhile (link));
+            }
+
+            /**
+             * Assume we won't replace the for statement itself.
+             * But search all our sub-ordinate statements.
+             */
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                forbody = (OTStmtBlock) forbody.ReplaceStatement (oldstmt, newstmt);
+                if (forinit != null) forinit = forinit.ReplaceStatement (oldstmt, newstmt);
+                if (forstep != null) forstep = forstep.ReplaceStatement (oldstmt, newstmt);
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                if (iswhile) {
+                    twout.Write ("while (");
+                    if (fortest == null) {
+                        twout.Write ("TRUE");
+                    } else {
+                        twout.Write (StripBrtrue (fortest).PrintableString);
+                    }
+                } else {
+                    twout.Write ("for (");
+                    if (forinit != null) {
+                        forinit.PrintStmt (twout, indent + INDENT);
+                    } else {
+                        twout.Write (';');
+                    }
+                    if (fortest != null) {
+                        twout.Write (' ' + StripBrtrue (fortest).PrintableString);
+                    }
+                    twout.Write (';');
+                    if (forstep != null) {
+                        StringWriter sw = new StringWriter ();
+                        sw.Write (' ');
+                        forstep.PrintStmt (sw, indent + INDENT);
+                        StringBuilder sb = sw.GetStringBuilder ();
+                        int sl = sb.Length;
+                        if ((sl > 0) && (sb[sl-1] == ';')) sb.Remove (-- sl, 1);
+                        twout.Write (sb.ToString ());
+                    }
+                }
+
+                twout.Write (") ");
+                forbody.PrintStmt (twout, indent);
+            }
+        }
+
+        /**
+         * if/then/else block.
+         */
+        private class OTStmtIf : OTStmt {
+            private OTOpnd testvalu;
+            private OTStmt thenstmt;
+            private OTStmt elsestmt;  // might be null
+
+            /**
+             * Try to detect a structured if statement.
+             *
+             *   if (condition) jump ifdone_<suffix>;   << link points here
+             *      ... then body ...
+             * @ifdone_<suffix>;
+             *
+             *   if (condition) jump ifelse_<suffix>;
+             *      ... then body ...
+             *   jump ifdone_<suffix>;  << optional if true body doesn't fall through
+             * @ifelse_<suffix>;
+             *      ... else body ...
+             * @ifdone_<suffix>;
+             */
+            public static bool Detect (LinkedListNode<OTStmt> link)
+            {
+                OTStmtCond condstmt = (OTStmtCond) link.Value;
+                if (!(condstmt.stmt is OTStmtJump)) return false;
+
+                OTStmtJump jumpstmt = (OTStmtJump) condstmt.stmt;
+                if (jumpstmt.label.name.StartsWith (_ifDone)) {
+
+                    // then-only if
+
+                    // skip forward to find the ifdone_<suffix> label
+                    // also save the intervening statements for the then body
+                    OTStmtBlock thenbody;
+                    LinkedListNode<OTStmt> donelink = ScanForLabel (link, jumpstmt.label, out thenbody);
+
+                    // make sure we found matching label
+                    if (donelink == null) return false;
+
+                    // replace the jump ifdone_<suffix> with the <then body>
+                    OTStmtIf it = new OTStmtIf ();
+                    it.thenstmt = thenbody;
+
+                    // replace the test value with the opposite
+                    it.testvalu = OTOpndUnOp.Make (MyOp.Brfalse, condstmt.valu);
+                    condstmt.valu = null;
+
+                    // strip out the true body statements from the main code including the ifdone_<suffix> label
+                    StripInterveningStatements (link, donelink);
+
+                    // replace the simple conditional with the if/then/else block
+                    link.Value = it;
+
+                    // tell caller we changed something
+                    return true;
+                }
+
+                if (jumpstmt.label.name.StartsWith (_ifElse)) {
+                    string suffix = jumpstmt.label.name.Substring (_ifElse.Length);
+
+                    // if/then/else
+                    OTStmtIf it = new OTStmtIf ();
+
+                    // skip forward to find the ifelse_<suffix> label
+                    // also save the intervening statements for the true body
+                    OTStmtBlock thenbody;
+                    LinkedListNode<OTStmt> elselink = ScanForLabel (link, jumpstmt.label, out thenbody);
+
+                    // make sure we found matching label
+                    if (elselink != null) {
+
+                        // the last statement of the then body might be a jump ifdone_<suffix>
+                        LinkedListNode<OTStmt> lastthenlink = thenbody.blkstmts.Last;
+                        if ((lastthenlink != null) && (lastthenlink.Value is OTStmtJump)) {
+                            OTStmtJump jumpifdone = (OTStmtJump) lastthenlink.Value;
+                            if (jumpifdone.label.name == _ifDone + suffix) {
+
+                                lastthenlink.List.Remove (lastthenlink);
+
+                                // skip forward to find the ifdone_<suffix> label
+                                // also save the intervening statements for the else body
+                                OTStmtBlock elsebody;
+                                LinkedListNode<OTStmt> donelink = ScanForLabel (elselink, jumpifdone.label, out elsebody);
+                                if (donelink != null) {
+
+                                    // replace the jump ifdone_<suffix> with the <true body>
+                                    it.thenstmt = thenbody;
+
+                                    // save the else body as well
+                                    it.elsestmt = elsebody;
+
+                                    // replace the test value with the opposite
+                                    it.testvalu = OTOpndUnOp.Make (MyOp.Brfalse, condstmt.valu);
+                                    condstmt.valu = null;
+
+                                    // strip out the true and else body statements from the main code including the ifdone_<suffix> label
+                                    StripInterveningStatements (link, donelink);
+
+                                    // replace the simple conditional with the if/then/else block
+                                    link.Value = it;
+
+                                    // tell caller we changed something
+                                    return true;
+                                }
+                            }
+                        }
+
+                        // missing the jump _ifDone_<suffix>, so make it a simple if/then
+                        //   if (condition) jump ifelse_<suffix>;   << link
+                        //      ... then body ...                   << encapsulated in block thenbody
+                        // @ifelse_<suffix>;                        << elselink
+                        //      ... else body ...                   << still inline and leave it there
+                        // @ifdone_<suffix>;                        << strip this out
+
+                        // replace the jump ifelse_<suffix> with the <true body>
+                        it.thenstmt = thenbody;
+
+                        // replace the test value with the opposite
+                        it.testvalu = OTOpndUnOp.Make (MyOp.Brfalse, condstmt.valu);
+                        condstmt.valu = null;
+
+                        // strip out the then body statements from the main code including the ifelse_<suffix> label
+                        StripInterveningStatements (link, elselink);
+
+                        // there's a dangling unused ifdone_<suffix> label ahead that has to be stripped
+                        for (LinkedListNode<OTStmt> donelink = link; (donelink = donelink.Next) != null;) {
+                            if ((donelink.Value is OTStmtLabel) && (((OTStmtLabel) donelink.Value).label.name == _ifDone + suffix)) {
+                                donelink.List.Remove (donelink);
+                                break;
+                            }
+                        }
+
+                        // replace the simple conditional with the if/then/else block
+                        link.Value = it;
+
+                        // tell caller we changed something
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            private OTStmtIf () { }
+
+            public override void CountRefs ()
+            {
+                if (testvalu != null) testvalu.CountRefs (false);
+                if (thenstmt != null) thenstmt.CountRefs ();
+                if (elsestmt != null) elsestmt.CountRefs ();
+            }
+
+            public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd)
+            {
+                bool rc = thenstmt.ReplaceOperand (oldopnd, newopnd);
+                testvalu = testvalu.ReplaceOperand (oldopnd, newopnd, ref rc);
+                return rc;
+            }
+
+            public override bool DetectDoForIfWhile (LinkedListNode<OTStmt> link)
+            {
+                return ((thenstmt != null) && thenstmt.DetectDoForIfWhile (link)) |
+                       ((elsestmt != null) && elsestmt.DetectDoForIfWhile (link));
+            }
+
+            /**
+             * Assume we won't replace the if statement itself.
+             * But search all our sub-ordinate statements.
+             */
+            public override OTStmt ReplaceStatement (OTStmt oldstmt, OTStmt newstmt)
+            {
+                thenstmt = thenstmt.ReplaceStatement (oldstmt, newstmt);
+                if (elsestmt != null) elsestmt = elsestmt.ReplaceStatement (oldstmt, newstmt);
+                return this;
+            }
+
+            public override void PrintStmt (TextWriter twout, string indent)
+            {
+                twout.Write ("if (" + StripBrtrue (testvalu).PrintableString + ") ");
+                OTStmt thenst = ReduceStmtBody (thenstmt, false);
+                thenst.PrintStmt (twout, indent);
+                if (elsestmt != null) {
+                    twout.Write ('\n' + indent + "else ");
+                    OTStmt elsest = ReduceStmtBody (elsestmt, true);
+                    elsest.PrintStmt (twout, indent);
+                }
+            }
+
+            // strip block off a single jump so it prints inline instead of with braces around it
+            // also, if this is part of else, strip block for ifs to make else if statement
+            private static OTStmt ReduceStmtBody (OTStmt statement, bool stripif)
+            {
+                OTStmt onestmt = statement;
+                if ((onestmt is OTStmtBlock) && (((OTStmtBlock) onestmt).blkstmts.Count == 1)) {
+                    onestmt = ((OTStmtBlock) onestmt).blkstmts.First.Value;
+                    if ((onestmt is OTStmtJump) || (stripif && (onestmt is OTStmtIf))) {
+                        return onestmt;
+                    }
+                }
+                return statement;
+            }
+
+            /**
+             * Scan forward for a given label definition.
+             * Put intervening statements in a statement block.
+             * @param link = start scanning after this statement
+             * @param label = look for this label definition
+             * @param block = where to return intervening statement block
+             * @returns null: label definition not found
+             *          else: label definition statement
+             */
+            private static LinkedListNode<OTStmt> ScanForLabel (LinkedListNode<OTStmt> link,
+                        OTLabel label, out OTStmtBlock block)
+            {
+                block = new OTStmtBlock ();
+                while ((link = link.Next) != null) {
+                    if (link.Value is OTStmtLabel) {
+                        if (((OTStmtLabel) link.Value).label == label) break;
+                    }
+                    block.blkstmts.AddLast (link.Value);
+                }
+                return link;
+            }
+
+            /**
+             * Strip statements after link up to and including donelink.
+             */
+            private static void StripInterveningStatements (LinkedListNode<OTStmt> link, LinkedListNode<OTStmt> donelink)
+            {
+                LinkedListNode<OTStmt> striplink;
+                do {
+                    striplink = link.Next;
+                    striplink.List.Remove (striplink);
+                } while (striplink != donelink);
+            }
+        }
+
+        private class MyOp {
+            public int index;
+            public OpCode sysop;
+            public string name;
+            public string source;
+
+            private static Dictionary<string,MyOp> myopsbyname = new Dictionary<string,MyOp> ();
+            private static int nextindex = 0;
+
+            public MyOp (OpCode sysop)
+            {
+                this.index = nextindex ++;
+                this.sysop = sysop;
+                this.name  = sysop.Name;
+                myopsbyname.Add (name, this);
+            }
+
+            public MyOp (OpCode sysop, string source)
+            {
+                this.index  = nextindex ++;
+                this.sysop  = sysop;
+                this.name   = sysop.Name;
+                this.source = source;
+                myopsbyname.Add (name, this);
+            }
+
+            public MyOp (string name)
+            {
+                this.index = nextindex ++;
+                this.name  = name;
+                myopsbyname.Add (name, this);
+            }
+
+            public MyOp (string name, string source)
+            {
+                this.index  = nextindex ++;
+                this.name   = name;
+                this.source = source;
+                myopsbyname.Add (name, this);
+            }
+
+            public static MyOp GetByName (string name)
+            {
+                return myopsbyname[name];
+            }
+
+            public override string ToString ()
+            {
+                return name;
+            }
+
+            // these copied from OpCodes.cs
+            public static readonly MyOp Nop            = new MyOp (OpCodes.Nop);
+            public static readonly MyOp Break          = new MyOp (OpCodes.Break);
+            public static readonly MyOp Ldarg_0        = new MyOp (OpCodes.Ldarg_0);
+            public static readonly MyOp Ldarg_1        = new MyOp (OpCodes.Ldarg_1);
+            public static readonly MyOp Ldarg_2        = new MyOp (OpCodes.Ldarg_2);
+            public static readonly MyOp Ldarg_3        = new MyOp (OpCodes.Ldarg_3);
+            public static readonly MyOp Ldloc_0        = new MyOp (OpCodes.Ldloc_0);
+            public static readonly MyOp Ldloc_1        = new MyOp (OpCodes.Ldloc_1);
+            public static readonly MyOp Ldloc_2        = new MyOp (OpCodes.Ldloc_2);
+            public static readonly MyOp Ldloc_3        = new MyOp (OpCodes.Ldloc_3);
+            public static readonly MyOp Stloc_0        = new MyOp (OpCodes.Stloc_0);
+            public static readonly MyOp Stloc_1        = new MyOp (OpCodes.Stloc_1);
+            public static readonly MyOp Stloc_2        = new MyOp (OpCodes.Stloc_2);
+            public static readonly MyOp Stloc_3        = new MyOp (OpCodes.Stloc_3);
+            public static readonly MyOp Ldarg_S        = new MyOp (OpCodes.Ldarg_S);
+            public static readonly MyOp Ldarga_S       = new MyOp (OpCodes.Ldarga_S);
+            public static readonly MyOp Starg_S        = new MyOp (OpCodes.Starg_S);
+            public static readonly MyOp Ldloc_S        = new MyOp (OpCodes.Ldloc_S);
+            public static readonly MyOp Ldloca_S       = new MyOp (OpCodes.Ldloca_S);
+            public static readonly MyOp Stloc_S        = new MyOp (OpCodes.Stloc_S);
+            public static readonly MyOp Ldnull         = new MyOp (OpCodes.Ldnull);
+            public static readonly MyOp Ldc_I4_M1      = new MyOp (OpCodes.Ldc_I4_M1);
+            public static readonly MyOp Ldc_I4_0       = new MyOp (OpCodes.Ldc_I4_0);
+            public static readonly MyOp Ldc_I4_1       = new MyOp (OpCodes.Ldc_I4_1);
+            public static readonly MyOp Ldc_I4_2       = new MyOp (OpCodes.Ldc_I4_2);
+            public static readonly MyOp Ldc_I4_3       = new MyOp (OpCodes.Ldc_I4_3);
+            public static readonly MyOp Ldc_I4_4       = new MyOp (OpCodes.Ldc_I4_4);
+            public static readonly MyOp Ldc_I4_5       = new MyOp (OpCodes.Ldc_I4_5);
+            public static readonly MyOp Ldc_I4_6       = new MyOp (OpCodes.Ldc_I4_6);
+            public static readonly MyOp Ldc_I4_7       = new MyOp (OpCodes.Ldc_I4_7);
+            public static readonly MyOp Ldc_I4_8       = new MyOp (OpCodes.Ldc_I4_8);
+            public static readonly MyOp Ldc_I4_S       = new MyOp (OpCodes.Ldc_I4_S);
+            public static readonly MyOp Ldc_I4         = new MyOp (OpCodes.Ldc_I4);
+            public static readonly MyOp Ldc_I8         = new MyOp (OpCodes.Ldc_I8);
+            public static readonly MyOp Ldc_R4         = new MyOp (OpCodes.Ldc_R4);
+            public static readonly MyOp Ldc_R8         = new MyOp (OpCodes.Ldc_R8);
+            public static readonly MyOp Dup            = new MyOp (OpCodes.Dup);
+            public static readonly MyOp Pop            = new MyOp (OpCodes.Pop);
+            public static readonly MyOp Jmp            = new MyOp (OpCodes.Jmp);
+            public static readonly MyOp Call           = new MyOp (OpCodes.Call);
+            public static readonly MyOp Calli          = new MyOp (OpCodes.Calli);
+            public static readonly MyOp Ret            = new MyOp (OpCodes.Ret);
+            public static readonly MyOp Br_S           = new MyOp (OpCodes.Br_S);
+            public static readonly MyOp Brfalse_S      = new MyOp (OpCodes.Brfalse_S);
+            public static readonly MyOp Brtrue_S       = new MyOp (OpCodes.Brtrue_S);
+            public static readonly MyOp Beq_S          = new MyOp (OpCodes.Beq_S, "==");
+            public static readonly MyOp Bge_S          = new MyOp (OpCodes.Bge_S, ">=");
+            public static readonly MyOp Bgt_S          = new MyOp (OpCodes.Bgt_S, ">");
+            public static readonly MyOp Ble_S          = new MyOp (OpCodes.Ble_S, "<=");
+            public static readonly MyOp Blt_S          = new MyOp (OpCodes.Blt_S, "<");
+            public static readonly MyOp Bne_Un_S       = new MyOp (OpCodes.Bne_Un_S, "!=");
+            public static readonly MyOp Bge_Un_S       = new MyOp (OpCodes.Bge_Un_S);
+            public static readonly MyOp Bgt_Un_S       = new MyOp (OpCodes.Bgt_Un_S);
+            public static readonly MyOp Ble_Un_S       = new MyOp (OpCodes.Ble_Un_S);
+            public static readonly MyOp Blt_Un_S       = new MyOp (OpCodes.Blt_Un_S);
+            public static readonly MyOp Br             = new MyOp (OpCodes.Br);
+            public static readonly MyOp Brfalse        = new MyOp (OpCodes.Brfalse, "!");
+            public static readonly MyOp Brtrue         = new MyOp (OpCodes.Brtrue,  "!!");
+            public static readonly MyOp Beq            = new MyOp (OpCodes.Beq, "==");
+            public static readonly MyOp Bge            = new MyOp (OpCodes.Bge, ">=");
+            public static readonly MyOp Bgt            = new MyOp (OpCodes.Bgt, ">");
+            public static readonly MyOp Ble            = new MyOp (OpCodes.Ble, "<=");
+            public static readonly MyOp Blt            = new MyOp (OpCodes.Blt, "<");
+            public static readonly MyOp Bne_Un         = new MyOp (OpCodes.Bne_Un, "!=");
+            public static readonly MyOp Bge_Un         = new MyOp (OpCodes.Bge_Un);
+            public static readonly MyOp Bgt_Un         = new MyOp (OpCodes.Bgt_Un);
+            public static readonly MyOp Ble_Un         = new MyOp (OpCodes.Ble_Un);
+            public static readonly MyOp Blt_Un         = new MyOp (OpCodes.Blt_Un);
+            public static readonly MyOp Switch         = new MyOp (OpCodes.Switch);
+            public static readonly MyOp Ldind_I1       = new MyOp (OpCodes.Ldind_I1);
+            public static readonly MyOp Ldind_U1       = new MyOp (OpCodes.Ldind_U1);
+            public static readonly MyOp Ldind_I2       = new MyOp (OpCodes.Ldind_I2);
+            public static readonly MyOp Ldind_U2       = new MyOp (OpCodes.Ldind_U2);
+            public static readonly MyOp Ldind_I4       = new MyOp (OpCodes.Ldind_I4);
+            public static readonly MyOp Ldind_U4       = new MyOp (OpCodes.Ldind_U4);
+            public static readonly MyOp Ldind_I8       = new MyOp (OpCodes.Ldind_I8);
+            public static readonly MyOp Ldind_I        = new MyOp (OpCodes.Ldind_I);
+            public static readonly MyOp Ldind_R4       = new MyOp (OpCodes.Ldind_R4);
+            public static readonly MyOp Ldind_R8       = new MyOp (OpCodes.Ldind_R8);
+            public static readonly MyOp Ldind_Ref      = new MyOp (OpCodes.Ldind_Ref);
+            public static readonly MyOp Stind_Ref      = new MyOp (OpCodes.Stind_Ref);
+            public static readonly MyOp Stind_I1       = new MyOp (OpCodes.Stind_I1);
+            public static readonly MyOp Stind_I2       = new MyOp (OpCodes.Stind_I2);
+            public static readonly MyOp Stind_I4       = new MyOp (OpCodes.Stind_I4);
+            public static readonly MyOp Stind_I8       = new MyOp (OpCodes.Stind_I8);
+            public static readonly MyOp Stind_R4       = new MyOp (OpCodes.Stind_R4);
+            public static readonly MyOp Stind_R8       = new MyOp (OpCodes.Stind_R8);
+            public static readonly MyOp Add            = new MyOp (OpCodes.Add, "+");
+            public static readonly MyOp Sub            = new MyOp (OpCodes.Sub, "-");
+            public static readonly MyOp Mul            = new MyOp (OpCodes.Mul, "*");
+            public static readonly MyOp Div            = new MyOp (OpCodes.Div, "/");
+            public static readonly MyOp Div_Un         = new MyOp (OpCodes.Div_Un);
+            public static readonly MyOp Rem            = new MyOp (OpCodes.Rem, "%");
+            public static readonly MyOp Rem_Un         = new MyOp (OpCodes.Rem_Un);
+            public static readonly MyOp And            = new MyOp (OpCodes.And, "&");
+            public static readonly MyOp Or             = new MyOp (OpCodes.Or, "|");
+            public static readonly MyOp Xor            = new MyOp (OpCodes.Xor, "^");
+            public static readonly MyOp Shl            = new MyOp (OpCodes.Shl, "<<");
+            public static readonly MyOp Shr            = new MyOp (OpCodes.Shr, ">>");
+            public static readonly MyOp Shr_Un         = new MyOp (OpCodes.Shr_Un);
+            public static readonly MyOp Neg            = new MyOp (OpCodes.Neg, "-");
+            public static readonly MyOp Not            = new MyOp (OpCodes.Not, "~");
+            public static readonly MyOp Conv_I1        = new MyOp (OpCodes.Conv_I1);
+            public static readonly MyOp Conv_I2        = new MyOp (OpCodes.Conv_I2);
+            public static readonly MyOp Conv_I4        = new MyOp (OpCodes.Conv_I4);
+            public static readonly MyOp Conv_I8        = new MyOp (OpCodes.Conv_I8);
+            public static readonly MyOp Conv_R4        = new MyOp (OpCodes.Conv_R4);
+            public static readonly MyOp Conv_R8        = new MyOp (OpCodes.Conv_R8);
+            public static readonly MyOp Conv_U4        = new MyOp (OpCodes.Conv_U4);
+            public static readonly MyOp Conv_U8        = new MyOp (OpCodes.Conv_U8);
+            public static readonly MyOp Callvirt       = new MyOp (OpCodes.Callvirt);
+            public static readonly MyOp Cpobj          = new MyOp (OpCodes.Cpobj);
+            public static readonly MyOp Ldobj          = new MyOp (OpCodes.Ldobj);
+            public static readonly MyOp Ldstr          = new MyOp (OpCodes.Ldstr);
+            public static readonly MyOp Newobj         = new MyOp (OpCodes.Newobj);
+            public static readonly MyOp Castclass      = new MyOp (OpCodes.Castclass);
+            public static readonly MyOp Isinst         = new MyOp (OpCodes.Isinst);
+            public static readonly MyOp Conv_R_Un      = new MyOp (OpCodes.Conv_R_Un);
+            public static readonly MyOp Unbox          = new MyOp (OpCodes.Unbox);
+            public static readonly MyOp Throw          = new MyOp (OpCodes.Throw);
+            public static readonly MyOp Ldfld          = new MyOp (OpCodes.Ldfld);
+            public static readonly MyOp Ldflda         = new MyOp (OpCodes.Ldflda);
+            public static readonly MyOp Stfld          = new MyOp (OpCodes.Stfld);
+            public static readonly MyOp Ldsfld         = new MyOp (OpCodes.Ldsfld);
+            public static readonly MyOp Ldsflda        = new MyOp (OpCodes.Ldsflda);
+            public static readonly MyOp Stsfld         = new MyOp (OpCodes.Stsfld);
+            public static readonly MyOp Stobj          = new MyOp (OpCodes.Stobj);
+            public static readonly MyOp Conv_Ovf_I1_Un = new MyOp (OpCodes.Conv_Ovf_I1_Un);
+            public static readonly MyOp Conv_Ovf_I2_Un = new MyOp (OpCodes.Conv_Ovf_I2_Un);
+            public static readonly MyOp Conv_Ovf_I4_Un = new MyOp (OpCodes.Conv_Ovf_I4_Un);
+            public static readonly MyOp Conv_Ovf_I8_Un = new MyOp (OpCodes.Conv_Ovf_I8_Un);
+            public static readonly MyOp Conv_Ovf_U1_Un = new MyOp (OpCodes.Conv_Ovf_U1_Un);
+            public static readonly MyOp Conv_Ovf_U2_Un = new MyOp (OpCodes.Conv_Ovf_U2_Un);
+            public static readonly MyOp Conv_Ovf_U4_Un = new MyOp (OpCodes.Conv_Ovf_U4_Un);
+            public static readonly MyOp Conv_Ovf_U8_Un = new MyOp (OpCodes.Conv_Ovf_U8_Un);
+            public static readonly MyOp Conv_Ovf_I_Un  = new MyOp (OpCodes.Conv_Ovf_I_Un);
+            public static readonly MyOp Conv_Ovf_U_Un  = new MyOp (OpCodes.Conv_Ovf_U_Un);
+            public static readonly MyOp Box            = new MyOp (OpCodes.Box);
+            public static readonly MyOp Newarr         = new MyOp (OpCodes.Newarr);
+            public static readonly MyOp Ldlen          = new MyOp (OpCodes.Ldlen);
+            public static readonly MyOp Ldelema        = new MyOp (OpCodes.Ldelema);
+            public static readonly MyOp Ldelem_I1      = new MyOp (OpCodes.Ldelem_I1);
+            public static readonly MyOp Ldelem_U1      = new MyOp (OpCodes.Ldelem_U1);
+            public static readonly MyOp Ldelem_I2      = new MyOp (OpCodes.Ldelem_I2);
+            public static readonly MyOp Ldelem_U2      = new MyOp (OpCodes.Ldelem_U2);
+            public static readonly MyOp Ldelem_I4      = new MyOp (OpCodes.Ldelem_I4);
+            public static readonly MyOp Ldelem_U4      = new MyOp (OpCodes.Ldelem_U4);
+            public static readonly MyOp Ldelem_I8      = new MyOp (OpCodes.Ldelem_I8);
+            public static readonly MyOp Ldelem_I       = new MyOp (OpCodes.Ldelem_I);
+            public static readonly MyOp Ldelem_R4      = new MyOp (OpCodes.Ldelem_R4);
+            public static readonly MyOp Ldelem_R8      = new MyOp (OpCodes.Ldelem_R8);
+            public static readonly MyOp Ldelem_Ref     = new MyOp (OpCodes.Ldelem_Ref);
+            public static readonly MyOp Stelem_I       = new MyOp (OpCodes.Stelem_I);
+            public static readonly MyOp Stelem_I1      = new MyOp (OpCodes.Stelem_I1);
+            public static readonly MyOp Stelem_I2      = new MyOp (OpCodes.Stelem_I2);
+            public static readonly MyOp Stelem_I4      = new MyOp (OpCodes.Stelem_I4);
+            public static readonly MyOp Stelem_I8      = new MyOp (OpCodes.Stelem_I8);
+            public static readonly MyOp Stelem_R4      = new MyOp (OpCodes.Stelem_R4);
+            public static readonly MyOp Stelem_R8      = new MyOp (OpCodes.Stelem_R8);
+            public static readonly MyOp Stelem_Ref     = new MyOp (OpCodes.Stelem_Ref);
+            public static readonly MyOp Ldelem         = new MyOp (OpCodes.Ldelem);
+            public static readonly MyOp Stelem         = new MyOp (OpCodes.Stelem);
+            public static readonly MyOp Unbox_Any      = new MyOp (OpCodes.Unbox_Any);
+            public static readonly MyOp Conv_Ovf_I1    = new MyOp (OpCodes.Conv_Ovf_I1);
+            public static readonly MyOp Conv_Ovf_U1    = new MyOp (OpCodes.Conv_Ovf_U1);
+            public static readonly MyOp Conv_Ovf_I2    = new MyOp (OpCodes.Conv_Ovf_I2);
+            public static readonly MyOp Conv_Ovf_U2    = new MyOp (OpCodes.Conv_Ovf_U2);
+            public static readonly MyOp Conv_Ovf_I4    = new MyOp (OpCodes.Conv_Ovf_I4);
+            public static readonly MyOp Conv_Ovf_U4    = new MyOp (OpCodes.Conv_Ovf_U4);
+            public static readonly MyOp Conv_Ovf_I8    = new MyOp (OpCodes.Conv_Ovf_I8);
+            public static readonly MyOp Conv_Ovf_U8    = new MyOp (OpCodes.Conv_Ovf_U8);
+            public static readonly MyOp Refanyval      = new MyOp (OpCodes.Refanyval);
+            public static readonly MyOp Ckfinite       = new MyOp (OpCodes.Ckfinite);
+            public static readonly MyOp Mkrefany       = new MyOp (OpCodes.Mkrefany);
+            public static readonly MyOp Ldtoken        = new MyOp (OpCodes.Ldtoken);
+            public static readonly MyOp Conv_U2        = new MyOp (OpCodes.Conv_U2);
+            public static readonly MyOp Conv_U1        = new MyOp (OpCodes.Conv_U1);
+            public static readonly MyOp Conv_I         = new MyOp (OpCodes.Conv_I);
+            public static readonly MyOp Conv_Ovf_I     = new MyOp (OpCodes.Conv_Ovf_I);
+            public static readonly MyOp Conv_Ovf_U     = new MyOp (OpCodes.Conv_Ovf_U);
+            public static readonly MyOp Add_Ovf        = new MyOp (OpCodes.Add_Ovf);
+            public static readonly MyOp Add_Ovf_Un     = new MyOp (OpCodes.Add_Ovf_Un);
+            public static readonly MyOp Mul_Ovf        = new MyOp (OpCodes.Mul_Ovf);
+            public static readonly MyOp Mul_Ovf_Un     = new MyOp (OpCodes.Mul_Ovf_Un);
+            public static readonly MyOp Sub_Ovf        = new MyOp (OpCodes.Sub_Ovf);
+            public static readonly MyOp Sub_Ovf_Un     = new MyOp (OpCodes.Sub_Ovf_Un);
+            public static readonly MyOp Endfinally     = new MyOp (OpCodes.Endfinally);
+            public static readonly MyOp Leave          = new MyOp (OpCodes.Leave);
+            public static readonly MyOp Leave_S        = new MyOp (OpCodes.Leave_S);
+            public static readonly MyOp Stind_I        = new MyOp (OpCodes.Stind_I);
+            public static readonly MyOp Conv_U         = new MyOp (OpCodes.Conv_U);
+            public static readonly MyOp Prefix7        = new MyOp (OpCodes.Prefix7);
+            public static readonly MyOp Prefix6        = new MyOp (OpCodes.Prefix6);
+            public static readonly MyOp Prefix5        = new MyOp (OpCodes.Prefix5);
+            public static readonly MyOp Prefix4        = new MyOp (OpCodes.Prefix4);
+            public static readonly MyOp Prefix3        = new MyOp (OpCodes.Prefix3);
+            public static readonly MyOp Prefix2        = new MyOp (OpCodes.Prefix2);
+            public static readonly MyOp Prefix1        = new MyOp (OpCodes.Prefix1);
+            public static readonly MyOp Prefixref      = new MyOp (OpCodes.Prefixref);
+            public static readonly MyOp Arglist        = new MyOp (OpCodes.Arglist);
+            public static readonly MyOp Ceq            = new MyOp (OpCodes.Ceq, "==");
+            public static readonly MyOp Cgt            = new MyOp (OpCodes.Cgt, ">");
+            public static readonly MyOp Cgt_Un         = new MyOp (OpCodes.Cgt_Un);
+            public static readonly MyOp Clt            = new MyOp (OpCodes.Clt, "<");
+            public static readonly MyOp Clt_Un         = new MyOp (OpCodes.Clt_Un);
+            public static readonly MyOp Ldftn          = new MyOp (OpCodes.Ldftn);
+            public static readonly MyOp Ldvirtftn      = new MyOp (OpCodes.Ldvirtftn);
+            public static readonly MyOp Ldarg          = new MyOp (OpCodes.Ldarg);
+            public static readonly MyOp Ldarga         = new MyOp (OpCodes.Ldarga);
+            public static readonly MyOp Starg          = new MyOp (OpCodes.Starg);
+            public static readonly MyOp Ldloc          = new MyOp (OpCodes.Ldloc);
+            public static readonly MyOp Ldloca         = new MyOp (OpCodes.Ldloca);
+            public static readonly MyOp Stloc          = new MyOp (OpCodes.Stloc);
+            public static readonly MyOp Localloc       = new MyOp (OpCodes.Localloc);
+            public static readonly MyOp Endfilter      = new MyOp (OpCodes.Endfilter);
+            public static readonly MyOp Unaligned      = new MyOp (OpCodes.Unaligned);
+            public static readonly MyOp Volatile       = new MyOp (OpCodes.Volatile);
+            public static readonly MyOp Tailcall       = new MyOp (OpCodes.Tailcall);
+            public static readonly MyOp Initobj        = new MyOp (OpCodes.Initobj);
+            public static readonly MyOp Constrained    = new MyOp (OpCodes.Constrained);
+            public static readonly MyOp Cpblk          = new MyOp (OpCodes.Cpblk);
+            public static readonly MyOp Initblk        = new MyOp (OpCodes.Initblk);
+            public static readonly MyOp Rethrow        = new MyOp (OpCodes.Rethrow);
+            public static readonly MyOp Sizeof         = new MyOp (OpCodes.Sizeof);
+            public static readonly MyOp Refanytype     = new MyOp (OpCodes.Refanytype);
+            public static readonly MyOp Readonly       = new MyOp (OpCodes.Readonly);
+
+            // used internally
+            public static readonly MyOp Cge            = new MyOp ("cge", ">=");
+            public static readonly MyOp Cle            = new MyOp ("cle", "<=");
+            public static readonly MyOp Cne            = new MyOp ("cne", "!=");
+        }
+    }
+}

+ 259 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRSDTypeClObj.cs

@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Reflection.Emit;
+
+using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
+using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
+using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
+using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
+using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
+using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+    public class XMRSDTypeClObj
+    {
+        /*
+         * Which script instance we are part of so we can access
+         * the script's global variables and functions.
+         */
+        public XMRInstAbstract xmrInst;
+
+        /*
+         * What class we actually are in the hierarchy
+         * used for casting.
+         */
+        public TokenDeclSDTypeClass sdtcClass;
+
+        /*
+         * Our VTable array, used for calling virtual functions.
+         * And ITable array, used for calling our implementation of interface functions.
+         */
+        public Delegate[]   sdtcVTable;
+        public Delegate[][] sdtcITable;
+
+        /*
+         * These arrays hold the insance variable values.
+         * The array lengths are determined by the script compilation,
+         * and are found in TokenDeclSDTypeClass.instSizes.
+         */
+        public XMRInstArrays instVars;
+
+        /**
+         * @brief Called by script's $new() to initialize a new object.
+         */
+        public XMRSDTypeClObj (XMRInstAbstract inst, int classindex)
+        {
+            Construct (inst, classindex);
+            instVars.AllocVarArrays (sdtcClass.instSizes);
+        }
+
+        /**
+         * @brief Set up everything except the instVars arrays.
+         * @param inst = script instance this object is part of
+         * @param classindex = which script-defined type class this object is an onstance of
+         * @returns with the vtables filled in
+         */
+        private void Construct (XMRInstAbstract inst, int classindex)
+        {
+            Delegate[] thisMid = null;
+            TokenDeclSDTypeClass clas = (TokenDeclSDTypeClass)inst.m_ObjCode.sdObjTypesIndx[classindex];
+
+            xmrInst   = inst;
+            sdtcClass = clas;
+            instVars  = new XMRInstArrays (inst);
+
+            /*
+             * VTable consists of delegates built from DynamicMethods and the 'this' pointer.
+             * Yes, yes, lots of shitty little mallocs.
+             */
+            DynamicMethod[] vDynMeths = clas.vDynMeths;
+            if (vDynMeths != null) {
+                int n = vDynMeths.Length;
+                Type[] vMethTypes = clas.vMethTypes;
+                sdtcVTable = new Delegate[n];
+                for (int i = 0; i < n; i ++) {
+                    sdtcVTable[i] = vDynMeths[i].CreateDelegate (vMethTypes[i], this);
+                }
+            }
+
+            /*
+             * Fill in interface vtables.
+             * There is one array of delegates for each implemented interface.
+             * The array of delegates IS the interface's object, ie, the 'this' value of the interface.
+             * To cast from the class type to the interface type, just get the correct array from the table.
+             * To cast from the interface type to the class type, just get Target of entry 0.
+             *
+             * So we end up with this:
+             *    sdtcITable[interfacenumber][methodofintfnumber] = delegate of this.ourimplementationofinterfacesmethod
+             */
+            if (clas.iDynMeths != null) {
+                int nIFaces = clas.iDynMeths.Length;
+                sdtcITable = new Delegate[nIFaces][];
+                for (int i = 0; i < nIFaces; i ++) {
+
+                    // get vector of entrypoints of our instance methods that implement that interface
+                    DynamicMethod[] iDynMeths = clas.iDynMeths[i];
+                    Type[]         iMethTypes = clas.iMethTypes[i];
+
+                    // allocate an array with a slot for each method the interface defines
+                    int nMeths = iDynMeths.Length;
+                    Delegate[] ivec;
+                    if (nMeths > 0) {
+                        // fill in the array with delegates that reference back to this class instance
+                        ivec = new Delegate[nMeths];
+                        for (int j = 0; j < nMeths; j ++) {
+                            ivec[j] = iDynMeths[j].CreateDelegate (iMethTypes[j], this);
+                        }
+                    } else {
+                        // just a marker interface with no methods,
+                        // allocate a one-element array and fill
+                        // with a dummy entry.  this will allow casting
+                        // back to the original class instance (this)
+                        // by reading Target of entry 0.
+                        if (thisMid == null) {
+                            thisMid = new Delegate[1];
+                            thisMid[0] = markerInterfaceDummy.CreateDelegate (typeof (MarkerInterfaceDummy), this);
+                        }
+                        ivec = thisMid;
+                    }
+
+                    // save whatever we ended up allocating
+                    sdtcITable[i] = ivec;
+                }
+            }
+        }
+
+        private delegate void MarkerInterfaceDummy ();
+        private static DynamicMethod markerInterfaceDummy = MakeMarkerInterfaceDummy ();
+        private static DynamicMethod MakeMarkerInterfaceDummy ()
+        {
+            DynamicMethod dm = new DynamicMethod ("XMRSDTypeClObj.MarkerInterfaceDummy", null, new Type[] { typeof (XMRSDTypeClObj) });
+            ILGenerator ilGen = dm.GetILGenerator ();
+            ilGen.Emit (OpCodes.Ret);
+            return dm;
+        }
+
+        /**
+         * @brief Perform runtime casting of script-defined interface object to
+         *        its corresponding script-defined class object.
+         * @param da = interface object (array of delegates pointing to class's implementations of interface's methods)
+         * @param classindex = what class those implementations are supposedly part of
+         * @returns original script-defined class object
+         */
+        public static XMRSDTypeClObj CastIFace2Class (Delegate[] da, int classindex)
+        {
+            return CastClass2Class (da[0].Target, classindex);
+        }
+
+        /**
+         * @brief Perform runtime casting of XMRSDTypeClObj's.
+         * @param ob = XMRSDTypeClObj of unknown script-defined class to cast
+         * @param classindex = script-defined class to cast it to
+         * @returns ob is a valid instance of classindex; else exception thrown
+         */
+        public static XMRSDTypeClObj CastClass2Class (object ob, int classindex)
+        {
+            /*
+             * Let mono check to see if we at least have an XMRSDTypeClObj.
+             */
+            XMRSDTypeClObj ci = (XMRSDTypeClObj)ob;
+            if (ci != null) {
+
+                /*
+                 * This is the target class, ie, what we are hoping the object can cast to.
+                 */
+                TokenDeclSDTypeClass tc = (TokenDeclSDTypeClass)ci.xmrInst.m_ObjCode.sdObjTypesIndx[classindex];
+
+                /*
+                 * Step from the object's actual class rootward.
+                 * If we find the target class along the way, the cast is valid.
+                 * If we run off the end of the root, the cast is not valid.
+                 */
+                for (TokenDeclSDTypeClass ac = ci.sdtcClass; ac != tc; ac = ac.extends) {
+                    if (ac == null) throw new InvalidCastException ("invalid cast from " + ci.sdtcClass.longName.val + 
+                                                                                  " to " + tc.longName.val);
+                }
+
+                /*
+                 * The target class is at or rootward of the actual class,
+                 * so the cast is valid.
+                 */
+            }
+            return ci;
+        }
+
+        /**
+         * @brief Cast an arbitrary object to the given interface.
+         * @param ob = object to be cast of unknown type
+         * @returns ob cast to the interface type
+         */
+        public static Delegate[] CastObj2IFace (object ob, string ifacename)
+        {
+            if (ob == null) return null;
+
+            /*
+             * If it is already one of our interfaces, extract the script-defined class object from it.
+             */
+            if (ob is Delegate[]) {
+                Delegate[] da = (Delegate[])ob;
+                ob = da[0].Target;
+            }
+
+            /*
+             * Now that we have a presumed script-defined class object, cast that to the requested interface
+             * by picking the array of delegates that corresponds to the requested interface.
+             */
+            XMRSDTypeClObj ci = (XMRSDTypeClObj)ob;
+            int iFaceIndex = ci.sdtcClass.intfIndices[ifacename];
+            return ci.sdtcITable[iFaceIndex];
+        }
+
+        /**
+         * @brief Write the whole thing out to a stream.
+         */
+        public void Capture (XMRInstArrays.Sender sendValue)
+        {
+            sendValue (this.sdtcClass.sdTypeIndex);
+            this.instVars.SendArrays (sendValue);
+        }
+
+        /**
+         * @brief Read the whole thing in from a stream.
+         */
+        public XMRSDTypeClObj () { }
+        public void Restore (XMRInstAbstract inst, XMRInstArrays.Recver recvValue)
+        {
+            int classindex = (int)recvValue ();
+            Construct (inst, classindex);
+            this.instVars.RecvArrays (recvValue);
+        }
+    }
+}

+ 262 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRScriptThread.cs

@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using Mono.Tasklets;
+using OpenSim.Framework.Monitoring;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace OpenSim.Region.ScriptEngine.XMREngine
+{
+
+    /**
+     * @brief There are NUMSCRIPTHREADWKRS of these.
+     *        Each sits in a loop checking the Start and Yield queues for 
+     *        a script to run and calls the script as a microthread.
+     */
+    public class XMRScriptThread {
+        private static int    m_WakeUpOne  = 0;
+        public  static object m_WakeUpLock = new object();
+        private static Dictionary<Thread,XMRScriptThread> m_AllThreads = new Dictionary<Thread,XMRScriptThread> ();
+
+        /**
+         * @brief Something was just added to the Start or Yield queue so
+         *        wake one of the XMRScriptThread instances to run it.
+         */
+        public static void WakeUpOne()
+        {
+            lock (m_WakeUpLock)
+            {
+                m_WakeUpOne ++;
+                Monitor.Pulse (m_WakeUpLock);
+            }
+        }
+
+        public static XMRScriptThread CurrentScriptThread ()
+        {
+            XMRScriptThread st;
+            lock (m_AllThreads) {
+                m_AllThreads.TryGetValue (Thread.CurrentThread, out st);
+            }
+            return st;
+        }
+
+        private bool        m_Exiting = false;
+        private bool        m_SuspendScriptThreadFlag = false;
+        private bool        m_WakeUpThis = false;
+        private bool        m_Continuations = false;
+        public  DateTime    m_LastRanAt = DateTime.MinValue;
+        public  int         m_ScriptThreadTID = 0;
+        public  long        m_ScriptExecTime = 0;
+        private Thread      thd;
+        private XMREngine   engine;
+        public  XMRInstance m_RunInstance = null;
+
+        public XMRScriptThread(XMREngine eng)
+        {
+            engine = eng;
+            m_Continuations = engine.uThreadCtor.DeclaringType == typeof (ScriptUThread_Con);
+            thd = XMREngine.StartMyThread (RunScriptThread, "xmrengine script", ThreadPriority.BelowNormal);
+            lock (m_AllThreads) {
+                m_AllThreads.Add (thd, this);
+            }
+        }
+
+        public void SuspendThread()
+        {
+            m_SuspendScriptThreadFlag = true;
+            WakeUpScriptThread();
+        }
+
+        public void ResumeThread()
+        {
+            m_SuspendScriptThreadFlag = false;
+            WakeUpScriptThread();
+        }
+
+        public void Terminate()
+        {
+            m_Exiting = true;
+            WakeUpScriptThread();
+            thd.Join();
+            lock (m_AllThreads) {
+                m_AllThreads.Remove (thd);
+            }
+            thd = null;
+        }
+
+        public void TimeSlice()
+        {
+            XMRInstance instance = m_RunInstance;
+            if (instance != null) {
+                instance.suspendOnCheckRunTemp = true;
+            }
+        }
+
+        /**
+         * @brief Wake up this XMRScriptThread instance.
+         */
+        private void WakeUpScriptThread()
+        {
+            lock (m_WakeUpLock) {
+                m_WakeUpThis = true;
+                Monitor.PulseAll (m_WakeUpLock);
+            }
+        }
+
+        /**
+         * @brief Thread that runs the scripts.
+         */
+        private void RunScriptThread()
+        {
+            XMRInstance inst;
+            Mono.Tasklets.Continuation engstack = null;
+            if (m_Continuations) {
+                engstack = new Mono.Tasklets.Continuation ();
+                engstack.Mark ();
+            }
+            m_ScriptThreadTID = System.Threading.Thread.CurrentThread.ManagedThreadId;
+
+            while (!m_Exiting) {
+                XMREngine.UpdateMyThread ();
+
+                /*
+                 * Handle 'xmr resume/suspend' commands.
+                 */
+                if (m_SuspendScriptThreadFlag) {
+                    lock (m_WakeUpLock) {
+                        while (m_SuspendScriptThreadFlag &&
+                               !m_Exiting &&
+                               (engine.m_ThunkQueue.Count == 0)) {
+                            Monitor.Wait (m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
+                            XMREngine.UpdateMyThread ();
+                        }
+                    }
+                }
+
+                /*
+                 * Maybe there are some scripts waiting to be migrated in or out.
+                 */
+                ThreadStart thunk = null;
+                lock (m_WakeUpLock) {
+                    if (engine.m_ThunkQueue.Count > 0) {
+                        thunk = engine.m_ThunkQueue.Dequeue ();
+                    }
+                }
+                if (thunk != null) {
+                    inst = (XMRInstance)thunk.Target;
+                    if (m_Continuations && (inst.scrstack == null)) {
+                        inst.engstack = engstack;
+                        inst.scrstack = new Mono.Tasklets.Continuation ();
+                        inst.scrstack.Mark ();
+                    }
+                    thunk ();
+                    continue;
+                }
+
+                if (engine.m_StartProcessing) {
+
+                    /*
+                     * If event just queued to any idle scripts
+                     * start them right away.  But only start so
+                     * many so we can make some progress on yield
+                     * queue.
+                     */
+                    int numStarts;
+                    for (numStarts = 5; -- numStarts >= 0;) {
+                        lock (engine.m_StartQueue) {
+                            inst = engine.m_StartQueue.RemoveHead();
+                        }
+                        if (inst == null) break;
+                        if (inst.m_IState != XMRInstState.ONSTARTQ) throw new Exception("bad state");
+                        if (m_Continuations && (inst.scrstack == null)) {
+                            inst.engstack = engstack;
+                            inst.scrstack = new Mono.Tasklets.Continuation ();
+                            inst.scrstack.Mark ();
+                        }
+                        RunInstance (inst);
+                    }
+
+                    /*
+                     * If there is something to run, run it
+                     * then rescan from the beginning in case
+                     * a lot of things have changed meanwhile.
+                     *
+                     * These are considered lower priority than
+                     * m_StartQueue as they have been taking at
+                     * least one quantum of CPU time and event
+                     * handlers are supposed to be quick.
+                     */
+                    lock (engine.m_YieldQueue) {
+                        inst = engine.m_YieldQueue.RemoveHead();
+                    }
+                    if (inst != null) {
+                        if (inst.m_IState != XMRInstState.ONYIELDQ) throw new Exception("bad state");
+                        RunInstance (inst);
+                        numStarts = -1;
+                    }
+
+                    /*
+                     * If we left something dangling in the m_StartQueue or m_YieldQueue, go back to check it.
+                     */
+                    if (numStarts < 0) continue;
+                }
+
+                /*
+                 * Nothing to do, sleep.
+                 */
+                lock (m_WakeUpLock) {
+                    if (!m_WakeUpThis && (m_WakeUpOne <= 0) && !m_Exiting) {
+                        Monitor.Wait (m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
+                    }
+                    m_WakeUpThis = false;
+                    if ((m_WakeUpOne > 0) && (-- m_WakeUpOne > 0)) {
+                        Monitor.Pulse (m_WakeUpLock);
+                    }
+                }
+            }
+            XMREngine.MyThreadExiting ();
+        }
+
+        /**
+         * @brief A script instance was just removed from the Start or Yield Queue.
+         *        So run it for a little bit then stick in whatever queue it should go in.
+         */
+        private void RunInstance (XMRInstance inst)
+        {
+            m_LastRanAt = DateTime.UtcNow;
+            m_ScriptExecTime -= (long)(m_LastRanAt - DateTime.MinValue).TotalMilliseconds;
+            inst.m_IState = XMRInstState.RUNNING;
+            m_RunInstance = inst;
+            XMRInstState newIState = inst.RunOne();
+            m_RunInstance = null;
+            engine.HandleNewIState(inst, newIState);
+            m_ScriptExecTime += (long)(DateTime.UtcNow - DateTime.MinValue).TotalMilliseconds;
+        }
+    }
+}

+ 557 - 0
OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs

@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using Mono.Tasklets;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Threading;
+
+
+
+/***************************\
+ *  Use standard C# code   *
+ *  - uses system threads  *
+\***************************/
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    public class ScriptUThread_Sys : IScriptUThread, IDisposable
+    {
+        private Exception except;
+        private int active;     // -1: hibernating
+                                //  0: exited
+                                //  1: running
+        private object activeLock = new object ();
+        private XMRInstance instance;
+
+        public ScriptUThread_Sys (XMRInstance instance)
+        {
+            this.instance = instance;
+        }
+
+        /**
+         * @brief Start script event handler from the beginning.
+         *        Return when either the script event handler completes
+         *        or the script calls Hiber().
+         * @returns null: script did not throw any exception so far
+         *          else: script threw an exception
+         */
+        public Exception StartEx ()
+        {
+            lock (activeLock) {
+
+                /*
+                 * We should only be called when script is inactive.
+                 */
+                if (active != 0) throw new Exception ("active=" + active);
+
+                /*
+                 * Tell CallSEHThread() to run script event handler in a thread.
+                 */
+                active = 1;
+                TredPoo.RunSomething (CallSEHThread);
+
+                /*
+                 * Wait for script to call Hiber() or for script to
+                 * return back out to CallSEHThread().
+                 */
+                while (active > 0) {
+                    Monitor.Wait (activeLock);
+                }
+            }
+
+            /*
+             * Return whether or not script threw an exception.
+             */
+            return except;
+        }
+
+        /**
+         * @brief We now want to run some more script code from where it last hibernated
+         *        until it either finishes the script event handler or until the script
+         *        calls Hiber() again.
+         */
+        public Exception ResumeEx ()
+        {
+            lock (activeLock) {
+
+                /*
+                 * We should only be called when script is hibernating.
+                 */
+                if (active >= 0) throw new Exception ("active=" + active);
+
+                /*
+                 * Tell Hiber() to return back to script.
+                 */
+                active = 1;
+                Monitor.PulseAll (activeLock);
+
+                /*
+                 * Wait for script to call Hiber() again or for script to
+                 * return back out to CallSEHThread().
+                 */
+                while (active > 0) {
+                    Monitor.Wait (activeLock);
+                }
+            }
+
+            /*
+             * Return whether or not script threw an exception.
+             */
+            return except;
+        }
+
+        /**
+         * @brief Script is being closed out.
+         *        Terminate thread asap.
+         */
+        public void Dispose ()
+        {
+            lock (activeLock) {
+                instance = null;
+                Monitor.PulseAll (activeLock);
+            }
+        }
+
+        /**
+         * @brief Determine if script is active.
+         * Returns: 0: nothing started or has returned
+         *             Resume() must not be called
+         *             Start() may be called
+         *             Hiber() must not be called
+         *         -1: thread has called Hiber()
+         *             Resume() may be called
+         *             Start() may be called
+         *             Hiber() must not be called
+         *          1: thread is running
+         *             Resume() must not be called
+         *             Start() must not be called
+         *             Hiber() may be called
+         */
+        public int Active ()
+        {
+            return active;
+        }
+
+        /**
+         * @brief This thread executes the script event handler code.
+         */
+        private void CallSEHThread ()
+        {
+            lock (activeLock) {
+                if (active <= 0) throw new Exception ("active=" + active);
+
+                except = null;                  // assume completion without exception
+                try {
+                    instance.CallSEH ();        // run script event handler
+                } catch (Exception e) {
+                    except = e;                 // threw exception, save for Start()/Resume()
+                }
+
+                active = 0;                     // tell Start() or Resume() we're done
+                Monitor.PulseAll (activeLock);
+            }
+        }
+
+        /**
+         * @brief Called by the script event handler whenever it wants to hibernate.
+         */
+        public void Hiber ()
+        {
+            if (active <= 0) throw new Exception ("active=" + active);
+
+            // tell Start() or Resume() we are hibernating
+            active = -1;
+            Monitor.PulseAll (activeLock);
+
+            // wait for Resume() or Dispose() to be called
+            while ((active < 0) && (instance != null)) {
+                Monitor.Wait (activeLock);
+            }
+
+            // don't execute any more script code, just exit
+            if (instance == null) {
+                throw new AbortedByDisposeException ();
+            }
+        }
+
+        /**
+         * @brief Number of remaining stack bytes.
+         */
+        public int StackLeft ()
+        {
+            return 0x7FFFFFFF;
+        }
+
+        public class AbortedByDisposeException : Exception, IXMRUncatchable { }
+
+        /**
+         * @brief Pool of threads that run script event handlers.
+         */
+        private class TredPoo {
+            private static readonly TimeSpan idleTimeSpan = new TimeSpan (0, 0, 1, 0, 0);  // 1 minute
+
+            private static int tredPooAvail = 0;
+            private static object tredPooLock = new object ();
+            private static Queue<ThreadStart> tredPooQueue = new Queue<ThreadStart> ();
+
+            /**
+             * @brief Queue a function for execution in a system thread.
+             */
+            public static void RunSomething (ThreadStart entry)
+            {
+                lock (tredPooLock) {
+                    tredPooQueue.Enqueue (entry);
+                    Monitor.Pulse (tredPooLock);
+                    if (tredPooAvail < tredPooQueue.Count) {
+                        new TredPoo ();
+                    }
+                }
+            }
+
+            /**
+             * @brief Start a new system thread.
+             *        It will shortly attempt to dequeue work or if none,
+             *        add itself to the available thread list.
+             */
+            private TredPoo ()
+            {
+                Thread thread = new Thread (Main);
+                thread.Name   = "XMRUThread_sys";
+                thread.IsBackground = true;
+                thread.Start ();
+                tredPooAvail ++;
+            }
+
+            /**
+             * @brief Executes items from the queue or waits a little while
+             *        if nothing.  If idle for a while, it exits.
+             */
+            private void Main ()
+            {
+                int first = 1;
+                ThreadStart entry;
+                while (true) {
+                    lock (tredPooLock) {
+                        tredPooAvail -= first;
+                        first = 0;
+                        while (tredPooQueue.Count <= 0) {
+                            tredPooAvail ++;
+                            bool keepgoing = Monitor.Wait (tredPooLock, idleTimeSpan);
+                            -- tredPooAvail;
+                            if (!keepgoing) return;
+                        }
+                        entry = tredPooQueue.Dequeue ();
+                    }
+                    entry ();
+                }
+            }
+        }
+    }
+}
+
+
+
+/*************************************\
+ *  Use Mono.Tasklets.Continuations  *
+ *  - memcpy's stack                 *
+\*************************************/
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    public partial class XMRInstance {
+        public Mono.Tasklets.Continuation engstack;
+        public Mono.Tasklets.Continuation scrstack;
+    }
+
+    public class ScriptUThread_Con : IScriptUThread, IDisposable
+    {
+        private XMRInstance instance;
+
+        public ScriptUThread_Con (XMRInstance instance)
+        {
+            this.instance = instance;
+        }
+
+        private const int SAVEENGINESTACK = 0;
+        private const int LOADENGINESTACK = 1;
+        private const int SAVESCRIPTSTACK = 2;
+        private const int LOADSCRIPTSTACK = 3;
+
+        private Exception except;
+        private int active;
+
+        /**
+         * @brief Start script event handler from the beginning.
+         *        Return when either the script event handler completes
+         *        or the script calls Hiber().
+         * @returns null: script did not throw any exception so far
+         *          else: script threw an exception
+         */
+        public Exception StartEx ()
+        {
+            /*
+             * Save engine stack so we know how to jump back to engine in case
+             * the script calls Hiber().
+             */
+            switch (instance.engstack.Store (SAVEENGINESTACK)) {
+
+                /*
+                 * Engine stack has been saved, start running the event handler.
+                 */
+                case SAVEENGINESTACK: {
+
+                    /*
+                     * Run event handler according to stackFrames.
+                     * In either case it is assumed that stateCode and eventCode
+                     * indicate which event handler is to be called and that ehArgs
+                     * points to the event handler argument list.
+                     */
+                    active = 1;
+                    except = null;
+                    try {
+                        instance.CallSEH ();
+                    } catch (Exception e) {
+                        except = e;
+                    }
+
+                    /*
+                     * We now want to return to the script engine.
+                     * Setting active = 0 means the microthread has exited.
+                     * We need to call engstack.Restore() in case the script called Hiber()
+                     * anywhere, we want to return out the corresponding Restore() and not the
+                     * Start().
+                     */
+                    active = 0;
+                    instance.engstack.Restore (LOADENGINESTACK);
+                    throw new Exception ("returned from Restore()");
+                }
+
+                /*
+                 * Script called Hiber() somewhere so just return back out.
+                 */
+                case LOADENGINESTACK: {
+                    break;
+                }
+
+                default: throw new Exception ("bad engstack code");
+            }
+
+            return except;
+        }
+
+        public void Dispose ()
+        { }
+
+        /**
+         * @brief Determine if script is active.
+         * Returns: 0: nothing started or has returned
+         *             Resume() must not be called
+         *             Start() may be called
+         *             Hiber() must not be called
+         *         -1: thread has called Hiber()
+         *             Resume() may be called
+         *             Start() may be called
+         *             Hiber() must not be called
+         *          1: thread is running
+         *             Resume() must not be called
+         *             Start() must not be called
+         *             Hiber() may be called
+         */
+        public int Active ()
+        {
+            return active;
+        }
+
+        /**
+         * @brief Called by the script wherever it wants to hibernate.
+         *        So this means to save the scripts stack in 'instance.scrstack' then
+         *        restore the engstack to cause us to return back to the engine.
+         */
+        public void Hiber ()
+        {
+            /*
+             * Save where we are in the script's code in 'instance.scrstack'
+             * so we can wake the script when Resume() is called.
+             */
+            switch (instance.scrstack.Store (SAVESCRIPTSTACK)) {
+
+                /*
+                 * Script's stack is now saved in 'instance.scrstack'.
+                 * Reload the engine's stack from 'instance.engstack' and jump to it.
+                 */
+                case SAVESCRIPTSTACK: {
+                    active = -1;
+                    instance.engstack.Restore (LOADENGINESTACK);
+                    throw new Exception ("returned from Restore()");
+                }
+
+                /*
+                 * Resume() was just called and we want to resume executing script code.
+                 */
+                case LOADSCRIPTSTACK: {
+                    break;
+                }
+
+                default: throw new Exception ("bad scrstack code");
+            }
+        }
+
+        /**
+         * @brief We now want to run some more script code from where it last hibernated
+         *        until it either finishes the script event handler or until the script
+         *        calls Hiber() again.
+         */
+        public Exception ResumeEx ()
+        {
+            /*
+             * Save where we are in the engine's code in 'instance.engstack'
+             * so if the script calls Hiber() again or exits, we know how to get
+             * back to the engine.
+             */
+            switch (instance.engstack.Store (SAVEENGINESTACK)) {
+
+                /*
+                 * This is original call to Resume() from the engine,
+                 * jump to where we left off within Hiber().
+                 */
+                case SAVEENGINESTACK: {
+                    active = 1;
+                    instance.scrstack.Restore (LOADSCRIPTSTACK);
+                    throw new Exception ("returned from Restore()");
+                }
+
+                /*
+                 * Script has called Hiber() again, so return back to
+                 * script engine code.
+                 */
+                case LOADENGINESTACK: {
+                    break;
+                }
+
+                default: throw new Exception ("bad engstack code");
+            }
+
+            return except;
+        }
+
+        /**
+         * @brief Number of remaining stack bytes.
+         */
+        public int StackLeft ()
+        {
+            return 0x7FFFFFFF;
+        }
+    }
+}
+
+
+
+/***********************************\
+ *  Use Mono.Tasklets.MMRUThreads  *
+ *  - switches stack pointer       *
+\***********************************/
+
+namespace OpenSim.Region.ScriptEngine.XMREngine {
+
+    public class ScriptUThread_MMR : IScriptUThread, IDisposable
+    {
+        private static Exception uthread_looked;
+        private static Type uttype;
+        private static Type uthread_entry;
+        private static MethodInfo uthread_dispose;
+        private static MethodInfo uthread_startex;
+        private static MethodInfo uthread_resumex;
+        private static MethodInfo uthread_suspend;
+        private static MethodInfo uthread_active;
+        private static MethodInfo uthread_stackleft;
+
+        public static Exception LoadMono ()
+        {
+            if ((uthread_looked == null) && (uthread_stackleft == null)) {
+                try {
+                    Assembly mt   = Assembly.Load ("Mono.Tasklets");
+                    uttype        = mt.GetType ("Mono.Tasklets.MMRUThread", true);
+                    uthread_entry = mt.GetType ("Mono.Tasklets.MMRUThread+Entry", true);
+
+                    uthread_dispose   = uttype.GetMethod ("Dispose");       // no parameters, no return value
+                    uthread_startex   = uttype.GetMethod ("StartEx");       // takes uthread_entry delegate as parameter, returns exception
+                    uthread_resumex   = uttype.GetMethod ("ResumeEx");      // takes exception as parameter, returns exception
+                    uthread_suspend   = uttype.GetMethod ("Suspend", new Type[] { });  // no return value
+                    uthread_active    = uttype.GetMethod ("Active");        // no parameters, returns int
+                    uthread_stackleft = uttype.GetMethod ("StackLeft");     // no parameters, returns IntPtr
+                } catch (Exception e) {
+                    uthread_looked = new NotSupportedException ("'mmr' thread model requires patched mono", e);
+                }
+            }
+            return uthread_looked;
+        }
+
+        private static object[] resumex_args = new object[] { null };
+
+        private object uthread;  // type MMRUThread
+        private object[] startex_args = new object[1];
+
+        public ScriptUThread_MMR (XMRInstance instance)
+        {
+            this.uthread    = Activator.CreateInstance (uttype, new object[] { (IntPtr) instance.m_StackSize, instance.m_DescName });
+            startex_args[0] = Delegate.CreateDelegate (uthread_entry, instance, "CallSEH");
+        }
+
+        public void Dispose ()
+        {
+            uthread_dispose.Invoke (uthread, null);
+            uthread = null;
+        }
+
+        public Exception StartEx ()
+        {
+            return (Exception) uthread_startex.Invoke (uthread, startex_args);
+        }
+
+        public Exception ResumeEx ()
+        {
+            return (Exception) uthread_resumex.Invoke (uthread, resumex_args);
+        }
+
+        public void Hiber ()
+        {
+            uthread_suspend.Invoke (null, null);
+        }
+
+        public int Active ()
+        {
+            return (int) uthread_active.Invoke (uthread, null);
+        }
+
+        public int StackLeft ()
+        {
+            return (int) (IntPtr) uthread_stackleft.Invoke (null, null);
+        }
+    }
+}

+ 67 - 0
prebuild.xml

@@ -2418,6 +2418,73 @@
       </Files>
     </Project>
 
+    <!-- XMRengine -->
+    <Project frameworkVersion="v4_6" name="Mono.Tasklets" path="OpenSim/Region/ScriptEngine/XMREngine" type="Library">
+      <Configuration name="Debug">
+        <Options>
+          <OutputPath>../../../../bin/</OutputPath>
+        </Options>
+      </Configuration>
+      <Configuration name="Release">
+        <Options>
+          <OutputPath>../../../../bin/</OutputPath>
+        </Options>
+      </Configuration>
+
+      <ReferencePath>../../../../bin/</ReferencePath>
+
+      <Files>
+        <Match pattern="MonoTaskletsDummy.cs" recurse="true">
+             <Exclude name="obj" pattern="obj"/>
+        </Match>
+      </Files>
+    </Project>   
+    
+    <Project frameworkVersion="v4_6" name="OpenSim.Region.ScriptEngine.XMREngine" path="OpenSim/Region/ScriptEngine/XMREngine" type="Library">
+      <Configuration name="Debug">
+        <Options>
+          <OutputPath>../../../../bin/</OutputPath>
+        </Options>
+      </Configuration>
+      <Configuration name="Release">
+        <Options>
+          <OutputPath>../../../../bin/</OutputPath>
+        </Options>
+      </Configuration>
+
+      <ReferencePath>../../../../bin/</ReferencePath>
+      <Reference name="log4net"/>
+      <Reference name="Mono.Addins"/>
+      <Reference name="Mono.Tasklets"/>
+      <Reference name="Nini"/>
+      <Reference name="OpenMetaverse"/>
+      <Reference name="OpenMetaverse.StructuredData"/>
+      <Reference name="OpenMetaverseTypes"/>
+      <Reference name="OpenSim.Framework"/>
+      <Reference name="OpenSim.Framework.Console"/>
+      <Reference name="OpenSim.Framework.Monitoring"/>
+      <Reference name="OpenSim.Region.ClientStack.LindenCaps"/>
+      <Reference name="OpenSim.Region.CoreModules"/>
+      <Reference name="OpenSim.Region.Framework"/>
+      <Reference name="OpenSim.Region.ScriptEngine.Shared"/>
+      <Reference name="OpenSim.Region.ScriptEngine.Shared.Api"/>
+      <Reference name="OpenSim.Region.ScriptEngine.Shared.Api.Runtime"/>
+      <Reference name="OpenSim.Services.Interfaces"/>
+      <Reference name="System"/>
+      <Reference name="System.Drawing"/>
+      <Reference name="System.Xml"/>
+
+      <Files>
+        <Match pattern="MMR*.cs" recurse="true">
+             <Exclude name="obj" pattern="obj"/>
+        </Match>
+        <Match pattern="XMR*.cs" recurse="true">
+             <Exclude name="obj" pattern="obj"/>
+        </Match>
+      </Files>
+</Project>
+   
+    
     <!-- Addons -->
 
     <Project frameworkVersion="v4_6" name="OpenSim.Addons.OfflineIM" path="OpenSim/Addons/OfflineIM" type="Library">