From 2129d941acbc5f83aca4dc4c30a62d3226888136 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 23 Feb 2018 14:52:34 +0000 Subject: [PATCH] rename XMREngine as Yengine (still not all done), big mess source formating changes, move state files to proper folder, fix a source file locking on errors, more changes for cross platform including from Mike,... yes yes i know a messy commit --- .../XMREngine/MMRScriptBinOpStr.cs | 1559 ---- .../XMREngine/MMRScriptCodeGen.cs | 6258 -------------- .../XMREngine/MMRScriptCollector.cs | 2637 ------ .../XMREngine/MMRScriptCompValu.cs | 1677 ---- .../ScriptEngine/XMREngine/MMRScriptConsts.cs | 250 - .../XMREngine/MMRScriptInlines.cs | 666 -- .../XMREngine/MMRScriptObjCode.cs | 256 - .../XMREngine/MMRScriptObjWriter.cs | 947 --- .../XMREngine/MMRScriptTokenize.cs | 1724 ---- .../XMREngine/MMRScriptTypeCast.cs | 819 -- .../ScriptEngine/XMREngine/MMRWebRequest.cs | 269 - .../ScriptEngine/XMREngine/XMREngXmrTestLs.cs | 491 -- .../ScriptEngine/XMREngine/XMREngine.cs | 2102 ----- .../ScriptEngine/XMREngine/XMRInstAbstract.cs | 2109 ----- .../ScriptEngine/XMREngine/XMRInstSorpra.cs | 76 - .../ScriptEngine/XMREngine/XMRObjectTokens.cs | 5476 ------------- .../ScriptEngine/XMREngine/XMRScriptThread.cs | 102 - .../XMREngine/XMRScriptUThread.cs | 196 - .../MMRDelegateCommon.cs | 59 +- .../MMRIEventHandlers.cs | 73 +- .../MMRInternalFuncDict.cs | 57 +- .../ScriptEngine/YEngine/MMRScriptBinOpStr.cs | 1569 ++++ .../ScriptEngine/YEngine/MMRScriptCodeGen.cs | 7170 +++++++++++++++++ .../YEngine/MMRScriptCollector.cs | 3105 +++++++ .../ScriptEngine/YEngine/MMRScriptCompValu.cs | 1882 +++++ .../MMRScriptCompile.cs | 173 +- .../ScriptEngine/YEngine/MMRScriptConsts.cs | 287 + .../MMRScriptEventCode.cs | 80 +- .../ScriptEngine/YEngine/MMRScriptInlines.cs | 727 ++ .../MMRScriptMyILGen.cs | 53 +- .../ScriptEngine/YEngine/MMRScriptObjCode.cs | 245 + .../YEngine/MMRScriptObjWriter.cs | 1040 +++ .../{XMREngine => YEngine}/MMRScriptReduce.cs | 6357 ++++++++------- .../ScriptEngine/YEngine/MMRScriptTokenize.cs | 2972 +++++++ .../ScriptEngine/YEngine/MMRScriptTypeCast.cs | 1012 +++ .../MMRScriptVarDict.cs | 253 +- .../{XMREngine => YEngine}/XMRArray.cs | 380 +- .../ScriptEngine/YEngine/XMREngXmrTestLs.cs | 578 ++ .../Region/ScriptEngine/YEngine/XMREngine.cs | 1901 +++++ .../{XMREngine => YEngine}/XMREvents.cs | 104 +- .../{XMREngine => YEngine}/XMRHeapTracker.cs | 170 +- .../ScriptEngine/YEngine/XMRInstAbstract.cs | 2348 ++++++ .../{XMREngine => YEngine}/XMRInstBackend.cs | 407 +- .../{XMREngine => YEngine}/XMRInstCapture.cs | 200 +- .../{XMREngine => YEngine}/XMRInstCtor.cs | 501 +- .../{XMREngine => YEngine}/XMRInstMain.cs | 70 +- .../{XMREngine => YEngine}/XMRInstMisc.cs | 239 +- .../{XMREngine => YEngine}/XMRInstQueue.cs | 82 +- .../{XMREngine => YEngine}/XMRInstRun.cs | 512 +- .../ScriptEngine/YEngine/XMRObjectTokens.cs | 6296 +++++++++++++++ .../{XMREngine => YEngine}/XMRSDTypeClObj.cs | 104 +- .../ScriptEngine/YEngine/XMRScriptThread.cs | 240 + .../ScriptEngine/YEngine/XMRScriptUThread.cs | 97 + bin/OpenSim.ini.example | 11 +- prebuild.xml | 4 +- 55 files changed, 36917 insertions(+), 32055 deletions(-) delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRScriptBinOpStr.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompValu.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRScriptConsts.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjCode.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjWriter.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTokenize.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTypeCast.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/MMRWebRequest.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/XMREngXmrTestLs.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/XMREngine.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/XMRInstAbstract.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/XMRInstSorpra.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/XMRObjectTokens.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/XMRScriptThread.cs delete mode 100644 OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/MMRDelegateCommon.cs (73%) rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/MMRIEventHandlers.cs (56%) rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/MMRInternalFuncDict.cs (67%) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/MMRScriptBinOpStr.cs create mode 100644 OpenSim/Region/ScriptEngine/YEngine/MMRScriptCodeGen.cs create mode 100644 OpenSim/Region/ScriptEngine/YEngine/MMRScriptCollector.cs create mode 100644 OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompValu.cs rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/MMRScriptCompile.cs (53%) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/MMRScriptConsts.cs rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/MMRScriptEventCode.cs (65%) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/MMRScriptInlines.cs rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/MMRScriptMyILGen.cs (62%) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/MMRScriptObjCode.cs create mode 100644 OpenSim/Region/ScriptEngine/YEngine/MMRScriptObjWriter.cs rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/MMRScriptReduce.cs (50%) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/MMRScriptTokenize.cs create mode 100644 OpenSim/Region/ScriptEngine/YEngine/MMRScriptTypeCast.cs rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/MMRScriptVarDict.cs (61%) rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMRArray.cs (58%) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/XMREngXmrTestLs.cs create mode 100644 OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMREvents.cs (81%) rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMRHeapTracker.cs (60%) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/XMRInstAbstract.cs rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMRInstBackend.cs (62%) rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMRInstCapture.cs (63%) rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMRInstCtor.cs (53%) rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMRInstMain.cs (82%) rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMRInstMisc.cs (63%) rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMRInstQueue.cs (82%) rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMRInstRun.cs (70%) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/XMRObjectTokens.cs rename OpenSim/Region/ScriptEngine/{XMREngine => YEngine}/XMRSDTypeClObj.cs (77%) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/XMRScriptThread.cs create mode 100644 OpenSim/Region/ScriptEngine/YEngine/XMRScriptUThread.cs diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptBinOpStr.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptBinOpStr.cs deleted file mode 100644 index f8c9b22b6d..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptBinOpStr.cs +++ /dev/null @@ -1,1559 +0,0 @@ -/* - * 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 defined = DefineBinOps (); - - public Type outtype; // type of result of computation - public BinOpStrEmitBO emitBO; // how to compute result - public bool rmwOK; // is the = 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 , - * where and are strings returned by (TokenType...).ToString() - * and 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 DefineBinOps () - { - Dictionary bos = new Dictionary (); - - 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 bos2 = new Dictionary (); - foreach (KeyValuePair 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 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 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 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 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 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), 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 bos) - { - bos.Add ("char==char", new BinOpStr (typeof (bool), BinOpStrCharEqChar)); - bos.Add ("char!=char", new BinOpStr (typeof (bool), BinOpStrCharNeChar)); - bos.Add ("charchar", 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 bos) - { - bos.Add ("integer==integer", new BinOpStr (typeof (bool), BinOpStrIntEqInt)); - bos.Add ("integer!=integer", new BinOpStr (typeof (bool), BinOpStrIntNeInt)); - bos.Add ("integerinteger", 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), BinOpStrIntShrInt, true)); - } - - private static void DefineBinOpsKeyX (Dictionary 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 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 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 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 bos) - { - bos.Add ("string==string", new BinOpStr (typeof (bool), BinOpStrStrEqStr)); - bos.Add ("string!=string", new BinOpStr (typeof (bool), BinOpStrStrNeStr)); - bos.Add ("stringstring", 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 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; - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs deleted file mode 100644 index 3b0ba66a4d..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs +++ /dev/null @@ -1,6258 +0,0 @@ -/* - * 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 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 = "<>"; - 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 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 actCallLabels = new LinkedList (); // for the active try/catch/finally stack or the big one outside them all - private LinkedList allCallLabels = new LinkedList (); // 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 (); - - /* - * 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 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 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.gbls[] 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 ., 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 - * $get() and $set() 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 newobjDeclFuncs = new List (); - 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 '. - * 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 - */ - foreach (KeyValuePair 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 - * - * 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 (XMRInstance __sw) - * { - * = ()__sw.ehArgs[0]; - * = ()__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 statehandlers = new Dictionary (); - 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 .$new() 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 activeTemps = null; - if (!isTrivial) { - - /* - * Build list of locals and temps active at all the call labels. - */ - activeTemps = new List (); - 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 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)) { - t = HeapTrackerList.GenPush (curDeclFunc, ilGen); - } - if (t == typeof (HeapTrackerObject)) { - t = HeapTrackerObject.GenPush (curDeclFunc, ilGen); - } - if (t == typeof(HeapTrackerString)) { - t = HeapTrackerString.GenPush (curDeclFunc, ilGen); - } - 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 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) { - if (t == typeof (HeapTrackerList)) HeapTrackerList.GenPop (curDeclFunc, ilGen); - if (t == typeof (HeapTrackerObject)) HeapTrackerObject.GenPop (curDeclFunc, ilGen); - if (t == typeof (HeapTrackerString)) HeapTrackerString.GenPop (curDeclFunc, ilGen); - } 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 finallyBlocksCalled = new Stack (); - 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 .$new() 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 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 - * : // 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 - * // execute script-defined code - * // ...stack capture WILL run catch { } subblock - * leave tryEnd; // exits - * tryThrow:: - * throw new ScriptRestoreCatchException(__catThrown); // catch { } was running, jump to its beginning - * tryCallSw: // restoring... - * switch (__tryCallNo) back up into // 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: - * if (__catCallNo >= 0) goto catchCallSw; // if restoring, go check below - * // normal, execute script-defined code - * leave tryEnd; // all done, exit catch { } - * catchRetro: - * rethrow; - * catchCallSw: - * switch (__catCallNo) back up into // 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); // : - 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 (); - - GenerateStmtBlock (tryStmt.tryStmt); // output the try block statement subblock - - bool tryBlockFallsOutBottom = mightGetHere; - if (tryBlockFallsOutBottom) { - new CallLabel (this, tryStmt); // : - ilGen.Emit (tryStmt, OpCodes.Leave, tryEnd); // leave tryEnd; - openCallLabel = null; - } - - CallLabel tryThrow = new CallLabel (this, tryStmt); // tryThrow:: - 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 - * : // 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 - * // execute script-defined code - * // ...stack capture WILL run catch/finally { } subblock - * leave tryEnd; // executes finally { } subblock and exits - * tryThrow:: - * throw new ScriptRestoreCatchException(__catThrown); // catch { } was running, jump to its beginning - * tryCallSw: // restoring... - * switch (__tryCallNo) back up into // 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:; // 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 - * // normal, execute script-defined code - * finEnd: - * endfinally // jump to leave/throw target or next outer finally { } - * finCallSw: - * switch (__finCallNo) back up into // 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); // : - 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 (); - - GenerateStmtBlock (tryStmt.tryStmt); // output the try block statement subblock - - if (mightGetHere) { - new CallLabel (this, tryStmt); // : - 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:: - 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:; - 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 scorvs = new List (); - 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 ="); - return GenerateFromRVal (token.rValLeft); - } - if (!binOpStr.rmwOK) { - ErrorMsg (token, "= 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 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 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 is 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; - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs deleted file mode 100644 index 9c0d621c9d..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs +++ /dev/null @@ -1,2637 +0,0 @@ -/* - * 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 { - public List isBoxeds = new List (); - - /** - * @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 nextNodes, nextNodesCatchFinally; - public System.Collections.Generic.IEnumerable 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 { - private GraphNode gn; - public NNEnumerableCatchFinally (GraphNode gn) - { - this.gn = gn; - } - System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.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 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 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 localsWrittenBeforeRead = new List (); - public List localsReadBeforeWritten = new List (); - 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 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 leaveTargets = new List (); - - 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 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 GetNNEnumerable () - { - return new NNEnumerable (this, typeof (NNEnumerator)); - } - - private class NNEnumerator : NNEnumeratorBase { - private GraphNodeEmitNullEndfinally gn; - private IEnumerator 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 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 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 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 { - 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 System.Collections.Generic.IEnumerable.GetEnumerator () - { - return (System.Collections.Generic.IEnumerator) 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 { - protected GraphNode nn; - - public abstract bool MoveNext (); - public abstract void Reset (); - - GraphNode System.Collections.Generic.IEnumerator.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 declaredLocals = new List (); - private List definedLabels = new List (); - - 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 except ld v2 - * ldloc v2 - * ...v2 unreferenced hereafter - * With: - * ld except ld 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 except ld 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...' 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/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 localsWrittenSoFar = new List (); - - /* - * 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)); - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompValu.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompValu.cs deleted file mode 100644 index 7263274ac5..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompValu.cs +++ /dev/null @@ -1,1677 +0,0 @@ -/* - * 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 - * - * 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 - * - * 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 = [.]() - // signature = () - // 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 = .() - // signature = () - // 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) { - type.CallHeapTrackerPushMeth (errorAt, scg.ilGen); - } - } - 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) { - type.CallHeapTrackerPopMeth (errorAt, scg.ilGen); - } 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); - type.CallHeapTrackerPopMeth (errorAt, scg.ilGen); - } 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) { } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptConsts.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptConsts.cs deleted file mode 100644 index 4cbb19c6af..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptConsts.cs +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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 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 Init () - { - Dictionary sc = new Dictionary (); - - /* - * For every event code, define XMREVENTCODE_ and XMREVENTMASKn_ 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 sc, FieldInfo[] allFields) - { - List ucfs = new List (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 sc, IEnumerator 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 lc, string name, CompValu rVal) - { - lc.Add (name, this); - this.name = name; - this.rVal = rVal; - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs deleted file mode 100644 index fcb4b66dc4..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs +++ /dev/null @@ -1,666 +0,0 @@ -/* - * 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 _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 lcms = new List (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 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 (,...) - * @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 bifs = new SortedDictionary (); - 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 scs = new SortedDictionary (); - 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) // 'this' pointer for API function - scg.ilGen.Emit (errorAt, OpCodes.Ldfld, apiContextField); - - } - for (int i = 0; i < args.Length; i ++) // push arguments, boxing/unboxing as needed - args[i].PushVal (scg, errorAt, argDecl.types[i]); - - // this should not be needed -// if (methInfo.Name == "llParcelMediaQuery") { -// scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaQuery); -// } - // this should not be needed -// 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() - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjCode.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjCode.cs deleted file mode 100644 index 038dfcd22c..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjCode.cs +++ /dev/null @@ -1,256 +0,0 @@ -/* - * 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 sdObjTypesName; - // all script-defined types by name - public TokenDeclSDType[] sdObjTypesIndx; - // all script-defined types by sdTypeIndex - - public Dictionary sdDelTypes; - // all script-defined delegates (including anonymous) - - public Dictionary dynamicMethods; - // all dyanmic methods - - public Dictionary[]> 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> globalVarNames = new Dictionary> (); - - 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 names; - if (!globalVarNames.TryGetValue (gblType, out names)) { - names = new Dictionary (); - 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 (); - sdDelTypes = new Dictionary (); - 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 (); - scriptSrcLocss = new Dictionary[]> (); - - 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: - */ - foreach (KeyValuePair 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 srcLocs) - { - /* - * Save method object code pointer. - */ - dynamicMethods.Add (method.Name, method); - - /* - * Build and sort iloffset -> source code location array. - */ - int n = srcLocs.Count; - KeyValuePair[] srcLocArray = new KeyValuePair[n]; - n = 0; - foreach (KeyValuePair 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 kvpx = (KeyValuePair)x; - KeyValuePair kvpy = (KeyValuePair)y; - return kvpx.Key - kvpy.Key; - } - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjWriter.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjWriter.cs deleted file mode 100644 index e4e0ac8f4b..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptObjWriter.cs +++ /dev/null @@ -1,947 +0,0 @@ -/* - * 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 opCodes = PopulateOpCodes (); - private static Dictionary string2Type = PopulateS2T (); - private static Dictionary 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 sdTypesRev = new Dictionary (); - 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. (); - * 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 sdTypes, BinaryReader objReader, - ScriptObjCode scriptObjCode, ObjectTokens objectTokens) - { - Dictionary methods = new Dictionary (); - DynamicMethod method = null; - ILGenerator ilGen = null; - Dictionary labels = new Dictionary (); - Dictionary locals = new Dictionary (); - Dictionary labelNames = new Dictionary (); - Dictionary localNames = new Dictionary (); - object[] ilGenArg = new object[1]; - int offset = 0; - Dictionary 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 (); - 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 PopulateOpCodes () - { - Dictionary opCodeDict = new Dictionary (); - 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 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 PopulateS2T () - { - Dictionary s2t = new Dictionary (); - - 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 PopulateT2S () - { - Dictionary s2t = PopulateS2T (); - Dictionary t2s = new Dictionary (); - foreach (KeyValuePair 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 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; - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTokenize.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTokenize.cs deleted file mode 100644 index 1bdcc27dcd..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTokenize.cs +++ /dev/null @@ -1,1724 +0,0 @@ -/* - * 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 keywords = BuildKeywords (); - private static Dictionary arrayKeywords = BuildArrayKeywords (); - private static Dictionary advFlowCtlKeywords = BuildAdvFlowCtlKeywords (); - private static Dictionary tryCatchKeywords = BuildTryCatchKeywords (); - private static Dictionary objectsKeywords = BuildObjectsKeywords (); - private static Dictionary charsKeywords = BuildCharsKeywords (); - - private static Dictionary BuildKeywords () - { - Dictionary kws = new Dictionary (); - - 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 BuildArrayKeywords () - { - Dictionary kws = new Dictionary (); - - 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 BuildAdvFlowCtlKeywords () - { - Dictionary kws = new Dictionary (); - - 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 BuildTryCatchKeywords () - { - Dictionary kws = new Dictionary (); - - 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 BuildObjectsKeywords () - { - Dictionary kws = new Dictionary (); - - 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 BuildCharsKeywords () - { - Dictionary kws = new Dictionary (); - - 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 scriptIncludes = new Dictionary (); - 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 fullBuffs = new List (); - 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. - * null indicates there is no heap tracker for the type. - */ - public virtual Type ToHeapTrackerType () - { - return null; - } - public virtual ConstructorInfo GetHeapTrackerCtor () - { - throw new ApplicationException("no GetHeapTrackerCtor for " + this.GetType()); - } - public virtual void CallHeapTrackerPopMeth (Token errorAt, ScriptMyILGen ilGen) - { - throw new ApplicationException("no CallHeapTrackerPopMeth for " + this.GetType()); - } - public virtual void CallHeapTrackerPushMeth(Token errorAt, ScriptMyILGen ilGen) - { - throw new ApplicationException("no CallHeapTrackerPushMeth for " + this.GetType()); - } - } - - 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) }); - - 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 void CallHeapTrackerPopMeth (Token errorAt, ScriptMyILGen ilGen) { HeapTrackerList.GenPop(errorAt, ilGen); } - public override void CallHeapTrackerPushMeth (Token errorAt, ScriptMyILGen ilGen) { HeapTrackerList.GenPush(errorAt, ilGen); } - } - 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) }); - - 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 void CallHeapTrackerPopMeth(Token errorAt, ScriptMyILGen ilGen) { HeapTrackerObject.GenPop (errorAt, ilGen); } - public override void CallHeapTrackerPushMeth(Token errorAt, ScriptMyILGen ilGen) { HeapTrackerObject.GenPush(errorAt, ilGen); } - } - 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) }); - - 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 void CallHeapTrackerPopMeth(Token errorAt, ScriptMyILGen ilGen) { HeapTrackerString.GenPop(errorAt, ilGen); } - public override void CallHeapTrackerPushMeth (Token errorAt, ScriptMyILGen ilGen) { HeapTrackerString.GenPush(errorAt, ilGen); } - } - 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); } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTypeCast.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTypeCast.cs deleted file mode 100644 index c8be7bbef7..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptTypeCast.cs +++ /dev/null @@ -1,819 +0,0 @@ -/* - * 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 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 for IMPLICIT casting. - * Key is of the form * for EXPLICIT casting. - * Value is a delegate that generates code to perform the type cast. - */ - private static Dictionary CreateLegalTypeCasts () - { - Dictionary ltc = new Dictionary (); - - // 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; - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRWebRequest.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRWebRequest.cs deleted file mode 100644 index 42865861c4..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRWebRequest.cs +++ /dev/null @@ -1,269 +0,0 @@ -/* - * 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 + " in " + requestUrl); - } - 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; - } - } - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMREngXmrTestLs.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMREngXmrTestLs.cs deleted file mode 100644 index 266c5aa367..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMREngXmrTestLs.cs +++ /dev/null @@ -1,491 +0,0 @@ -/* - * 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= -out= -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(instances, CompareInstancesByCPUTime); - } - - /* - * Print the entries. - */ - if (!flagFull) { - outFile.WriteLine(" ItemID" + - " CPU(ms)" + - " NumEvents" + - " Status " + - " World Position " + - " :"); - } - 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 selargs = new List (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 | "); - 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 , 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 valuelist = new List (); - 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 valuelist = new List (); - 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 | "); - 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; - } - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMREngine.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMREngine.cs deleted file mode 100644 index 7447f2f55e..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMREngine.cs +++ /dev/null @@ -1,2102 +0,0 @@ -/* - * 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. - */ - -// based on XMREngine from Mike Rieker (Dreamnation) and Melanie Thielker -// but with several changes to be more cross platform. - - -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 OpenMetaverse; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -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 m_ScriptErrors = - new Dictionary(); - private Dictionary> m_ObjectItemList = - new Dictionary>(); - private Dictionary m_ObjectInstArray = - new Dictionary(); - public Dictionary m_XMRInstanceApiCtxFieldInfos = - new Dictionary (); - private int m_StackSize; - private int m_HeapSize; - - private XMRScriptThread[] m_ScriptThreads; - private int m_WakeUpOne = 0; - public object m_WakeUpLock = new object(); - private Dictionary m_AllThreads = new Dictionary (); - - private bool m_SuspendScriptThreadFlag = false; - /** - * @brief Something was just added to the Start or Yield queue so - * wake one of the XMRScriptThread instances to run it. - */ - - private Thread m_SleepThread = 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 m_InstancesDict = - new Dictionary(); - public Queue m_ThunkQueue = new Queue (); - 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() - { - } - - 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 WakeUpOne() - { - lock (m_WakeUpLock) - { - m_WakeUpOne++; - Monitor.Pulse(m_WakeUpLock); - } - } - - public void AddThread(Thread thd, XMRScriptThread xthd) - { - lock(m_AllThreads) - m_AllThreads.Add(thd, xthd); - } - - public void RemoveThread(Thread thd) - { - lock(m_AllThreads) - m_AllThreads.Remove(thd); - } - - public XMRScriptThread CurrentScriptThread () - { - XMRScriptThread st; - lock (m_AllThreads) - m_AllThreads.TryGetValue (Thread.CurrentThread, out st); - - return st; - } - - 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; - - Type uThreadType = null; - uThreadType = typeof (ScriptUThread_Nul); - uThreadCtor = uThreadType.GetConstructor (new Type[] { typeof (XMRInstance) }); - - m_UseSourceHashCode = m_Config.GetBoolean("UseSourceHashCode", false); - numThreadScriptWorkers = m_Config.GetInt("NumThreadScriptWorkers", 3); - m_ScriptThreads = new XMRScriptThread[numThreadScriptWorkers]; - - m_TraceCalls = m_Config.GetBoolean("TraceCalls", false); - m_Verbose = m_Config.GetBoolean("Verbose", false); - m_ScriptDebug = m_Config.GetBoolean("ScriptDebug", false); - - // 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; - } - - for (int i = 0; i < numThreadScriptWorkers; i ++) - { - m_ScriptThreads[i] = new XMRScriptThread(this, i);; - } - - - m_SleepThread = StartMyThread(RunSleepThread, "xmrengine sleep", ThreadPriority.Normal); - - 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(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(this); - } - - private void OneTimeLateInitialization () - { - // Build list of defined APIs and their 'this' types and define a field in XMRInstanceSuperType. - ApiManager am = new ApiManager (); - Dictionary apiCtxTypes = new Dictionary (); - 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_ 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); -#if DEBUG - Type daType = typeof(DebuggableAttribute); - ConstructorInfo daCtor = daType.GetConstructor(new Type[] { typeof(DebuggableAttribute.DebuggingModes) }); - - CustomAttributeBuilder daBuilder = new CustomAttributeBuilder(daCtor, new object[] { - DebuggableAttribute.DebuggingModes.DisableOptimizations | - DebuggableAttribute.DebuggingModes.EnableEditAndContinue | - DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | - DebuggableAttribute.DebuggingModes.Default }); - - assemblyBuilder.SetCustomAttribute(daBuilder); -#endif - 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_ field of XMRInstanceSuperType. - - foreach (KeyValuePair 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 (); - 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 consts = comms.GetConstants (); - foreach (KeyValuePair 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; - - 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; - - for (int i = 0; i < numThreadScriptWorkers; i ++) - { - XMRScriptThread scriptThread = m_ScriptThreads[i]; - if (scriptThread != null) - { - scriptThread.WakeUpScriptThread(); - Monitor.PulseAll (m_WakeUpLock); - scriptThread.Terminate(); - m_ScriptThreads[i] = null; - } - } - - if (m_SleepThread != null) - { - lock (m_SleepQueue) - { - Monitor.PulseAll (m_SleepQueue); - } - if(!m_SleepThread.Join(250)) - m_SleepThread.Abort(); - m_SleepThread = null; - } - - 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 ++) { - 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 []"); - 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 [] - show/set compiled version value"); - m_log.Info ("[XMREngine]: xmr gc"); - m_log.Info ("[XMREngine]: xmr ls [-help ...]"); - m_log.Info ("[XMREngine]: xmr mvv [] - 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 []"); - break; - } - break; - - case "pev": - XmrTestPev (args, 2); - break; - - case "reset": - XmrTestReset (args, 2); - break; - - case "resume": - m_log.Info ("[XMREngine]: resuming scripts"); - m_SuspendScriptThreadFlag = false; - for (int i = 0; i < numThreadScriptWorkers; i ++) - m_ScriptThreads[i].WakeUpScriptThread(); - Monitor.PulseAll(m_WakeUpLock); - break; - - case "suspend": - m_log.Info ("[XMREngine]: suspending scripts"); - m_SuspendScriptThreadFlag = true; - for (int i = 0; i < numThreadScriptWorkers; i ++) - m_ScriptThreads[i].WakeUpScriptThread(); - Monitor.PulseAll(m_WakeUpLock); - 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!!"); - } - -#pragma warning disable 0067 - public event ScriptRemoved OnScriptRemoved; - public event ObjectRemoved OnObjectRemoved; -#pragma warning restore 0067 - - // 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(); - 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 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 ... 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 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; - - // ... 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 ... 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 (); - 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 itemIDList; - if (!m_ObjectItemList.TryGetValue(instance.m_PartUUID, out itemIDList)) - { - itemIDList = new List(); - 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 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(); - 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); - } - WakeUpOne(); - } - - public void QueueToTrunk(ThreadStart thds) - { - lock (m_WakeUpLock) - m_ThunkQueue.Enqueue (thds); - 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. - 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); - } - WakeUpOne (); - } - } - - /** - * @brief Thread that runs a time slicer. - */ - 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 GetObjectScriptsExecutionTimes () - { - Dictionary topScripts = new Dictionary (); - 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 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 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.IsBackground = true; - 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); - } - - public void RunScriptThread(XMRScriptThread xthd) - { - XMRInstance inst; - while (!m_Exiting) - { - Watchdog.UpdateThread(); - - /* - * Handle 'xmr resume/suspend' commands. - */ - if (m_SuspendScriptThreadFlag) - { - lock (m_WakeUpLock) - { - while (m_SuspendScriptThreadFlag && - !m_Exiting && - (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 (m_ThunkQueue.Count > 0) - thunk = m_ThunkQueue.Dequeue (); - } - if (thunk != null) - { - inst = (XMRInstance)thunk.Target; - thunk (); - if (m_Exiting || m_SuspendScriptThreadFlag) - continue; - } - - if (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 (m_StartQueue) - { - inst = m_StartQueue.RemoveHead(); - } - if (inst == null) break; - if (inst.m_IState != XMRInstState.ONSTARTQ) throw new Exception("bad state"); - xthd.RunInstance (inst); - if (m_Exiting || m_SuspendScriptThreadFlag) - continue; - } - - // 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 (m_YieldQueue) - { - inst = m_YieldQueue.RemoveHead(); - } - if (inst != null) - { - if (inst.m_IState != XMRInstState.ONYIELDQ) throw new Exception("bad state"); - xthd.RunInstance(inst); - numStarts = -1; - } - - // If we left something dangling in the m_StartQueue or m_YieldQueue, go back to check it. - if (m_Exiting || numStarts < 0) - continue; - } - - // Nothing to do, sleep. - lock (m_WakeUpLock) - { - if (!xthd.m_WakeUpThis && (m_WakeUpOne <= 0) && !m_Exiting) - Monitor.Wait(m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2); - - xthd.m_WakeUpThis = false; - if ((m_WakeUpOne > 0) && (--m_WakeUpOne > 0)) - Monitor.Pulse (m_WakeUpLock); - } - } - Watchdog.RemoveThread(true); - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstAbstract.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstAbstract.cs deleted file mode 100644 index 1ea05b6042..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstAbstract.cs +++ /dev/null @@ -1,2109 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using OpenSim.Region.ScriptEngine.Shared.ScriptBase; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Reflection.Emit; -using System.Text; -using System.Threading; - -using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; -using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; -using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; -using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; -using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; -using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; -using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; - -namespace OpenSim.Region.ScriptEngine.XMREngine -{ - public class XMRInstArrays { - public XMR_Array[] iarArrays; - public char[] iarChars; - public double[] iarFloats; - public int[] iarIntegers; - public LSL_List[] iarLists; - public object[] iarObjects; - public LSL_Rotation[] iarRotations; - public string[] iarStrings; - public LSL_Vector[] iarVectors; - public XMRSDTypeClObj[] iarSDTClObjs; - public Delegate[][] iarSDTIntfObjs; - - private XMRInstAbstract instance; - private int heapUse; - - private static readonly XMR_Array[] noArrays = new XMR_Array[0]; - private static readonly char[] noChars = new char[0]; - private static readonly double[] noFloats = new double[0]; - private static readonly int[] noIntegers = new int[0]; - private static readonly LSL_List[] noLists = new LSL_List[0]; - private static readonly object[] noObjects = new object[0]; - private static readonly LSL_Rotation[] noRotations = new LSL_Rotation[0]; - private static readonly string[] noStrings = new string[0]; - private static readonly LSL_Vector[] noVectors = new LSL_Vector[0]; - private static readonly XMRSDTypeClObj[] noSDTClObjs = new XMRSDTypeClObj[0]; - private static readonly Delegate[][] noSDTIntfObjs = new Delegate[0][]; - - public XMRInstArrays (XMRInstAbstract inst) - { - instance = inst; - } - - ~XMRInstArrays () - { - heapUse = instance.UpdateHeapUse (heapUse, 0); - } - - public void AllocVarArrays (XMRInstArSizes ars) - { - ClearOldArrays (); - - heapUse = instance.UpdateHeapUse (heapUse, - ars.iasChars * HeapTrackerObject.HT_CHAR + - ars.iasFloats * HeapTrackerObject.HT_SFLT + - ars.iasIntegers * HeapTrackerObject.HT_INT + - ars.iasRotations * HeapTrackerObject.HT_ROT + - ars.iasVectors * HeapTrackerObject.HT_VEC + - ars.iasSDTIntfObjs * HeapTrackerObject.HT_DELE); - - iarArrays = (ars.iasArrays > 0) ? new XMR_Array [ars.iasArrays] : noArrays; - iarChars = (ars.iasChars > 0) ? new char [ars.iasChars] : noChars; - iarFloats = (ars.iasFloats > 0) ? new double [ars.iasFloats] : noFloats; - iarIntegers = (ars.iasIntegers > 0) ? new int [ars.iasIntegers] : noIntegers; - iarLists = (ars.iasLists > 0) ? new LSL_List [ars.iasLists] : noLists; - iarObjects = (ars.iasObjects > 0) ? new object [ars.iasObjects] : noObjects; - iarRotations = (ars.iasRotations > 0) ? new LSL_Rotation [ars.iasRotations] : noRotations; - iarStrings = (ars.iasStrings > 0) ? new string [ars.iasStrings] : noStrings; - iarVectors = (ars.iasVectors > 0) ? new LSL_Vector [ars.iasVectors] : noVectors; - iarSDTClObjs = (ars.iasSDTClObjs > 0) ? new XMRSDTypeClObj[ars.iasSDTClObjs] : noSDTClObjs; - iarSDTIntfObjs = (ars.iasSDTIntfObjs > 0) ? new Delegate [ars.iasSDTIntfObjs][] : noSDTIntfObjs; - } - - /** - * @brief Do not write directly to iarLists[index], rather use this method. - */ - public void PopList (int index, LSL_List lis) - { - LSL_List old = iarLists[index]; - int newheapuse = heapUse + HeapTrackerList.Size (lis) - HeapTrackerList.Size (old); - heapUse = instance.UpdateHeapUse (heapUse, newheapuse); - iarLists[index] = lis; - } - - /** - * @brief Do not write directly to iarObjects[index], rather use this method. - */ - public void PopObject (int index, object obj) - { - object old = iarObjects[index]; - int newheapuse = heapUse + HeapTrackerObject.Size (obj) - HeapTrackerObject.Size (old); - heapUse = instance.UpdateHeapUse (heapUse, newheapuse); - iarObjects[index] = obj; - } - - /** - * @brief Do not write directly to iarStrings[index], rather use this method. - */ - public void PopString (int index, string str) - { - string old = iarStrings[index]; - int newheapuse = heapUse + HeapTrackerString.Size (str) - HeapTrackerString.Size (old); - heapUse = instance.UpdateHeapUse (heapUse, newheapuse); - iarStrings[index] = str; - } - - /** - * @brief Write all arrays out to a file. - */ - public delegate void Sender (object value); - public void SendArrays (Sender sender) - { - sender (iarArrays); - sender (iarChars); - sender (iarFloats); - sender (iarIntegers); - sender (iarLists); - sender (iarObjects); - sender (iarRotations); - sender (iarStrings); - sender (iarVectors); - sender (iarSDTClObjs); - sender (iarSDTIntfObjs); - } - - /** - * @brief Read all arrays in from a file. - */ - public delegate object Recver (); - public void RecvArrays (Recver recver) - { - ClearOldArrays (); - - iarArrays = (XMR_Array[]) recver (); - char[] chrs = (char[]) recver (); - double[] flts = (double[]) recver (); - int[] ints = (int[]) recver (); - LSL_List[] liss = (LSL_List[]) recver (); - object[] objs = (object[]) recver (); - LSL_Rotation[] rots = (LSL_Rotation[]) recver (); - string[] strs = (string[]) recver (); - LSL_Vector[] vecs = (LSL_Vector[]) recver (); - iarSDTClObjs = (XMRSDTypeClObj[]) recver (); - Delegate[][] dels = (Delegate[][]) recver (); - - int newheapuse = heapUse; - - // value types simply are the size of the value * number of values - newheapuse += chrs.Length * HeapTrackerObject.HT_CHAR; - newheapuse += flts.Length * HeapTrackerObject.HT_SFLT; - newheapuse += ints.Length * HeapTrackerObject.HT_INT; - newheapuse += rots.Length * HeapTrackerObject.HT_ROT; - newheapuse += vecs.Length * HeapTrackerObject.HT_VEC; - newheapuse += dels.Length * HeapTrackerObject.HT_DELE; - - // lists, objects, strings are the sum of the size of each element - foreach (LSL_List lis in liss) - newheapuse += HeapTrackerList.Size (lis); - - foreach (object obj in objs) - newheapuse += HeapTrackerObject.Size (obj); - - foreach (string str in strs) - newheapuse += HeapTrackerString.Size (str); - - // others (XMR_Array, XMRSDTypeClObj) keep track of their own heap usage - - // update script heap usage, throwing an exception before finalizing changes - heapUse = instance.UpdateHeapUse (heapUse, newheapuse); - - iarChars = chrs; - iarFloats = flts; - iarIntegers = ints; - iarLists = liss; - iarObjects = objs; - iarRotations = rots; - iarStrings = strs; - iarVectors = vecs; - iarSDTIntfObjs = dels; - } - - private void ClearOldArrays () - { - int newheapuse = heapUse; - - iarArrays = null; - if (iarChars != null) - { - newheapuse -= iarChars.Length * HeapTrackerObject.HT_CHAR; - iarChars = null; - } - if (iarFloats != null) - { - newheapuse -= iarFloats.Length * HeapTrackerObject.HT_SFLT; - iarFloats = null; - } - if (iarIntegers != null) - { - newheapuse -= iarIntegers.Length * HeapTrackerObject.HT_INT; - iarIntegers = null; - } - if (iarLists != null) - { - foreach (LSL_List lis in iarLists) - newheapuse -= HeapTrackerList.Size (lis); - - iarLists = null; - } - if (iarObjects != null) - { - foreach (object obj in iarObjects) - newheapuse -= HeapTrackerObject.Size (obj); - - iarObjects = null; - } - if (iarRotations != null) - { - newheapuse -= iarRotations.Length * HeapTrackerObject.HT_ROT; - iarRotations = null; - } - if (iarStrings != null) - { - foreach (string str in iarStrings) - newheapuse -= HeapTrackerString.Size (str); - - iarStrings = null; - } - if (iarVectors != null) - { - newheapuse -= iarVectors.Length * HeapTrackerObject.HT_VEC; - iarVectors = null; - } - iarSDTClObjs = null; - if (iarSDTIntfObjs != null) - { - newheapuse -= iarSDTIntfObjs.Length * HeapTrackerObject.HT_DELE; - iarSDTIntfObjs = null; - } - - heapUse = instance.UpdateHeapUse (heapUse, newheapuse); - } - } - - public class XMRInstArSizes - { - public int iasArrays; - public int iasChars; - public int iasFloats; - public int iasIntegers; - public int iasLists; - public int iasObjects; - public int iasRotations; - public int iasStrings; - public int iasVectors; - public int iasSDTClObjs; - public int iasSDTIntfObjs; - - public void WriteAsmFile (TextWriter asmFileWriter, string label) - { - asmFileWriter.WriteLine (" {0}Arrays {1}", label, iasArrays); - asmFileWriter.WriteLine (" {0}Chars {1}", label, iasChars); - asmFileWriter.WriteLine (" {0}Floats {1}", label, iasFloats); - asmFileWriter.WriteLine (" {0}Integers {1}", label, iasIntegers); - asmFileWriter.WriteLine (" {0}Lists {1}", label, iasLists); - asmFileWriter.WriteLine (" {0}Objects {1}", label, iasObjects); - asmFileWriter.WriteLine (" {0}Rotations {1}", label, iasRotations); - asmFileWriter.WriteLine (" {0}Strings {1}", label, iasStrings); - asmFileWriter.WriteLine (" {0}Vectors {1}", label, iasVectors); - asmFileWriter.WriteLine (" {0}SDTClObjs {1}", label, iasSDTClObjs); - asmFileWriter.WriteLine (" {0}SDTIntfObjs {1}", label, iasSDTIntfObjs); - } - public void WriteToFile (BinaryWriter objFileWriter) - { - objFileWriter.Write (iasArrays); - objFileWriter.Write (iasChars); - objFileWriter.Write (iasFloats); - objFileWriter.Write (iasIntegers); - objFileWriter.Write (iasLists); - objFileWriter.Write (iasObjects); - objFileWriter.Write (iasRotations); - objFileWriter.Write (iasStrings); - objFileWriter.Write (iasVectors); - objFileWriter.Write (iasSDTClObjs); - objFileWriter.Write (iasSDTIntfObjs); - } - public void ReadFromFile (BinaryReader objFileReader) - { - iasArrays = objFileReader.ReadInt32 (); - iasChars = objFileReader.ReadInt32 (); - iasFloats = objFileReader.ReadInt32 (); - iasIntegers = objFileReader.ReadInt32 (); - iasLists = objFileReader.ReadInt32 (); - iasObjects = objFileReader.ReadInt32 (); - iasRotations = objFileReader.ReadInt32 (); - iasStrings = objFileReader.ReadInt32 (); - iasVectors = objFileReader.ReadInt32 (); - iasSDTClObjs = objFileReader.ReadInt32 (); - iasSDTIntfObjs = objFileReader.ReadInt32 (); - } - } - - public class XMRStackFrame - { - public XMRStackFrame nextSF; - public string funcName; - public int callNo; - public object[] objArray; - } - - /* - * Contains only items required by the stand-alone compiler - * so the compiler doesn't need to pull in all of OpenSim. - * - * Inherit from ScriptBaseClass so we can be used as 'this' - * parameter for backend-API calls, eg llSay(). - */ - public abstract class XMRInstAbstract : ScriptBaseClass - { - public const int CallMode_NORMAL = 0; // when function is called, it proceeds normally - public const int CallMode_SAVE = 1; // StackSaveException() was thrown, push args/locals to stackFrames - public const int CallMode_RESTORE = 2; // when function is called, it pops state from stackFrames - - public bool suspendOnCheckRunHold; // suspend script execution until explicitly set false - public bool suspendOnCheckRunTemp; // suspend script execution for single step only - public int stackLimit; // stack must have at least this many bytes free on entry to functions - - public ScriptObjCode m_ObjCode; // script object code this instance was created from - - public object[] ehArgs; // event handler argument array - public bool doGblInit = true; // default state_entry() needs to initialize global variables - public int stateCode = 0; // state the script is in (0 = 'default') - public int newStateCode = -1; // if >= 0, in the middle of exiting 'stateCode' and entering 'newStateCode' - public ScriptEventCode eventCode = ScriptEventCode.None; - // what event handler is executing (or None if not) - - public int callMode = CallMode_NORMAL; - // to capture stack frames on stackFrames: - // set to CallMode_SAVE just before throwing StackSaveException() - // from within CheckRun() and cleared to CallMode_NORMAL when - // the exception is caught - // to restore stack frames from stackFrames: - // set to CallMode_RESTORE just before calling CallSEH() and - // cleared to CallMode_NORMAL by CheckRun() - public XMRStackFrame stackFrames; // stack frames being saved/restored - - private static readonly char[] justacomma = { ',' }; - - /* - * These arrays hold the global variable values for the script instance. - * The array lengths are determined by the script compilation, - * and are found in ScriptObjCode.glblSizes. - */ - public XMRInstArrays glblVars; - - public XMRInstAbstract () - { - glblVars = new XMRInstArrays (this); - } - - /****************************************************************\ - * Abstract function prototypes. * - * These functions require access to the OpenSim environment. * - \****************************************************************/ - - public abstract void CheckRunWork (); - public abstract void StateChange (); - public abstract int xmrStackLeft (); - - [xmrMethodCallsCheckRunAttribute] // calls CheckRun() - [xmrMethodIsNoisyAttribute] // calls Stub() - public abstract LSL_List xmrEventDequeue (double timeout, int returnMask1, int returnMask2, - int backgroundMask1, int backgroundMask2); - - [xmrMethodIsNoisyAttribute] // calls Stub() - public abstract void xmrEventEnqueue (LSL_List ev); - - [xmrMethodIsNoisyAttribute] // calls Stub() - public abstract LSL_List xmrEventSaveDets (); - - [xmrMethodIsNoisyAttribute] // calls Stub() - public abstract void xmrEventLoadDets (LSL_List dpList); - - [xmrMethodIsNoisyAttribute] // calls Stub() - public abstract void xmrTrapRegionCrossing (int en); - - [xmrMethodIsNoisyAttribute] // calls Stub() - 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 ;' 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) is the same. - * And there must be a '[' in each meaning that it is a script-defined array type. - */ - int i = srctypename.IndexOf ('['); - int j = dsttypename.IndexOf ('['); - if ((i < 0) || (j < 0)) - throw new InvalidCastException ("non-array passed: " + srctypename + " and/or " + dsttypename); - if ((i != j) || !srctypename.StartsWith (dsttypename.Substring (0, j))) - throw new ArrayTypeMismatchException (srctypename + " vs " + dsttypename); - - - /* - * The number of brackets must match exactly. - * This permits copying from something like a float[,][] to something like a float[][]. - * But you cannot copy from a float[][] to a float[] or wisa wersa. - * Counting either '[' or ']' would work equally well. - */ - int srclen = srctypename.Length; - int dstlen = dsttypename.Length; - int srcjags = 0; - int dstjags = 0; - while (++ i < srclen) - if (srctypename[i] == ']') - srcjags ++; - while (++ j < dstlen) - if (dsttypename[j] == ']') - dstjags ++; - if (dstjags != srcjags) - throw new ArrayTypeMismatchException (srctypename + " vs " + dsttypename); - - - /* - * Perform the copy. - */ - Array srcarray = (Array)srcsdt.instVars.iarObjects[0]; - Array dstarray = (Array)dstsdt.instVars.iarObjects[0]; - Array.Copy (srcarray, srcstart, dstarray, dststart, count); - } - - /** - * @brief Copy from an array to a list. - * @param srcar = the array to copy from - * @param start = where to start in the array - * @param count = number of elements - * @returns the list - */ - public static LSL_List xmrArray2List (object srcar, int start, int count) - { - /* - * Get the script-visible type of the array. - * We only do arrays. - */ - XMRSDTypeClObj array = (XMRSDTypeClObj)srcar; - TokenDeclSDTypeClass sdtClass = array.sdtcClass; - if (sdtClass.arrayOfRank == 0) - throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val); - - - /* - * Validate objects they want to put in the list. - * We can't allow anything funky that OpenSim runtime doesn't expect. - */ - Array srcarray = (Array)array.instVars.iarObjects[0]; - object[] output = new object[count]; - for (int i = 0; i < count; i ++) - { - object src = srcarray.GetValue (i + start); - if (src == null) - throw new NullReferenceException ("null element " + i); - if (src is double) - { - output[i] = new LSL_Float ((double)src); - continue; - } - if (src is int) - { - output[i] = new LSL_Integer ((int)src); - continue; - } - if (src is LSL_Rotation) - { - output[i] = src; - continue; - } - if (src is LSL_Vector) - { - output[i] = src; - continue; - } - if (src is string) - { - output[i] = new LSL_String ((string)src); - continue; - } - throw new InvalidCastException ("invalid element " + i + " type " + src.GetType ().Name); - } - - /* - * Make a list out of that now immutable array. - */ - return new LSL_List (output); - } - - /** - * @brief Copy from a list to an array. - * @param srclist = list to copy from - * @param srcstart = where to start in the list - * @param dstobj = array to copy to - * @param dststart = where to start in the array - * @param count = number of elements - */ - public static void xmrList2Array (LSL_List srclist, int srcstart, object dstobj, int dststart, int count) - { - /* - * Get the script-visible type of the destination. - * We only do arrays. - */ - XMRSDTypeClObj dstarray = (XMRSDTypeClObj)dstobj; - TokenDeclSDTypeClass sdtClass = dstarray.sdtcClass; - if (sdtClass.arrayOfType == null) - throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val); - - /* - * Copy from the immutable array to the mutable array. - * Strip off any LSL wrappers as the script code doesn't expect any. - */ - object[] srcarr = srclist.Data; - Array dstarr = (Array)dstarray.instVars.iarObjects[0]; - - for (int i = 0; i < count; i ++) - { - object obj = srcarr[i+srcstart]; - if (obj is LSL_Float) obj = ((LSL_Float)obj).value; - else if (obj is LSL_Integer) obj = ((LSL_Integer)obj).value; - else if (obj is LSL_String) obj = ((LSL_String)obj).m_string; - dstarr.SetValue (obj, i + dststart); - } - } - - /** - * @brief Copy from an array of characters to a string. - * @param srcar = the array to copy from - * @param start = where to start in the array - * @param count = number of elements - * @returns the string - */ - public static string xmrChars2String (object srcar, int start, int count) - { - /* - * Make sure they gave us a script-defined array object. - */ - XMRSDTypeClObj array = (XMRSDTypeClObj)srcar; - TokenDeclSDTypeClass sdtClass = array.sdtcClass; - if (sdtClass.arrayOfRank == 0) - throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val); - - /* - * We get a type cast error from mono if they didn't give us a character array. - * But if it is ok, create a string from the requested characters. - */ - char[] srcarray = (char[])array.instVars.iarObjects[0]; - return new string (srcarray, start, count); - } - - /** - * @brief Copy from a string to a character array. - * @param srcstr = string to copy from - * @param srcstart = where to start in the string - * @param dstobj = array to copy to - * @param dststart = where to start in the array - * @param count = number of elements - */ - public static void xmrString2Chars (string srcstr, int srcstart, object dstobj, int dststart, int count) - { - /* - * Make sure they gave us a script-defined array object. - */ - XMRSDTypeClObj dstarray = (XMRSDTypeClObj)dstobj; - TokenDeclSDTypeClass sdtClass = dstarray.sdtcClass; - if (sdtClass.arrayOfType == null) - throw new InvalidCastException ("only do arrays not " + sdtClass.longName.val); - - /* - * We get a type cast error from mono if they didn't give us a character array. - * But if it is ok, copy from the string to the character array. - */ - char[] dstarr = (char[])dstarray.instVars.iarObjects[0]; - for (int i = 0; i < count; i ++) - dstarr[i+dststart] = srcstr[i+srcstart]; - } - - /** - * @brief Implement osParseJSON() so we return an array to the script. - * No coherent example of its use in scripts on web found. - * see http://www.json.org/ for more details on JSON - */ - private static LSL_List nullList = new LSL_List (new object[0]); - public new XMR_Array osParseJSON (string json) - { - XMR_Array dict = new XMR_Array (this); - int idx = ParseJSON (dict, nullList, json, 0); - while (idx < json.Length) - { - if (json[idx] > ' ') - throw new Exception ("left-over json " + json); - idx ++; - } - return dict; - } - - private static int ParseJSON (XMR_Array dict, LSL_List keys, string json, int idx) - { - char c; - - while ((c = json[idx++]) <= ' ') { } - switch (c) - { - - // '{' ':' [ ',' ':' ... ] '}' - 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; - } - - // '[' [ ',' ... ] ']' - 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; - } - - // '"''"' - 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 (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) - 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[] 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 (); - this.migrateOutLists = new Dictionary (); - 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 migrateOutObjects; - private Dictionary 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 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 (); - 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 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() 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 { } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstSorpra.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstSorpra.cs deleted file mode 100644 index dd60cb2714..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstSorpra.cs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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; - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRObjectTokens.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRObjectTokens.cs deleted file mode 100644 index f214f2870a..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRObjectTokens.cs +++ /dev/null @@ -1,5476 +0,0 @@ -/* - * 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 labelNames; - private Dictionary 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 (); - localNames = new Dictionary (); - - 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 typeTranslator = InitTypeTranslator (); - private static Dictionary InitTypeTranslator () - { - Dictionary d = new Dictionary (); - 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 eharglist; - private Dictionary labels; - private Dictionary locals; - private Dictionary methargnames; - private LinkedList cilinstrs; - private OTStmtBlock topBlock; - private Stack opstack; - private Stack trystack; - private Stack 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 (); - } - - 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 (); - labels = new Dictionary (); - locals = new Dictionary (); - cilinstrs = new LinkedList (); - opstack = new Stack (); - trystack = new Stack (); - blockstack = new Stack (); - - 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 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_; 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 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 namecounts = new Dictionary (); - foreach (Dictionary 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 inittypes = new Dictionary (); - 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 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 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 link) - { - OTStmtLabel.AddLast (decompile, this); - } - } - - /* - * 'try {' - */ - private class OTCilBegExcBlk : OTCilInstr { - public LinkedList catches = new LinkedList (); - - public OTCilBegExcBlk (int offset) : base (offset) - { } - - public override string DumpString () - { - return "try {"; - } - - public override void BuildStatements (OTDecompile decompile, LinkedListNode 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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[] is a reference to a global variable - * likewise so is __xmrinst.glblVars.iar[] - */ - if ((array is OTOpndField) && (index is OTOpndInt)) { - - /* - * arrayfield = (arg$0.glblVars).iar - * arrayfieldobj = arg$0.glblVars - * iartypename = iar - */ - 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 xor1ops = InitXor1Ops (); - - private static Dictionary InitXor1Ops () - { - Dictionary d = new Dictionary (); - 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 precedence = InitPrecedence (); - private static Dictionary InitPrecedence () - { - Dictionary d = new Dictionary (); - 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 mathmeths = InitMathMeths (); - private static Dictionary InitMathMeths () - { - Dictionary d = new Dictionary (); - 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 - * 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 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$ = newarr object[] << link points here - * dup$[0] = bla - * dup$[1] = bla - * ... - * ... newobj list (dup$) ... - */ - public static bool Detect (LinkedListNode link) - { - if (link == null) return false; - - /* - * Check for 'dup$ = newarr object[]' and get listsize from . - */ - 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$[] = bla - * If so, save the bla values in the values[] array. - */ - LinkedListNode 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$)' 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 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) => - 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) => - 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 binops = InitBinops (); - private static Dictionary InitBinops () - { - Dictionary d = new Dictionary (); - 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 brfops = InitBrfOps (); - private static Dictionary InitBrfOps () - { - Dictionary d = new Dictionary (); - 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 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 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 link) - { - // strip all statements following jump until seeing some label - bool rc = false; - if (link != null) { - LinkedListNode 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 link) - { - return catchblock.StripStuff (null); - } - - public override bool ReplaceOperand (OTOpnd oldopnd, OTOpnd newopnd) - { - return catchblock.ReplaceOperand (oldopnd, newopnd); - } - - public override bool DetectDoForIfWhile (LinkedListNode 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 catches = new LinkedList (); - - // 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 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 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 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 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 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 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 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 ScanForLabel (LinkedListNode 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 link, LinkedListNode donelink) - { - LinkedListNode 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 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 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 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 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 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 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 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 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 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 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 blkstmts = new LinkedList (); - - 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 link) - { - // loop through all sub-statements to strip out behind-the-scenes references - bool rc = false; - loop: - for (LinkedListNode 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 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 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 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_; << link points here - * ... ... - * [ if (dotest) ] jump doloop_; - */ - public static bool Detect (LinkedListNode 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 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 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 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 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... - * - * @forloop_; << link points here - * [ if () jump forbreak_; ] - * ... ... - * jump forloop_; - * [ @forbreak_; ] - */ - public static bool Detect (LinkedListNode 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_;' statement - // also gather up the statements for the for body block - it.forbody = new OTStmtBlock (); - LinkedListNode 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 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 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 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_; << link points here - * ... then body ... - * @ifdone_; - * - * if (condition) jump ifelse_; - * ... then body ... - * jump ifdone_; << optional if true body doesn't fall through - * @ifelse_; - * ... else body ... - * @ifdone_; - */ - public static bool Detect (LinkedListNode 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_ label - // also save the intervening statements for the then body - OTStmtBlock thenbody; - LinkedListNode donelink = ScanForLabel (link, jumpstmt.label, out thenbody); - - // make sure we found matching label - if (donelink == null) return false; - - // replace the jump ifdone_ with the - 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_ 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_ label - // also save the intervening statements for the true body - OTStmtBlock thenbody; - LinkedListNode 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_ - LinkedListNode 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_ label - // also save the intervening statements for the else body - OTStmtBlock elsebody; - LinkedListNode donelink = ScanForLabel (elselink, jumpifdone.label, out elsebody); - if (donelink != null) { - - // replace the jump ifdone_ with the - 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_ 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_, so make it a simple if/then - // if (condition) jump ifelse_; << link - // ... then body ... << encapsulated in block thenbody - // @ifelse_; << elselink - // ... else body ... << still inline and leave it there - // @ifdone_; << strip this out - - // replace the jump ifelse_ with the - 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_ label - StripInterveningStatements (link, elselink); - - // there's a dangling unused ifdone_ label ahead that has to be stripped - for (LinkedListNode 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 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 ScanForLabel (LinkedListNode 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 link, LinkedListNode donelink) - { - LinkedListNode 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 myopsbyname = new Dictionary (); - 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", "!="); - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptThread.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptThread.cs deleted file mode 100644 index 710355643f..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptThread.cs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.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 - { - public bool m_WakeUpThis = 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, int i) - { - engine = eng; - if(i < 0) - thd = XMREngine.StartMyThread(RunScriptThread, "xmrengine script", ThreadPriority.Normal); - else - thd = XMREngine.StartMyThread(RunScriptThread, "xmrengineExec" + i.ToString(), ThreadPriority.Normal); - engine.AddThread(thd, this); - m_ScriptThreadTID = thd.ManagedThreadId; - } - - public void Terminate() - { - m_WakeUpThis = true; - if(!thd.Join(250)) - thd.Abort(); - - engine.RemoveThread(thd); - - thd = null; - } - - /** - * @brief Wake up this XMRScriptThread instance. - */ - public void WakeUpScriptThread() - { - m_WakeUpThis = true; - } - - /** - * @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 RunScriptThread() - { - engine.RunScriptThread(this); - } - - public 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; - } - } -} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs deleted file mode 100644 index ca2806ecf5..0000000000 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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; - -/***************************\ - * Use standard C# code * - * - uses stack smashing * -\***************************/ - -namespace OpenSim.Region.ScriptEngine.XMREngine -{ - - public class ScriptUThread_Nul : IScriptUThread, IDisposable - { - private int active; // -1: hibernating - // 0: exited - // 1: running - private XMRInstance instance; - - public ScriptUThread_Nul (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 () - { - // We should only be called when no event handler running. - if (active != 0) - throw new Exception ("active=" + active); - - // Start script event handler from very beginning. - active = 1; - Exception except = null; - instance.callMode = XMRInstance.CallMode_NORMAL; - try - { - instance.CallSEH (); // run script event handler - active = 0; - } - catch (StackHibernateException) - { - if (instance.callMode != XMRInstance.CallMode_SAVE) - { - throw new Exception ("callMode=" + instance.callMode); - } - active = -1; // it is hibernating, can be resumed - } - catch (Exception e) - { - active = 0; - except = e; // threw exception, save for Start()/Resume() - } - - // 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 () - { - // We should only be called when script is hibernating. - if (active >= 0) - throw new Exception ("active=" + active); - - // Resume script from captured stack. - instance.callMode = XMRInstance.CallMode_RESTORE; - instance.suspendOnCheckRunTemp = true; - Exception except = null; - try - { - instance.CallSEH (); // run script event handler - active = 0; - } - catch (StackHibernateException) - { - if (instance.callMode != XMRInstance.CallMode_SAVE) - { - throw new Exception ("callMode=" + instance.callMode); - } - active = -1; - } - catch (Exception e) - { - active = 0; - except = e; // threw exception, save for Start()/Resume() - } - - // Return whether or not script threw an exception. - return except; - } - - /** - * @brief Script is being closed out. - * Terminate thread asap. - */ - 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 event handler whenever it wants to hibernate. - */ - public void Hiber () - { - if (instance.callMode != XMRInstance.CallMode_NORMAL) { - throw new Exception ("callMode=" + instance.callMode); - } - - switch (active) { - - // the stack has been restored as a result of calling ResumeEx() - // say the microthread is now active and resume processing - case -1: { - active = 1; - return; - } - - // the script event handler wants to hibernate - // capture stack frames and unwind to Start() or Resume() - case 1: { - instance.callMode = XMRInstance.CallMode_SAVE; - instance.stackFrames = null; - throw new StackHibernateException (); - } - - default: throw new Exception ("active=" + active); - } - } - - /** - * @brief Number of remaining stack bytes. - */ - public int StackLeft () - { - return 0x7FFFFFFF; - } - - public class StackHibernateException : Exception, IXMRUncatchable { } - } -} - diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRDelegateCommon.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRDelegateCommon.cs similarity index 73% rename from OpenSim/Region/ScriptEngine/XMREngine/MMRDelegateCommon.cs rename to OpenSim/Region/ScriptEngine/YEngine/MMRDelegateCommon.cs index 48b665ba3d..433062ad77 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRDelegateCommon.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRDelegateCommon.cs @@ -30,65 +30,76 @@ using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; -namespace OpenSim.Region.ScriptEngine.XMREngine { +namespace OpenSim.Region.ScriptEngine.Yengine +{ - public class DelegateCommon { + public class DelegateCommon + { private string sig; // rettype(arg1type,arg2type,...), eg, "void(list,string,integer)" private Type type; // resultant delegate type - private static Dictionary delegateCommons = new Dictionary (); - private static Dictionary delegateCommonsBySysType = new Dictionary (); + private static Dictionary delegateCommons = new Dictionary(); + private static Dictionary delegateCommonsBySysType = new Dictionary(); private static ModuleBuilder delegateModuleBuilder = null; - public static Type[] constructorArgTypes = new Type[] { typeof (object), typeof (IntPtr) }; + public static Type[] constructorArgTypes = new Type[] { typeof(object), typeof(IntPtr) }; - private DelegateCommon () { } + private DelegateCommon() + { + } - public static Type GetType (System.Type ret, System.Type[] args, string sig) + 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); + 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) + public static Type TryGetType(string sig) { DelegateCommon dc; - lock (delegateCommons) { - if (!delegateCommons.TryGetValue (sig, out dc)) dc = null; + lock(delegateCommons) + { + if(!delegateCommons.TryGetValue(sig, out dc)) + dc = null; } return (dc == null) ? null : dc.type; } - public static string TryGetName (Type t) + public static string TryGetName(Type t) { DelegateCommon dc; - lock (delegateCommons) { - if (!delegateCommonsBySysType.TryGetValue (t, out dc)) dc = null; + 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) + private static Type CreateDelegateType(string name, Type retType, Type[] argTypes) { - if (delegateModuleBuilder == null) { + 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, + TypeBuilder typeBuilder = delegateModuleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class | - TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof (MulticastDelegate)); + TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(MulticastDelegate)); ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor( MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRIEventHandlers.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRIEventHandlers.cs similarity index 56% rename from OpenSim/Region/ScriptEngine/XMREngine/MMRIEventHandlers.cs rename to OpenSim/Region/ScriptEngine/YEngine/MMRIEventHandlers.cs index 440beb3f32..905a23072f 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRIEventHandlers.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRIEventHandlers.cs @@ -33,43 +33,44 @@ 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 +namespace OpenSim.Region.ScriptEngine.Yengine { - 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); + 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); diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRInternalFuncDict.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRInternalFuncDict.cs similarity index 67% rename from OpenSim/Region/ScriptEngine/XMREngine/MMRInternalFuncDict.cs rename to OpenSim/Region/ScriptEngine/YEngine/MMRInternalFuncDict.cs index c2c01b5861..5d1cbc046e 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRInternalFuncDict.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRInternalFuncDict.cs @@ -30,9 +30,11 @@ using System.Collections.Generic; using System.IO; using System.Reflection; -namespace OpenSim.Region.ScriptEngine.XMREngine { +namespace OpenSim.Region.ScriptEngine.Yengine +{ - public class InternalFuncDict : VarDict { + public class InternalFuncDict: VarDict + { /** * @brief build dictionary of internal functions from an interface. @@ -41,50 +43,57 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * false: catalog by simple name only, eg, state_entry * @returns dictionary of function definition tokens */ - public InternalFuncDict (Type iface, bool inclSig) - : base (false) + 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) { + 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; + if((key[0] < 'a') || (key[0] > 'z')) + continue; - try { + 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[] 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); + 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; + 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) { + 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); + 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 ???/// } diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptBinOpStr.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptBinOpStr.cs new file mode 100644 index 0000000000..0d6d4bd7c1 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptBinOpStr.cs @@ -0,0 +1,1569 @@ +/* + * 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.Yengine; +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.Yengine +{ + + /** + * @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 defined = DefineBinOps(); + + public Type outtype; // type of result of computation + public BinOpStrEmitBO emitBO; // how to compute result + public bool rmwOK; // is the = 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 , + * where and are strings returned by (TokenType...).ToString() + * and 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 DefineBinOps() + { + Dictionary bos = new Dictionary(); + + 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 bos2 = new Dictionary(); + foreach(KeyValuePair 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 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 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 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 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 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), 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 bos) + { + bos.Add("char==char", new BinOpStr(typeof(bool), BinOpStrCharEqChar)); + bos.Add("char!=char", new BinOpStr(typeof(bool), BinOpStrCharNeChar)); + bos.Add("charchar", 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 bos) + { + bos.Add("integer==integer", new BinOpStr(typeof(bool), BinOpStrIntEqInt)); + bos.Add("integer!=integer", new BinOpStr(typeof(bool), BinOpStrIntNeInt)); + bos.Add("integerinteger", 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), BinOpStrIntShrInt, true)); + } + + private static void DefineBinOpsKeyX(Dictionary 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 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 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 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 bos) + { + bos.Add("string==string", new BinOpStr(typeof(bool), BinOpStrStrEqStr)); + bos.Add("string!=string", new BinOpStr(typeof(bool), BinOpStrStrNeStr)); + bos.Add("stringstring", 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 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; + } + } +} diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCodeGen.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCodeGen.cs new file mode 100644 index 0000000000..a480263301 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCodeGen.cs @@ -0,0 +1,7170 @@ +/* + * 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.Yengine; +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.Yengine +{ + 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"; + // reserve positive version values for original xmr + public static int COMPILED_VERSION_VALUE = -1; // decremented when compiler or object file changes + + 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 stackLeftFieldInfo = typeof(XMRInstAbstract).GetField("m_StackLeft"); + 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 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 = "<>"; + 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 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 actCallLabels = new LinkedList(); // for the active try/catch/finally stack or the big one outside them all + private LinkedList allCallLabels = new LinkedList(); // 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(); + + /* + * 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 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 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.gbls[] 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 ., 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 + * $get() and $set() 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 newobjDeclFuncs = new List(); + 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); + 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 '. + * 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 + */ + foreach(KeyValuePair 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 + * + * 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 (XMRInstance __sw) + * { + * = ()__sw.ehArgs[0]; + * = ()__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 statehandlers = new Dictionary(); + 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 .$new() 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. + */ + int stackframesize = AllocLocalVarStackSpace(); + + /* + * Include argument variables in stack space for this frame. + */ + foreach(TokenType tokType in curDeclFunc.argDecl.types) + { + stackframesize += LocalVarStackSize(tokType); + } + + /* + * 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; + * instance.m_StackLeft -= stackframesize; + * 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); + PushXMRInst(); + ilGen.Emit(curDeclFunc, OpCodes.Dup); + ilGen.Emit(curDeclFunc, OpCodes.Ldfld, stackLeftFieldInfo); + ilGen.Emit(curDeclFunc, OpCodes.Ldc_I4, stackframesize); + ilGen.Emit(curDeclFunc, OpCodes.Sub); + ilGen.Emit(curDeclFunc, OpCodes.Stfld, stackLeftFieldInfo); + 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 activeTemps = null; + if(!isTrivial) + { + + /* + * Build list of locals and temps active at all the call labels. + */ + activeTemps = new List(); + 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 { + * instance.m_StackLeft += stackframesize; + * if (instance.callMode != CallMode_SAVE) goto __endFin; + * GenerateFrameCaptureCode(); + * __endFin: + * } + */ + ScriptMyLabel endFin = null; + if(!isTrivial) + { + ilGen.BeginFinallyBlock(); + PushXMRInst(); + ilGen.Emit(curDeclFunc, OpCodes.Dup); + ilGen.Emit(curDeclFunc, OpCodes.Ldfld, stackLeftFieldInfo); + ilGen.Emit(curDeclFunc, OpCodes.Ldc_I4, stackframesize); + ilGen.Emit(curDeclFunc, OpCodes.Add); + ilGen.Emit(curDeclFunc, OpCodes.Stfld, stackLeftFieldInfo); + 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. + * @returns approximate stack frame size + */ + private int AllocLocalVarStackSpace() + { + int stackframesize = 64; // RIP, RBX, RBP, R12..R15, one extra + 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); + + /* + * Stack size for the local variable. + */ + stackframesize += LocalVarStackSize(localVar.type); + } + return stackframesize; + } + + private static int LocalVarStackSize(TokenType tokType) + { + Type sysType = tokType.ToSysType(); + return sysType.IsValueType ? System.Runtime.InteropServices.Marshal.SizeOf(sysType) : 8; + } + + /** + * @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 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)) + { + t = HeapTrackerList.GenPush(curDeclFunc, ilGen); + } + if(t == typeof(HeapTrackerObject)) + { + t = HeapTrackerObject.GenPush(curDeclFunc, ilGen); + } + if(t == typeof(HeapTrackerString)) + { + t = HeapTrackerString.GenPush(curDeclFunc, ilGen); + } + 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 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) + { + if(t == typeof(HeapTrackerList)) + HeapTrackerList.GenPop(curDeclFunc, ilGen); + if(t == typeof(HeapTrackerObject)) + HeapTrackerObject.GenPop(curDeclFunc, ilGen); + if(t == typeof(HeapTrackerString)) + HeapTrackerString.GenPop(curDeclFunc, ilGen); + } + 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 finallyBlocksCalled = new Stack(); + 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 .$new() 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 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 + * : // 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 + * // execute script-defined code + * // ...stack capture WILL run catch { } subblock + * leave tryEnd; // exits + * tryThrow:: + * throw new ScriptRestoreCatchException(__catThrown); // catch { } was running, jump to its beginning + * tryCallSw: // restoring... + * switch (__tryCallNo) back up into // 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: + * if (__catCallNo >= 0) goto catchCallSw; // if restoring, go check below + * // normal, execute script-defined code + * leave tryEnd; // all done, exit catch { } + * catchRetro: + * rethrow; + * catchCallSw: + * switch (__catCallNo) back up into // 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); // : + 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(); + + GenerateStmtBlock(tryStmt.tryStmt); // output the try block statement subblock + + bool tryBlockFallsOutBottom = mightGetHere; + if(tryBlockFallsOutBottom) + { + new CallLabel(this, tryStmt); // : + ilGen.Emit(tryStmt, OpCodes.Leave, tryEnd); // leave tryEnd; + openCallLabel = null; + } + + CallLabel tryThrow = new CallLabel(this, tryStmt); // tryThrow:: + 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 + * : // 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 + * // execute script-defined code + * // ...stack capture WILL run catch/finally { } subblock + * leave tryEnd; // executes finally { } subblock and exits + * tryThrow:: + * throw new ScriptRestoreCatchException(__catThrown); // catch { } was running, jump to its beginning + * tryCallSw: // restoring... + * switch (__tryCallNo) back up into // 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:; // 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 + * // normal, execute script-defined code + * finEnd: + * endfinally // jump to leave/throw target or next outer finally { } + * finCallSw: + * switch (__finCallNo) back up into // 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); // : + 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(); + + GenerateStmtBlock(tryStmt.tryStmt); // output the try block statement subblock + + if(mightGetHere) + { + new CallLabel(this, tryStmt); // : + 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:: + 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:; + 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 scorvs = new List(); + 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 ="); + return GenerateFromRVal(token.rValLeft); + } + if(!binOpStr.rmwOK) + { + ErrorMsg(token, "= 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 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 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 is 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; + } + } +} diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCollector.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCollector.cs new file mode 100644 index 0000000000..4a57823bca --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCollector.cs @@ -0,0 +1,3105 @@ +/* + * 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.Yengine +{ + /** + * @brief This is a list that keeps track of types pushed on the evaluation stack. + */ + public class StackDepth: List + { + public List isBoxeds = new List(); + + /** + * @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 nextNodes, nextNodesCatchFinally; + public System.Collections.Generic.IEnumerable 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 + { + private GraphNode gn; + public NNEnumerableCatchFinally(GraphNode gn) + { + this.gn = gn; + } + System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.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 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 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 localsWrittenBeforeRead = new List(); + public List localsReadBeforeWritten = new List(); + 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 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 leaveTargets = new List(); + + 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 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 GetNNEnumerable() + { + return new NNEnumerable(this, typeof(NNEnumerator)); + } + + private class NNEnumerator: NNEnumeratorBase + { + private GraphNodeEmitNullEndfinally gn; + private IEnumerator 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 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 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 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 + { + 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 System.Collections.Generic.IEnumerable.GetEnumerator() + { + return (System.Collections.Generic.IEnumerator)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 + { + protected GraphNode nn; + + public abstract bool MoveNext(); + public abstract void Reset(); + + GraphNode System.Collections.Generic.IEnumerator.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 declaredLocals = new List(); + private List definedLabels = new List(); + + 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 except ld v2 + * ldloc v2 + * ...v2 unreferenced hereafter + * With: + * ld except ld 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 except ld 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...' 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/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 localsWrittenSoFar = new List(); + + /* + * 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)); + } + } +} diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompValu.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompValu.cs new file mode 100644 index 0000000000..17bc3eccdb --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompValu.cs @@ -0,0 +1,1882 @@ +/* + * 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.Yengine; +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 + * + * 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 + * + * cv.CallPost() - calls the function/method + */ + +namespace OpenSim.Region.ScriptEngine.Yengine +{ + + /** + * @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 = [.]() + // signature = () + // 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 = .() + // signature = () + // 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) + { + type.CallHeapTrackerPushMeth(errorAt, scg.ilGen); + } + } + 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) + { + type.CallHeapTrackerPopMeth(errorAt, scg.ilGen); + } + 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); + type.CallHeapTrackerPopMeth(errorAt, scg.ilGen); + } + 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) + { + } + } +} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompile.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompile.cs similarity index 53% rename from OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompile.cs rename to OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompile.cs index 017d2c5461..bd7ccc1ac7 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCompile.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompile.cs @@ -31,12 +31,8 @@ using System; using System.IO; -using System.IO.Compression; -using System.Reflection; -using System.Security.Cryptography; -using System.Text; -namespace OpenSim.Region.ScriptEngine.XMREngine +namespace OpenSim.Region.ScriptEngine.Yengine { public partial class XMRInstance { @@ -45,155 +41,136 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @returns object code pointer or null if compile error * also can throw compile error exception */ - public ScriptObjCode Compile () + 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)) { + // 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)) { + } + 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); + // Maybe write script source to a file for debugging. + if (m_Engine.m_ScriptDebugSaveSource) + { + string lslFileName = GetScriptFileName (m_ScriptObjCodeKey + ".lsl"); +// m_log.Debug ("[YEngine]: MMRScriptCompileSaveSource: saving to " + lslFileName); saveSource = File.CreateText (lslFileName); } - /* - * Parse source string into tokens. - */ + // Parse source string into tokens. TokenBegin tokenBegin; - try { + 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); + finally + { + if (saveSource != null) + saveSource.Close (); + } + if (tokenBegin == null) + { + m_log.Debug ("[YEngine]: parsing errors on " + m_ScriptObjCodeKey); return null; } - /* - * Create object file one way or another. - */ - try { + // Create object file one way or another. + try + { objFileStream = File.Create (tmpFileName); - /* - * Create abstract syntax tree from raw tokens. - */ + // 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 (); + if (tokenScript == null) + { + m_log.Warn ("[YEngine]: reduction errors on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")"); + PrintCompilerErrors(); + objFileStream.Close(); return null; } - /* - * Compile abstract syntax tree to write object file. - */ + // 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 + ")"); + if (!ok) + { + m_log.Warn ("[YEngine]: compile error on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")"); PrintCompilerErrors (); - objFileStream.Close (); + objFileWriter.Close (); return null; } - objFileStream.Close (); + objFileWriter.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 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 { + else File.Move (tmpFileName, objFileName); - } - objFileStream = File.OpenRead (objFileName); - } finally { - /* - * In case something went wrong writing temp file, delete it. - */ - try { + objFileStream = File.OpenRead (objFileName); + } + finally + { + // In case something went wrong writing temp file, delete it. + try + { File.Delete (tmpFileName); - } catch { + } + 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); + // Since we just wrote the .xmrobj file, maybe save disassembly. + if (m_Engine.m_ScriptDebugSaveIL) + { + string asmFileName = GetScriptFileName (m_ScriptObjCodeKey + ".xmrasm"); +// m_log.Debug ("[YEngine]: MMRScriptCompileSaveILGen: saving to " + asmFileName); asmFileWriter = File.CreateText (asmFileName); } } - /* - * Read object file to create ScriptObjCode object. - * Maybe also write disassembly to a file for debugging. - */ + // 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 { + try + { scriptObjCode = new ScriptObjCode (objFileReader, asmFileWriter, null); - if (scriptObjCode != null) { - scriptObjCode.fileDateUtc = File.GetLastWriteTimeUtc (objFileName); - } - } finally { + } + finally + { objFileReader.Close (); - if (asmFileWriter != null) { + 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); + m_log.Info ("[YEngine]: - " + m_Part.GetWorldPosition () + " " + m_DescName); foreach (string error in m_CompilerErrors) { - m_log.Info ("[XMREngine]: - " + error); + m_log.Info ("[YEngine]: - " + error); } } @@ -204,11 +181,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { int len = source.Length; bool skipeol = false; - for (int i = 0; i < len; i ++) { + 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; + if ((c > ' ') && !skipeol) + return false; } return true; } diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptConsts.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptConsts.cs new file mode 100644 index 0000000000..63a6ee93b2 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptConsts.cs @@ -0,0 +1,287 @@ +/* + * 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.Yengine +{ + + public class ScriptConst + { + + public static Dictionary 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 Init() + { + Dictionary sc = new Dictionary(); + + /* + * For every event code, define XMREVENTCODE_ and XMREVENTMASKn_ 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 sc, FieldInfo[] allFields) + { + List ucfs = new List(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 sc, IEnumerator 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 lc, string name, CompValu rVal) + { + lc.Add(name, this); + this.name = name; + this.rVal = rVal; + } + } +} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptEventCode.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptEventCode.cs similarity index 65% rename from OpenSim/Region/ScriptEngine/XMREngine/MMRScriptEventCode.cs rename to OpenSim/Region/ScriptEngine/YEngine/MMRScriptEventCode.cs index 8e8b7554ca..c00e8d4dda 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptEventCode.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptEventCode.cs @@ -25,7 +25,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -namespace OpenSim.Region.ScriptEngine.XMREngine { +namespace OpenSim.Region.ScriptEngine.Yengine +{ /** * @brief List of event codes that can be passed to StartEventHandler(). @@ -38,58 +39,59 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * in range 0..63 that begin with a lower-case letter for scripts to * reference. */ - public enum ScriptEventCode : int { + public enum ScriptEventCode: int + { // used by XMRInstance to indicate no event being processed - None = -1, + 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, + 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, + 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, + 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, + changed = 33, + link_message = 34, + no_sensor = 35, + on_rez = 36, + sensor = 37, + http_request = 38, - path_update = 40, + path_update = 40, // XMRE specific - region_cross = 63, + region_cross = 63, // marks highest numbered event, ie, number of columns in seht. - Size = 64 + Size = 64 } } diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptInlines.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptInlines.cs new file mode 100644 index 0000000000..e17d41ac2d --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptInlines.cs @@ -0,0 +1,727 @@ +/* + * 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.Yengine +{ + 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 _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 lcms = new List(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 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 (,...) + * @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 bifs = new SortedDictionary(); + 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 scs = new SortedDictionary(); + 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) // 'this' pointer for API function + scg.ilGen.Emit(errorAt, OpCodes.Ldfld, apiContextField); + + } + for(int i = 0; i < args.Length; i++) // push arguments, boxing/unboxing as needed + args[i].PushVal(scg, errorAt, argDecl.types[i]); + + // this should not be needed + // if (methInfo.Name == "llParcelMediaQuery") { + // scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaQuery); + // } + // this should not be needed + // 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() + } + } +} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptMyILGen.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptMyILGen.cs similarity index 62% rename from OpenSim/Region/ScriptEngine/XMREngine/MMRScriptMyILGen.cs rename to OpenSim/Region/ScriptEngine/YEngine/MMRScriptMyILGen.cs index ecc217ea0b..bf0db119d1 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptMyILGen.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptMyILGen.cs @@ -29,37 +29,41 @@ using System; using System.Reflection; using System.Reflection.Emit; -namespace OpenSim.Region.ScriptEngine.XMREngine +namespace OpenSim.Region.ScriptEngine.Yengine { 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); + 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 class ScriptMyLabel + { public string name; public int number; @@ -71,7 +75,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief One of these per local variable defined in the function. */ - public class ScriptMyLocal { + public class ScriptMyLocal + { public string name; public Type type; public int number; diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptObjCode.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptObjCode.cs new file mode 100644 index 0000000000..d5b08f078f --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptObjCode.cs @@ -0,0 +1,245 @@ +/* + * 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.Yengine; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; + +namespace OpenSim.Region.ScriptEngine.Yengine +{ + 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 sdObjTypesName; + // all script-defined types by name + + public TokenDeclSDType[] sdObjTypesIndx; + // all script-defined types by sdTypeIndex + + public Dictionary sdDelTypes; + // all script-defined delegates (including anonymous) + + public Dictionary dynamicMethods; + // all dyanmic methods + + public Dictionary[]> 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> globalVarNames = new Dictionary>(); + + /** + * @brief Fill in ScriptObjCode from an YEngine 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(); + 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 names; + if(!globalVarNames.TryGetValue(gblType, out names)) + { + names = new Dictionary(); + 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(); + sdDelTypes = new Dictionary(); + 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(); + scriptSrcLocss = new Dictionary[]>(); + + 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: + foreach(KeyValuePair 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 srcLocs) + { + /* + * Save method object code pointer. + */ + dynamicMethods.Add(method.Name, method); + + /* + * Build and sort iloffset -> source code location array. + */ + int n = srcLocs.Count; + KeyValuePair[] srcLocArray = new KeyValuePair[n]; + n = 0; + foreach(KeyValuePair 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 kvpx = (KeyValuePair)x; + KeyValuePair kvpy = (KeyValuePair)y; + return kvpx.Key - kvpy.Key; + } + } + } +} diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptObjWriter.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptObjWriter.cs new file mode 100644 index 0000000000..b87bc7276c --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptObjWriter.cs @@ -0,0 +1,1040 @@ +/* + * 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.Yengine +{ + 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 opCodes = PopulateOpCodes(); + private static Dictionary string2Type = PopulateS2T(); + private static Dictionary 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 sdTypesRev = new Dictionary(); + 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. (); + * 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 sdTypes, BinaryReader objReader, + ScriptObjCode scriptObjCode, ObjectTokens objectTokens) + { + Dictionary methods = new Dictionary(); + DynamicMethod method = null; + ILGenerator ilGen = null; + Dictionary labels = new Dictionary(); + Dictionary locals = new Dictionary(); + Dictionary labelNames = new Dictionary(); + Dictionary localNames = new Dictionary(); + object[] ilGenArg = new object[1]; + int offset = 0; + Dictionary 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(); + 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 PopulateOpCodes() + { + Dictionary opCodeDict = new Dictionary(); + 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 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 PopulateS2T() + { + Dictionary s2t = new Dictionary(); + + 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 PopulateT2S() + { + Dictionary s2t = PopulateS2T(); + Dictionary t2s = new Dictionary(); + foreach(KeyValuePair 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 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; + } +} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptReduce.cs similarity index 50% rename from OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs rename to OpenSim/Region/ScriptEngine/YEngine/MMRScriptReduce.cs index a8af740c65..b0653f7514 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptReduce.cs @@ -56,87 +56,89 @@ 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 { +namespace OpenSim.Region.ScriptEngine.Yengine +{ - 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; + 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 precedence = PrecedenceInit (); + private static Dictionary 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) }; + 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 PrecedenceInit () + private static Dictionary PrecedenceInit() { - Dictionary p = new Dictionary (); + Dictionary p = new Dictionary(); // http://www.lslwiki.net/lslwiki/wakka.php?wakka=operators - p.Add (typeof (TokenKwComma), 30); + 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(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(TokenKwQMark), 60); - p.Add (typeof (TokenKwOrOrOr), 70); - p.Add (typeof (TokenKwAndAndAnd), 80); + p.Add(typeof(TokenKwOrOrOr), 70); + p.Add(typeof(TokenKwAndAndAnd), 80); - p.Add (typeof (TokenKwOrOr), 100); + p.Add(typeof(TokenKwOrOr), 100); - p.Add (typeof (TokenKwAndAnd), 120); + p.Add(typeof(TokenKwAndAnd), 120); - p.Add (typeof (TokenKwOr), 140); + p.Add(typeof(TokenKwOr), 140); - p.Add (typeof (TokenKwXor), 160); + p.Add(typeof(TokenKwXor), 160); - p.Add (typeof (TokenKwAnd), 180); + p.Add(typeof(TokenKwAnd), 180); - p.Add (typeof (TokenKwCmpEQ), 200); - p.Add (typeof (TokenKwCmpNE), 200); + 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(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(TokenKwRSh), 260); + p.Add(typeof(TokenKwLSh), 260); - p.Add (typeof (TokenKwAdd), 280); - p.Add (typeof (TokenKwSub), 280); + 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); + p.Add(typeof(TokenKwMul), 320); + p.Add(typeof(TokenKwDiv), 320); + p.Add(typeof(TokenKwMod), 320); return p; } @@ -150,9 +152,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @returns null: not a valid script, error messages have been output * else: valid script top token */ - public static TokenScript Reduce (TokenBegin tokenBegin) + public static TokenScript Reduce(TokenBegin tokenBegin) { - return new ScriptReduce (tokenBegin).tokenScript; + return new ScriptReduce(tokenBegin).tokenScript; } /* @@ -173,14 +175,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @returns tokenScript = null: there were errors * else: successful */ - private ScriptReduce (TokenBegin tokenBegin) + 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; + tokenScript = new TokenScript(tokenBegin.nextToken); /* * 'class', 'delegate', 'instance' all define types. @@ -195,8 +196,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * be skipped over, because their bodies have been substituted * in the source for any references. */ - ParseSDTypePreScanPassOne (tokenBegin); // catalog definitions - ParseSDTypePreScanPassTwo (tokenBegin); // substitute references + ParseSDTypePreScanPassOne(tokenBegin); // catalog definitions + ParseSDTypePreScanPassTwo(tokenBegin); // substitute references /* int braces = 0; @@ -229,21 +230,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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; + 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); + tokenScript.AddVarEntry(gviFunc); /* * Scan through the tokens until we reach the end. */ - for (Token token = tokenBegin.nextToken; !(token is TokenEnd);) { - if (token is TokenKwSemi) { + for(Token token = tokenBegin.nextToken; !(token is TokenEnd);) + { + if(token is TokenKwSemi) + { token = token.nextToken; continue; } @@ -251,13 +254,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Script-defined type declarations. */ - if (ParseDeclSDTypes (ref token, null, SDT_PUBLIC)) continue; + if(ParseDeclSDTypes(ref token, null, SDT_PUBLIC)) + continue; /* * constant = ; */ - if (token is TokenKwConst) { - ParseDeclVar (ref token, null); + if(token is TokenKwConst) + { + ParseDeclVar(ref token, null); continue; } @@ -265,15 +270,17 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * ; * = ; */ - if ((token is TokenType) && + 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) { + ((token.nextToken.nextToken is TokenKwSemi) || + (token.nextToken.nextToken is TokenKwAssign))) + { + TokenDeclVar var = ParseDeclVar(ref token, gviFunc); + if(var != null) + { // = ; - TokenLValName left = new TokenLValName (var.name, tokenScript.variablesStack); - DoVarInit (gviFunc, left, var.init); + TokenLValName left = new TokenLValName(var.name, tokenScript.variablesStack); + DoVarInit(gviFunc, left, var.init); } continue; } @@ -281,10 +288,11 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * { [ get { } ] [ set { } ] } */ - if ((token is TokenType) && + if((token is TokenType) && (token.nextToken is TokenName) && - (token.nextToken.nextToken is TokenKwBrcOpen)) { - ParseProperty (ref token, false, true); + (token.nextToken.nextToken is TokenKwBrcOpen)) + { + ParseProperty(ref token, false, true); continue; } @@ -292,27 +300,32 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * * global function returning specified type */ - if (token is TokenType) { + if(token is TokenType) + { TokenType tokenType = (TokenType)token; token = token.nextToken; - if (!(token is TokenName)) { - ErrorMsg (token, "expecting variable/function name"); - token = SkipPastSemi (token); + 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, " must be followed by ; = or ("); - token = SkipPastSemi (token); + if(!(token is TokenKwParOpen)) + { + ErrorMsg(token, " 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); + TokenDeclVar tokenDeclFunc = ParseDeclFunc(ref token, false, false, false); + if(tokenDeclFunc == null) + continue; + if(!tokenScript.AddVarEntry(tokenDeclFunc)) + { + ErrorMsg(tokenName, "duplicate function " + tokenDeclFunc.funcNameSig.val); } continue; } @@ -321,34 +334,40 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * * global function returning void */ - if (token is TokenName) { + if(token is TokenName) + { TokenName tokenName = (TokenName)token; token = token.nextToken; - if (!(token is TokenKwParOpen)) { - ErrorMsg (token, "looking for open paren after assuming " + + if(!(token is TokenKwParOpen)) + { + ErrorMsg(token, "looking for open paren after assuming " + tokenName.val + " is a function name"); - token = SkipPastSemi (token); + 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); - } + 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 */ - if (token is TokenKwDefault) { - TokenDeclState tokenDeclState = new TokenDeclState (token); + 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"); + tokenDeclState.body = ParseStateBody(ref token); + if(tokenDeclState.body == null) + continue; + if(tokenScript.defaultState != null) + { + ErrorMsg(tokenDeclState, "default state already declared"); continue; } tokenScript.defaultState = tokenDeclState; @@ -358,45 +377,51 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * state */ - if (token is TokenKwState) { - TokenDeclState tokenDeclState = new TokenDeclState (token); + 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); + 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"); + 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); + 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); + 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(!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; + if(errors) + tokenScript = null; } /** @@ -409,32 +434,36 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * parameter names. The body remains intact in the source token stream following the * TokenDeclSDType* token. */ - private void ParseSDTypePreScanPassOne (Token tokenBegin) + private void ParseSDTypePreScanPassOne(Token tokenBegin) { - Stack braceLevels = new Stack (); - Stack outerLevels = new Stack (); + Stack braceLevels = new Stack(); + Stack outerLevels = new Stack(); int openBraceLevel = 0; - braceLevels.Push (-1); - outerLevels.Push (null); - - for (Token t = tokenBegin; !((t = t.nextToken) is TokenEnd);) { + 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 ++; + if(t is TokenKwBrcOpen) + { + openBraceLevel++; continue; } - if (t is TokenKwBrcClose) { - if (-- openBraceLevel < 0) { - ErrorMsg (t, "{ } mismatch"); + if(t is TokenKwBrcClose) + { + if(--openBraceLevel < 0) + { + ErrorMsg(t, "{ } mismatch"); return; } - if (braceLevels.Peek () == openBraceLevel) { - braceLevels.Pop (); - outerLevels.Pop ().endToken = t; + if(braceLevels.Peek() == openBraceLevel) + { + braceLevels.Pop(); + outerLevels.Pop().endToken = t; } continue; } @@ -444,12 +473,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * They always define a new class or interface. * They can contain nested script-defined type definitions. */ - if ((t is TokenKwClass) || (t is TokenKwInterface)) { + 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; + if(!(t is TokenName)) + { + ErrorMsg(t, "expecting class or interface name"); + t = SkipPastSemi(t).prevToken; continue; } TokenName name = (TokenName)t; @@ -459,14 +490,17 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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 (); + 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; + if(!ParseGenProtoParamList(ref t, decl)) + continue; /* * Splice in a TokenDeclSDType token that replaces the keyword and the name tokens @@ -482,13 +516,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Enter it in name lists so it can be seen by others. */ - Token partialNewBody = CatalogSDTypeDecl (decl); + Token partialNewBody = CatalogSDTypeDecl(decl); /* * Start inner type definitions. */ - braceLevels.Push (openBraceLevel); - outerLevels.Push (decl); + braceLevels.Push(openBraceLevel); + outerLevels.Push(decl); /* * Scan the body starting on for before the '{'. @@ -498,7 +532,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * ie, what used to be the first token after the '{' * before the old body was spliced in. */ - if (partialNewBody != null) { + if(partialNewBody != null) + { /* * We have a partial that has had old partial body merged @@ -511,7 +546,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * but inc openBraceLevel because * we skipped scanning the '{' */ - openBraceLevel ++; + openBraceLevel++; t = partialNewBody; } t = t.prevToken; @@ -523,7 +558,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * It always defines a new delegate. * Delegates never define nested types. */ - if (t is TokenKwDelegate) { + if(t is TokenKwDelegate) + { Token kw = t; t = t.nextToken; @@ -538,41 +574,51 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { 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; + 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; + 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; + if(delName == null) + { + ErrorMsg(u, "expecting delegate name"); + t = SkipPastSemi(t).prevToken; continue; } - if (retType == delName) retType = null; + if(retType == delName) + retType = null; /* * Malloc the script-defined type object. */ - TokenDeclSDTypeDelegate decl = new TokenDeclSDTypeDelegate (delName); - decl.outerSDType = outerLevels.Peek (); + TokenDeclSDTypeDelegate decl = new TokenDeclSDTypeDelegate(delName); + decl.outerSDType = outerLevels.Peek(); /* * Check for generic parameter list. */ t = delName.nextToken; - if (!ParseGenProtoParamList (ref t, decl)) continue; + if(!ParseGenProtoParamList(ref t, decl)) + continue; /* * Enter it in name lists so it can be seen by others. */ - CatalogSDTypeDecl (decl); + CatalogSDTypeDecl(decl); /* * Splice in the token that replaces the 'delegate' keyword and the whole name @@ -582,10 +628,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { decl.prevToken = kw.prevToken; kw.prevToken.nextToken = decl; - if (retType == null) { + if(retType == null) + { decl.nextToken = t; t.prevToken = decl; - } else { + } + else + { decl.nextToken = retType; retType.prevToken = decl; retType.nextToken = t; @@ -596,16 +645,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * Scan for terminating ';'. * There cannot be an intervening class, delegate, interfate, typedef, { or }. */ - for (t = decl; !(t is TokenKwSemi); t = u) { + for(t = decl; !(t is TokenKwSemi); t = u) + { u = t.nextToken; - if ((u is TokenEnd) || + 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 ;"); + (u is TokenKwBrcOpen) || + (u is TokenKwBrcClose)) + { + ErrorMsg(t, "delegate missing terminating ;"); break; } } @@ -618,13 +669,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * It always defines a new macro. * Typedefs never define nested types. */ - if (t is TokenKwTypedef) { + if(t is TokenKwTypedef) + { Token kw = t; t = t.nextToken; - if (!(t is TokenName)) { - ErrorMsg (t, "expecting typedef name"); - t = SkipPastSemi (t).prevToken; + if(!(t is TokenName)) + { + ErrorMsg(t, "expecting typedef name"); + t = SkipPastSemi(t).prevToken; continue; } TokenName tdName = (TokenName)t; @@ -633,19 +686,20 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Malloc the script-defined type object. */ - TokenDeclSDTypeTypedef decl = new TokenDeclSDTypeTypedef (tdName); - decl.outerSDType = outerLevels.Peek (); + TokenDeclSDTypeTypedef decl = new TokenDeclSDTypeTypedef(tdName); + decl.outerSDType = outerLevels.Peek(); /* * Check for generic parameter list. */ - if (!ParseGenProtoParamList (ref t, decl)) continue; + if(!ParseGenProtoParamList(ref t, decl)) + continue; /* * Enter it in name lists so it can be seen by others. */ - CatalogSDTypeDecl (decl); - numTypedefs ++; + CatalogSDTypeDecl(decl); + numTypedefs++; /* * Splice in the token that replaces the 'typedef' keyword and the whole name @@ -661,16 +715,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * There cannot be an intervening class, delegate, interfate, typedef, { or }. */ Token u; - for (t = decl; !(t is TokenKwSemi); t = u) { + for(t = decl; !(t is TokenKwSemi); t = u) + { u = t.nextToken; - if ((u is TokenEnd) || + 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 ;"); + (u is TokenKwBrcOpen) || + (u is TokenKwBrcClose)) + { + ErrorMsg(t, "typedef missing terminating ;"); break; } } @@ -689,34 +745,38 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * true: decl.genParams = filled in with parameter list * decl.innerSDTypes = filled in with parameter list */ - private bool ParseGenProtoParamList (ref Token t, TokenDeclSDType decl) + 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; + if(!(t is TokenKwCmpLT)) + return true; /* * Build list of generic parameter names. */ - Dictionary parms = new Dictionary (); - do { + Dictionary parms = new Dictionary(); + do + { t = t.nextToken; - if (!(t is TokenName)) { - ErrorMsg (t, "expecting generic parameter name"); + 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); - } + 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"); + } while(t is TokenKwComma); + + if(!(t is TokenKwCmpGT)) + { + ErrorMsg(t, "expecting , for more params or > to end param list"); return false; } t = t.nextToken; @@ -730,44 +790,46 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) + 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); - } + 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"); + 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"); - } + 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; - } + 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 {"); + for(dupBrcOpen = dupDecl; !(dupBrcOpen is TokenKwBrcOpen); dupBrcOpen = dupBrcOpen.nextToken) + { + if(dupBrcOpen == dupDecl.endToken) + { + ErrorMsg(dupDecl, "missing {"); return null; } } @@ -776,9 +838,11 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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 {"); + for(brcOpen = decl; !(brcOpen is TokenKwBrcOpen); brcOpen = brcOpen.nextToken) + { + if(brcOpen is TokenEnd) + { + ErrorMsg(decl, "missing {"); return null; } } @@ -803,7 +867,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * class oldextimp newextimp { oldbody newbody } ... * decl brcOpen body decl.endToken */ - if (dupBrcOpen != dupDecl.nextToken) { + if(dupBrcOpen != dupDecl.nextToken) + { dupBrcOpen.prevToken.nextToken = decl.nextToken; dupDecl.nextToken.prevToken = decl; decl.nextToken.prevToken = dupBrcOpen.prevToken; @@ -814,7 +879,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * Stick old partial definition's body just * in front of new partial definition's body. */ - if (dupBrcOpen.nextToken != dupDecl.endToken) { + if(dupBrcOpen.nextToken != dupDecl.endToken) + { dupBrcOpen.nextToken.prevToken = brcOpen; dupDecl.endToken.prevToken.nextToken = body; body.prevToken = dupDecl.endToken.prevToken; @@ -835,17 +901,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief Determine whether or not the generic parameters of two class declarations match exactly. */ - private static bool GenericParametersMatch (TokenDeclSDType c1, TokenDeclSDType c2) + 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; + if((c1.genParams == null) && (c2.genParams == null)) + return true; + if((c1.genParams == null) || (c2.genParams == null)) + return false; Dictionary gp1 = c1.genParams; Dictionary gp2 = c2.genParams; - if (gp1.Count != gp2.Count) return false; - foreach (KeyValuePair kvp1 in gp1) { + if(gp1.Count != gp2.Count) + return false; + foreach(KeyValuePair kvp1 in gp1) + { int v2; - if (!gp2.TryGetValue (kvp1.Key, out v2)) return false; - if (v2 != kvp1.Value) return false; + if(!gp2.TryGetValue(kvp1.Key, out v2)) + return false; + if(v2 != kvp1.Value) + return false; } return true; } @@ -860,23 +932,26 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { private const uint REPEAT_INSTGEN = 2; private const uint REPEAT_SUBST = 4; - private void ParseSDTypePreScanPassTwo (Token tokenBegin) + private void ParseSDTypePreScanPassTwo(Token tokenBegin) { - List noTypes = new List (); + List noTypes = new List(); TokenDeclSDType outerSDType; uint repeat; - do { + do + { repeat = 0; outerSDType = null; - noTypes.Clear (); + noTypes.Clear(); - for (Token t = tokenBegin; !((t = t.nextToken) is TokenEnd);) { + 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)) { + if((outerSDType != null) && (outerSDType.endToken == t)) + { outerSDType = outerSDType.outerSDType; continue; } @@ -886,7 +961,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * We only need to process their instantiations which are non- * generic versions of the generics. */ - if ((t is TokenDeclSDType) && (((TokenDeclSDType)t).genParams != null)) { + if((t is TokenDeclSDType) && (((TokenDeclSDType)t).genParams != null)) + { t = ((TokenDeclSDType)t).endToken; continue; } @@ -905,10 +981,11 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) { + if(t is TokenDeclSDType) + { + if(((TokenDeclSDType)t).endToken != t) outerSDType = (TokenDeclSDType)t; - } + continue; } @@ -916,9 +993,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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); - } + if((t is TokenName) && !(t.prevToken is TokenKwDot)) + t = TrySpliceTypeRef(t, outerSDType, ref repeat, noTypes); /* * This handles types such as integer[,][], List[], etc. @@ -929,10 +1005,12 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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((t is TokenType) && (t.nextToken is TokenKwBrkOpen)) + { + if((t.nextToken.nextToken is TokenKwBrkClose) || + (t.nextToken.nextToken is TokenKwComma)) + { + t = InstantiateJaggedArray(t, tokenBegin, ref repeat); } } } @@ -943,16 +1021,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) || + } 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"); - } + foreach(Token t in noTypes) + ErrorMsg(t, "looking for type"); } /** @@ -966,7 +1043,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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 noTypes) + private Token TrySpliceTypeRef(Token t, TokenDeclSDType outerSDType, ref uint repeat, List noTypes) { Token start = t; string tnamestr = ((TokenName)t).val; @@ -977,52 +1054,63 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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; + while(outerSDType != null) + { + if(outerSDType.innerSDTypes.TryGetValue(tnamestr, out decl)) + break; outerSDType = outerSDType.outerSDType; } - if ((outerSDType == null) && !tokenScript.sdSrcTypesTryGetValue (tnamestr, out decl)) return t; + if((outerSDType == null) && !tokenScript.sdSrcTypesTryGetValue(tnamestr, out decl)) + return t; TokenDeclSDType instdecl; - while (true) { + while(true) + { /* * If it is a generic type, it must be followed by instantiation arguments. */ instdecl = decl; - if (decl.genParams != null) { + if(decl.genParams != null) + { t = t.nextToken; - if (!(t is TokenKwCmpLT)) { - ErrorMsg (t, "expecting < for generic argument list"); + 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;) { + for(int i = 0; i < nArgs;) + { t = t.nextToken; - if (!(t is TokenType)) { + if(!(t is TokenType)) + { repeat |= REPEAT_NOTYPE; - noTypes.Add (t); + 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 (); + tnamestr += ga.ToString(); t = t.nextToken; - if (++ i < nArgs) { - if (!(t is TokenKwComma)) { - ErrorMsg (t, "expecting , for more generic arguments"); + 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 ++; + 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; @@ -1031,15 +1119,19 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { v.nextToken.prevToken = v; t = u; } - if (!(t is TokenKwCmpGT)) { - ErrorMsg (t, "expecting > at end of generic argument list"); + 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); + if(outerSDType != null) + { + outerSDType.innerSDTypes.TryGetValue(tnamestr, out instdecl); + } + else + { + tokenScript.sdSrcTypesTryGetValue(tnamestr, out instdecl); } /* @@ -1048,9 +1140,10 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * of 'List' 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); + if(instdecl == null) + { + instdecl = decl.InstantiateGeneric(tnamestr, genArgs, this); + CatalogSDTypeDecl(instdecl); repeat |= REPEAT_INSTGEN; } } @@ -1058,10 +1151,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Maybe caller wants a subtype by putting a '.' following all that. */ - if (!(t.nextToken is TokenKwDot)) break; - if (!(t.nextToken.nextToken is TokenName)) break; + 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; + if(!instdecl.innerSDTypes.TryGetValue(tnamestr, out decl)) + break; t = t.nextToken.nextToken; outerSDType = instdecl; } @@ -1074,10 +1170,11 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 'Dictionary' '<' 'string' ',' 'integer' '>' '.' 'ValueList' * with 'Dictionary.ValueList'. */ - TokenType refer = instdecl.MakeRefToken (start); - if (refer == null) { + TokenType refer = instdecl.MakeRefToken(start); + if(refer == null) + { // typedef body is not yet a type - noTypes.Add (start); + noTypes.Add(start); repeat |= REPEAT_NOTYPE; return start; } @@ -1099,31 +1196,34 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) + private Token InstantiateJaggedArray(Token t, Token tokenBegin, ref uint repeat) { Token start = t; TokenType ofType = (TokenType)t; - Stack ranks = new Stack (); + Stack ranks = new Stack(); /* * 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 { + do + { t = t.nextToken; // point at '[' int rank = 0; - do { - rank ++; // count '[' and ','s + 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"); + } 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); + ranks.Push(rank); + } while(t.nextToken is TokenKwBrkOpen); /* * Now we build the types in reverse order. For the example above we will: @@ -1133,11 +1233,12 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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); + 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. @@ -1159,7 +1260,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + private TokenDeclSDType InstantiateFixedArray(int rank, TokenType ofType, Token tokenBegin, ref uint repeat) { /* * Create the array type's name. @@ -1168,23 +1269,29 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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 ++, ','); + 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 (); + sb.Insert(ix, ']'); + name = sb.ToString(); TokenDeclSDType fa; - if (!tokenScript.sdSrcTypesTryGetValue (name, out 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'; + 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. @@ -1192,14 +1299,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * * class { */ - fa = new TokenDeclSDTypeClass (new TokenName (tokenScript, name), false); - CatalogSDTypeDecl (fa); + 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)); + Token t = SpliceAfter(tokenBegin, fa); + t = SpliceAfter(t, new TokenKwBrcOpen(t)); /* * public integer len0; @@ -1207,17 +1314,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * ... * 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)); + 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)); + 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, ...) { @@ -1227,63 +1335,70 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * this.obj = xmrFixedArrayAlloc (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 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)); + 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)); + 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 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)); + 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 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 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 TokenKwSemi(t)); - t = SpliceAfter (t, new TokenKwBrcClose (t)); - t = SpliceAfter (t, new TokenKwBrcClose (t)); + t = SpliceAfter(t, new TokenKwBrcClose(t)); + t = SpliceAfter(t, new TokenKwBrcClose(t)); /* * public integer Length (integer dim) { @@ -1295,37 +1410,38 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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 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)); + 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)); + 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 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)); + 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, ...) { @@ -1336,41 +1452,44 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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 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 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)); + 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)); + 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)); + 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 Get (integer idx0, integet idx1, ...) { @@ -1381,53 +1500,57 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * return () xmrFixedArrayGet (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 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 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)); + 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)); + 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 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)); + 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, ..., val) { @@ -1438,56 +1561,58 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * xmrFixedArraySet (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, 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, 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 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 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)); + t = SpliceAfter(t, new TokenKwBrcClose(t)); + t = SpliceAfter(t, new TokenKwBrcClose(t)); } return fa; } - private Token SpliceAfter (Token before, Token after) + private Token SpliceAfter(Token before, Token after) { after.nextToken = before.nextToken; after.prevToken = before; @@ -1504,9 +1629,10 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + private bool ParseDeclSDTypes(ref Token token, TokenDeclSDType outerSDType, uint flags) { - if (!(token is TokenDeclSDType)) return false; + if(!(token is TokenDeclSDType)) + return false; TokenDeclSDType decl = (TokenDeclSDType)token; @@ -1515,7 +1641,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * The instantiations get parsed (ie, when we know the concrete types) * below because they appear as non-generic types. */ - if (decl.genParams != null) { + if(decl.genParams != null) + { token = decl.endToken.nextToken; return true; } @@ -1524,7 +1651,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * Also skip over any typedefs. They were all processed in * ParseSDTypePreScanPassTwo(). */ - if (decl is TokenDeclSDTypeTypedef) { + if(decl is TokenDeclSDTypeTypedef) + { token = decl.endToken.nextToken; return true; } @@ -1532,20 +1660,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Non-generic types get parsed inline because we know all their types. */ - if (decl is TokenDeclSDTypeClass) { - ParseDeclClass (ref token, outerSDType, flags); + if(decl is TokenDeclSDTypeClass) + { + ParseDeclClass(ref token, outerSDType, flags); return true; } - if (decl is TokenDeclSDTypeDelegate) { - ParseDeclDelegate (ref token, outerSDType, flags); + if(decl is TokenDeclSDTypeDelegate) + { + ParseDeclDelegate(ref token, outerSDType, flags); return true; } - if (decl is TokenDeclSDTypeInterface) { - ParseDeclInterface (ref token, outerSDType, flags); + if(decl is TokenDeclSDTypeInterface) + { + ParseDeclInterface(ref token, outerSDType, flags); return true; } - throw new Exception ("unhandled token " + token.GetType ().ToString ()); + throw new Exception("unhandled token " + token.GetType().ToString()); } /** @@ -1556,7 +1687,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) + private void ParseDeclClass(ref Token token, TokenDeclSDType outerSDType, uint flags) { bool haveExplicitConstructor = false; Token u = token; @@ -1569,7 +1700,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { // 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) { + if(tokdeclcl.endToken == tokdeclcl) + { token = u; return; } @@ -1582,78 +1714,93 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { // next can be ':' followed by list of implemented // interfaces and one extended class - if (u is TokenKwColon) { + if(u is TokenKwColon) + { u = u.nextToken; - while (true) { - if (u is TokenTypeSDTypeClass) { + while(true) + { + if(u is TokenTypeSDTypeClass) + { TokenDeclSDTypeClass c = ((TokenTypeSDTypeClass)u).decl; - if (tokdeclcl.extends == null) { + 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) { + 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; + 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; + 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); + 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); + 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; + 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); + 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; + 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); + tokenScript.AddVarEntry(sfiFunc); // process declaration statements until '}' - while (!(token is TokenKwBrcClose)) { - if (token is TokenKwSemi) { + while(!(token is TokenKwBrcClose)) + { + if(token is TokenKwSemi) + { token = token.nextToken; continue; } @@ -1663,24 +1810,29 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * typedef has an implied 'public' qualifier. */ flags = SDT_PUBLIC; - if (!(token is TokenDeclSDTypeTypedef)) { - flags = ParseQualifierFlags (ref token); + if(!(token is TokenDeclSDTypeTypedef)) + { + flags = ParseQualifierFlags(ref token); } /* * Parse nested script-defined type definitions. */ - if (ParseDeclSDTypes (ref token, tokdeclcl, flags)) continue; + if(ParseDeclSDTypes(ref token, tokdeclcl, flags)) + continue; /* * constant = ; */ - 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"); + 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) { + TokenDeclVar var = ParseDeclVar(ref token, null); + if(var != null) + { var.sdtClass = tokdeclcl; var.sdtFlags = flags | SDT_STATIC; } @@ -1691,29 +1843,35 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * ; * = ; */ - if ((token is TokenType) && + 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"); + ((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) { + TokenDeclVar var = ParseDeclVar(ref token, ifiFunc); + if(var != null) + { var.sdtClass = tokdeclcl; var.sdtFlags = flags; - if ((flags & SDT_STATIC) != 0) { + if((flags & SDT_STATIC) != 0) + { // . = ; - 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) { + 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. = ; - TokenLValIField left = new TokenLValIField (var.init); - left.baseRVal = new TokenRValThis (var.init, tokdeclcl); - left.fieldName = var.name; - DoVarInit (ifiFunc, left, var.init); + TokenLValIField left = new TokenLValIField(var.init); + left.baseRVal = new TokenRValThis(var.init, tokdeclcl); + left.fieldName = var.name; + DoVarInit(ifiFunc, left, var.init); } } continue; @@ -1725,19 +1883,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { */ bool prop = (token is TokenType) && (token.nextToken is TokenName) && - (token.nextToken.nextToken is TokenKwBrcOpen || + (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) { + 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) { + if(var.getProp != null) + { var.getProp.sdtClass = tokdeclcl; var.getProp.sdtFlags = flags; } - if (var.setProp != null) { + if(var.setProp != null) + { var.setProp.sdtClass = tokdeclcl; var.setProp.sdtFlags = flags; } @@ -1748,8 +1910,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * 'constructor' '(' arglist ')' [ ':' [ 'base' ] '(' baseconstructorcall ')' ] '{' body '}' */ - if (token is TokenKwConstructor) { - ParseSDTClassCtorDecl (ref token, flags, tokdeclcl); + if(token is TokenKwConstructor) + { + ParseSDTClassCtorDecl(ref token, flags, tokdeclcl); haveExplicitConstructor = true; continue; } @@ -1758,8 +1921,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * * method with explicit return type */ - if (token is TokenType) { - ParseSDTClassMethodDecl (ref token, flags, tokdeclcl); + if(token is TokenType) + { + ParseSDTClassMethodDecl(ref token, flags, tokdeclcl); continue; } @@ -1767,47 +1931,52 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * * method returning void */ - if ((token is TokenName) || ((token is TokenKw) && ((TokenKw)token).sdtClassOp)) { - ParseSDTClassMethodDecl (ref token, flags, tokdeclcl); + 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); + 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); + 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 { + if(tokdeclcl.extends != null) + { + SetUpDefaultBaseCtorCall(tokenDeclFunc); + } + else + { // default constructor that doesn't do anything is trivial tokenDeclFunc.triviality = Triviality.trivial; } - tokenScript.AddVarEntry (tokenDeclFunc); + tokenScript.AddVarEntry(tokenDeclFunc); } /* * Skip over the closing brace and pop corresponding var frame. */ token = token.nextToken; - tokenScript.PopVarFrame (); - ret: + tokenScript.PopVarFrame(); + ret: currentDeclSDType = saveCurSDType; } @@ -1816,65 +1985,78 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @param token = first token to evaluate * @returns flags found; token = unprocessed token */ - private Dictionary foundFlags = new Dictionary (); - private uint ParseQualifierFlags (ref Token token) + private Dictionary foundFlags = new Dictionary(); + private uint ParseQualifierFlags(ref Token token) { - foundFlags.Clear (); - while (true) { - if (token is TokenKwPrivate) { - token = AddQualifierFlag (token, SDT_PRIVATE, SDT_PROTECTED | SDT_PUBLIC); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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"); - } + 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) + private Token AddQualifierFlag(Token token, uint add, uint confs) { - while (confs != 0) { + while(confs != 0) + { uint conf = (uint)(confs & -confs); Token confToken; - if (foundFlags.TryGetValue (conf, out confToken)) { - ErrorMsg (token, "conflicts with " + confToken.ToString ()); + if(foundFlags.TryGetValue(conf, out confToken)) + { + ErrorMsg(token, "conflicts with " + confToken.ToString()); } confs -= conf; } @@ -1895,7 +2077,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * [ : ] { [ get { } ] [ set { } ] } * '[' ... ']' [ : ] { [ get { } ] [ set { } ] } */ - private TokenDeclVar ParseProperty (ref Token token, bool abs, bool imp) + private TokenDeclVar ParseProperty(ref Token token, bool abs, bool imp) { /* * Parse out the property's type and name. @@ -1906,14 +2088,17 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { TokenArgDecl args; Token argTokens = null; token = token.nextToken; - if (token is TokenKwBrkOpen) { + if(token is TokenKwBrkOpen) + { argTokens = token; - name = new TokenName (token, "$idxprop"); - args = ParseFuncArgs (ref token, typeof (TokenKwBrkClose)); - } else { - name = (TokenName)token; + name = new TokenName(token, "$idxprop"); + args = ParseFuncArgs(ref token, typeof(TokenKwBrkClose)); + } + else + { + name = (TokenName)token; token = token.nextToken; - args = new TokenArgDecl (token); + args = new TokenArgDecl(token); } /* @@ -1921,20 +2106,24 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * [ ':' [.] ',' ... ] */ 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"); + 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); + if(!(token is TokenKwBrcOpen)) + { + ErrorMsg(token, "expect { to open property definition"); + token = SkipPastSemi(token); return null; } token = token.nextToken; @@ -1946,23 +2135,28 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { */ TokenDeclVar getFunc = null; TokenDeclVar setFunc = null; - while (!(token is TokenKwBrcClose)) { + 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"); + 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)) { + if(!ParseFunctionBody(ref token, getFunc, abs)) + { getFunc = null; - } else if (!tokenScript.AddVarEntry (getFunc)) { - ErrorMsg (getFunc, "duplicate getter"); + } + else if(!tokenScript.AddVarEntry(getFunc)) + { + ErrorMsg(getFunc, "duplicate getter"); } continue; } @@ -1970,54 +2164,61 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Maybe create a setter function. */ - if (token is TokenKwSet) { + if(token is TokenKwSet) + { TokenArgDecl argDecl = args; - if (getFunc != null) { - argDecl = (argTokens == null) ? new TokenArgDecl (token) : - ParseFuncArgs (ref argTokens, typeof (TokenKwBrkClose)); + if(getFunc != null) + { + argDecl = (argTokens == null) ? new TokenArgDecl(token) : + ParseFuncArgs(ref argTokens, typeof(TokenKwBrkClose)); } - argDecl.AddArg (type, new TokenName (token, "value")); + 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"); + 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)) { + if(!ParseFunctionBody(ref token, setFunc, abs)) + { setFunc = null; - } else if (!tokenScript.AddVarEntry (setFunc)) { - ErrorMsg (setFunc, "duplicate setter"); + } + else if(!tokenScript.AddVarEntry(setFunc)) + { + ErrorMsg(setFunc, "duplicate setter"); } continue; } - ErrorMsg (token, "expecting get or set"); - token = SkipPastSemi (token); + 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"); + 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 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); + if(!tokenScript.AddVarEntry(tokenDeclVar)) + { + ErrorMsg(tokenDeclVar, "duplicate member " + name.val); return null; } return tokenDeclVar; @@ -2029,12 +2230,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + 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)); + 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; } @@ -2048,77 +2250,94 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + 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"); + 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; + 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); + 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; + tokenDeclFunc.argDecl = ParseFuncArgs(ref token, typeof(TokenKwParClose)); + if(tokenDeclFunc.argDecl == null) + return; TokenDeclVar saveDeclFunc = currentDeclFunc; currentDeclFunc = tokenDeclFunc; - tokenScript.PushVarFrame (tokenDeclFunc.argDecl.varDict); - try { + tokenScript.PushVarFrame(tokenDeclFunc.argDecl.varDict); + try + { /* * Set up reference to base constructor. */ - TokenLValBaseField baseCtor = new TokenLValBaseField (token, - new TokenName (token, "$ctor"), + 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) { + if(token is TokenKwColon) + { token = token.nextToken; - if (token is TokenKwBase) { + if(token is TokenKwBase) + { token = token.nextToken; } - if (!(token is TokenKwParOpen)) { - ErrorMsg (token, "expecting ( for base constructor call arguments"); - token = SkipPastSemi (token); + 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) { + 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"); + tokenDeclFunc.unknownTrivialityCalls.AddLast(rvc); } - } else if (tokdeclcl.extends != null) { + 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); + SetUpDefaultBaseCtorCall(tokenDeclFunc); } /* * Parse the constructor body. */ - tokenDeclFunc.body = ParseStmtBlock (ref token); - if (tokenDeclFunc.body == null) return; - if (tokenDeclFunc.argDecl == null) return; - } finally { - tokenScript.PopVarFrame (); + tokenDeclFunc.body = ParseStmtBlock(ref token); + if(tokenDeclFunc.body == null) + return; + if(tokenDeclFunc.argDecl == null) + return; + } + finally + { + tokenScript.PopVarFrame(); currentDeclFunc = saveDeclFunc; } @@ -2126,23 +2345,24 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * Add to list of methods defined by this class. * It has the name "$ctor(argsig)". */ - if (!tokenScript.AddVarEntry (tokenDeclFunc)) { - ErrorMsg (tokenDeclFunc, "duplicate constructor definition"); + 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) + private void SetUpDefaultBaseCtorCall(TokenDeclVar thisCtor) { - TokenLValBaseField baseCtor = new TokenLValBaseField (thisCtor, - new TokenName (thisCtor, "$ctor"), + TokenLValBaseField baseCtor = new TokenLValBaseField(thisCtor, + new TokenName(thisCtor, "$ctor"), (TokenDeclSDTypeClass)thisCtor.sdtClass); - TokenRValCall rvc = new TokenRValCall (thisCtor); - rvc.meth = baseCtor; + TokenRValCall rvc = new TokenRValCall(thisCtor); + rvc.meth = baseCtor; thisCtor.baseCtorCall = rvc; - thisCtor.unknownTrivialityCalls.AddLast (rvc); + thisCtor.unknownTrivialityCalls.AddLast(rvc); } /** @@ -2152,18 +2372,20 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + private void ParseSDTClassMethodDecl(ref Token token, uint flags, TokenDeclSDTypeClass tokdeclcl) { - TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, - (flags & SDT_ABSTRACT) != 0, - (flags & SDT_STATIC) == 0, + TokenDeclVar tokenDeclFunc = ParseDeclFunc(ref token, + (flags & SDT_ABSTRACT) != 0, + (flags & SDT_STATIC) == 0, (flags & SDT_STATIC) == 0); - if (tokenDeclFunc != null) { + if(tokenDeclFunc != null) + { tokenDeclFunc.sdtClass = tokdeclcl; tokenDeclFunc.sdtFlags = flags; - if (!tokenScript.AddVarEntry (tokenDeclFunc)) { + if(!tokenScript.AddVarEntry(tokenDeclFunc)) + { string funcNameSig = tokenDeclFunc.funcNameSig.val; - ErrorMsg (tokenDeclFunc.funcNameSig, "duplicate method name " + funcNameSig); + ErrorMsg(tokenDeclFunc.funcNameSig, "duplicate method name " + funcNameSig); } } } @@ -2176,7 +2398,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) + private void ParseDeclDelegate(ref Token token, TokenDeclSDType outerSDType, uint flags) { Token u = token; TokenDeclSDTypeDelegate tokdecldel; @@ -2189,60 +2411,73 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { // 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) { + if(u is TokenType) + { retType = (TokenType)u; u = u.nextToken; - } else { - retType = new TokenTypeVoid (u); + } + else + { + retType = new TokenTypeVoid(u); } // get list of argument types until we see a ')' - List args = new List (); + List args = new List(); bool first = true; - do { - if (first) { + do + { + if(first) + { // first time should have '(' ')' or '(' - if (!(u is TokenKwParOpen)) { - ErrorMsg (u, "expecting ( after delegate name"); - token = SkipPastSemi (token); + 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 { + if(u is TokenKwParClose) + break; + } + else + { // other times should have ',' - if (!(u is TokenKwComma)) { - ErrorMsg (u, "expecting , separating arg types"); - token = SkipPastSemi (token); + 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); + if(!(u is TokenType)) + { + ErrorMsg(u, "expecting argument type"); + token = SkipPastSemi(token); return; } - args.Add ((TokenType)u); + 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; + if(u is TokenName) + u = u.nextToken; // scanning ends on a ')' - } while (!(u is TokenKwParClose)); + } while(!(u is TokenKwParClose)); // fill in the return type and argment type array - tokdecldel.SetRetArgTypes (retType, args.ToArray ()); + 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); + if(!(u is TokenKwSemi)) + { + ErrorMsg(u, "expecting ; after ) in delegate"); + token = SkipPastSemi(token); return; } token = u.nextToken; @@ -2256,7 +2491,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) + private void ParseDeclInterface(ref Token token, TokenDeclSDType outerSDType, uint flags) { Token u = token; TokenDeclSDTypeInterface tokdeclin; @@ -2267,39 +2502,50 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { u = u.nextToken; // next can be ':' followed by list of implemented interfaces - if (u is TokenKwColon) { + if(u is TokenKwColon) + { u = u.nextToken; - while (true) { - if (u is TokenTypeSDTypeInterface) { + while(true) + { + if(u is TokenTypeSDTypeInterface) + { TokenDeclSDTypeInterface i = ((TokenTypeSDTypeInterface)u).decl; - if (!tokdeclin.implements.Contains (i)) { - tokdeclin.implements.Add (i); + if(!tokdeclin.implements.Contains(i)) + { + tokdeclin.implements.Add(i); } - } else { - ErrorMsg (u, "expecting interface name"); - if (u is TokenKwBrcOpen) break; + } + else + { + ErrorMsg(u, "expecting interface name"); + if(u is TokenKwBrcOpen) + break; } u = u.nextToken; - if (!(u is TokenKwComma)) break; + 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); + 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); + tokenScript.PushVarFrame(false); tokdeclin.methsNProps = tokenScript.variablesStack; // process declaration statements until '}' - while (!(token is TokenKwBrcClose)) { - if (token is TokenKwSemi) { + while(!(token is TokenKwBrcClose)) + { + if(token is TokenKwSemi) + { token = token.nextToken; continue; } @@ -2307,20 +2553,24 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Parse nested script-defined type definitions. */ - if (ParseDeclSDTypes (ref token, tokdeclin, SDT_PUBLIC)) continue; + if(ParseDeclSDTypes(ref token, tokdeclin, SDT_PUBLIC)) + continue; /* * ; * abstract method with explicit return type */ - if ((token is TokenType) && - (token.nextToken is TokenName) && - (token.nextToken.nextToken is TokenKwParOpen)) { + 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"); + TokenDeclVar tokenDeclFunc = ParseDeclFunc(ref token, true, false, false); + if(tokenDeclFunc == null) + continue; + if(!tokenScript.AddVarEntry(tokenDeclFunc)) + { + ErrorMsg(name, "duplicate method name"); continue; } continue; @@ -2330,13 +2580,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * ; * abstract method returning void */ - if ((token is TokenName) && - (token.nextToken is TokenKwParOpen)) { + 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"); + TokenDeclVar tokenDeclFunc = ParseDeclFunc(ref token, true, false, false); + if(tokenDeclFunc == null) + continue; + if(!tokenScript.AddVarEntry(tokenDeclFunc)) + { + ErrorMsg(name, "duplicate method name"); } continue; } @@ -2348,26 +2601,27 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { */ bool prop = (token is TokenType) && (token.nextToken is TokenName) && - (token.nextToken.nextToken is TokenKwBrcOpen || + (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); + 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); + 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 (); + tokenScript.PopVarFrame(); } /** @@ -2377,26 +2631,31 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: token representing state * token = points past close brace */ - private TokenStateBody ParseStateBody (ref Token token) + private TokenStateBody ParseStateBody(ref Token token) { - TokenStateBody tokenStateBody = new TokenStateBody (token); + TokenStateBody tokenStateBody = new TokenStateBody(token); - if (!(token is TokenKwBrcOpen)) { - ErrorMsg (token, "expecting { at beg of state"); - token = SkipPastSemi (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"); + 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"); + 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; @@ -2416,30 +2675,39 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) + private TokenDeclVar ParseDeclFunc(ref Token token, bool abs, bool imp, bool ops) { TokenType retType; - if (token is TokenType) { + if(token is TokenType) + { retType = (TokenType)token; token = token.nextToken; - } else { - retType = new TokenTypeVoid (token); + } + 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); + 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 { + } + else + { simpleName = (TokenName)token; } token = token.nextToken; - return ParseDeclFunc (ref token, abs, imp, retType, simpleName); + return ParseDeclFunc(ref token, abs, imp, retType, simpleName); } /** @@ -2454,25 +2722,31 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) + 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; + 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"); + 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; + if(!ParseFunctionBody(ref token, tokenDeclFunc, abs)) + return null; + if(tokenDeclFunc.argDecl == null) + return null; return tokenDeclFunc; } @@ -2484,27 +2758,30 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * is implementing the interface method/property * @returns list of implemented interface methods/properties */ - private TokenIntfImpl ParseImplements (ref Token token, TokenName simpleName) + private TokenIntfImpl ParseImplements(ref Token token, TokenName simpleName) { TokenIntfImpl implements = null; - do { + do + { token = token.nextToken; - if (!(token is TokenTypeSDTypeInterface)) { - ErrorMsg (token, "expecting interface type"); - token = SkipPastSemi (token); + 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)) { + if((token is TokenKwDot) && (token.nextToken is TokenName)) + { methName = (TokenName)token.nextToken; token = token.nextToken.nextToken; } - TokenIntfImpl intfImpl = new TokenIntfImpl (intfType, methName); + TokenIntfImpl intfImpl = new TokenIntfImpl(intfType, methName); intfImpl.nextToken = implements; implements = intfImpl; - } while (token is TokenKwComma); + } while(token is TokenKwComma); return implements; } @@ -2516,12 +2793,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + 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); + if(token is TokenKwSemi) + { + if(!abs) + { + ErrorMsg(token, "concrete function must have body"); + token = SkipPastSemi(token); return false; } token = token.nextToken; @@ -2535,25 +2814,27 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { */ TokenDeclVar saveDeclFunc = currentDeclFunc; currentDeclFunc = tokenDeclFunc; - tokenScript.PushVarFrame (tokenDeclFunc.argDecl.varDict); + tokenScript.PushVarFrame(tokenDeclFunc.argDecl.varDict); /* * Now parse the function statement block. */ - tokenDeclFunc.body = ParseStmtBlock (ref token); + tokenDeclFunc.body = ParseStmtBlock(ref token); /* * Pop the var frame that contains the arguments. */ - tokenScript.PopVarFrame (); + tokenScript.PopVarFrame(); currentDeclFunc = saveDeclFunc; /* * Check final errors. */ - if (tokenDeclFunc.body == null) return false; - if (abs) { - ErrorMsg (tokenDeclFunc.body, "abstract function must not have body"); + if(tokenDeclFunc.body == null) + return false; + if(abs) + { + ErrorMsg(tokenDeclFunc.body, "abstract function must not have body"); tokenDeclFunc.body = null; return false; } @@ -2568,35 +2849,52 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: token representing whole statement * token = points past statement */ - private TokenStmt ParseStmt (ref Token token) + 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); + 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); + TokenRVal tokenRVal = ParseRVal(ref token, semiOnly); + if(tokenRVal != null) + { + TokenStmtRVal tokenStmtRVal = new TokenStmtRVal(tokenRVal); tokenStmtRVal.rVal = tokenRVal; return tokenStmtRVal; } @@ -2604,8 +2902,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Who knows what it is... */ - ErrorMsg (token, "unknown statement"); - token = SkipPastSemi (token); + ErrorMsg(token, "unknown statement"); + token = SkipPastSemi(token); return null; } @@ -2616,40 +2914,52 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: statements bundled in this token * token = advanced just past the } token */ - private TokenStmtBlock ParseStmtBlock (ref Token token) + private TokenStmtBlock ParseStmtBlock(ref Token token) { - if (!(token is TokenKwBrcOpen)) { - ErrorMsg (token, "statement block body must begin with a {"); - token = SkipPastSemi (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 tokenStmtBlock = new TokenStmtBlock(token); tokenStmtBlock.function = currentDeclFunc; tokenStmtBlock.outerStmtBlock = currentStmtBlock; currentStmtBlock = tokenStmtBlock; VarDict outerVariablesStack = tokenScript.variablesStack; - try { + try + { Token prevStmt = null; token = token.nextToken; - while (!(token is TokenKwBrcClose)) { - if (token is TokenEnd) { - ErrorMsg (tokenStmtBlock, "missing }"); + 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(((token is TokenType) && (token.nextToken is TokenName)) || + (token is TokenKwConst)) + { + thisStmt = ParseDeclVar(ref token, null); } - if (thisStmt == null) return null; - if (prevStmt == null) tokenStmtBlock.statements = thisStmt; - else prevStmt.nextToken = thisStmt; + 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 { + } + finally + { tokenScript.variablesStack = outerVariablesStack; currentStmtBlock = tokenStmtBlock.outerStmtBlock; } @@ -2663,13 +2973,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: statements bundled in this token * token = advanced just past the ; token */ - private TokenStmtBreak ParseStmtBreak (ref Token token) + private TokenStmtBreak ParseStmtBreak(ref Token token) { - TokenStmtBreak tokenStmtBreak = new TokenStmtBreak (token); + TokenStmtBreak tokenStmtBreak = new TokenStmtBreak(token); token = token.nextToken; - if (!(token is TokenKwSemi)) { - ErrorMsg (token, "expecting ;"); - token = SkipPastSemi (token); + if(!(token is TokenKwSemi)) + { + ErrorMsg(token, "expecting ;"); + token = SkipPastSemi(token); return null; } token = token.nextToken; @@ -2683,13 +2994,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: statements bundled in this token * token = advanced just past the ; token */ - private TokenStmtCont ParseStmtCont (ref Token token) + private TokenStmtCont ParseStmtCont(ref Token token) { - TokenStmtCont tokenStmtCont = new TokenStmtCont (token); + TokenStmtCont tokenStmtCont = new TokenStmtCont(token); token = token.nextToken; - if (!(token is TokenKwSemi)) { - ErrorMsg (token, "expecting ;"); - token = SkipPastSemi (token); + if(!(token is TokenKwSemi)) + { + ErrorMsg(token, "expecting ;"); + token = SkipPastSemi(token); return null; } token = token.nextToken; @@ -2703,23 +3015,27 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: pointer to token encapsulating the do statement, including body * token = advanced just past the body statement */ - private TokenStmtDo ParseStmtDo (ref Token token) + private TokenStmtDo ParseStmtDo(ref Token token) { currentDeclFunc.triviality = Triviality.complex; - TokenStmtDo tokenStmtDo = new TokenStmtDo (token); + 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"); + 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); + 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; @@ -2733,17 +3049,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: pointer to encapsulated for statement token * token = advanced just past for body statement */ - private TokenStmt ParseStmtFor (ref Token token) + private TokenStmt ParseStmtFor(ref Token token) { currentDeclFunc.triviality = Triviality.complex; /* * Create encapsulating token and skip past 'for (' */ - TokenStmtFor tokenStmtFor = new TokenStmtFor (token); + TokenStmtFor tokenStmtFor = new TokenStmtFor(token); token = token.nextToken; - if (!(token is TokenKwParOpen)) { - ErrorMsg (token, "for must be followed by ("); + if(!(token is TokenKwParOpen)) + { + ErrorMsg(token, "for must be followed by ("); return null; } token = token.nextToken; @@ -2751,35 +3068,38 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * 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; + 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); + TokenStmtBlock forStmtBlock = new TokenStmtBlock(tokenStmtFor); forStmtBlock.outerStmtBlock = currentStmtBlock; - forStmtBlock.function = currentDeclFunc; - currentStmtBlock = forStmtBlock; - tokenScript.PushVarFrame (true); + forStmtBlock.function = currentDeclFunc; + currentStmtBlock = forStmtBlock; + tokenScript.PushVarFrame(true); - TokenDeclVar tokenDeclVar = ParseDeclVar (ref token, null); - if (tokenDeclVar == null) { - tokenScript.PopVarFrame (); - currentStmtBlock = forStmtBlock.outerStmtBlock; + TokenDeclVar tokenDeclVar = ParseDeclVar(ref token, null); + if(tokenDeclVar == null) + { + tokenScript.PopVarFrame(); + currentStmtBlock = forStmtBlock.outerStmtBlock; return null; } - forStmtBlock.statements = tokenDeclVar; - tokenDeclVar.nextToken = tokenStmtFor; + forStmtBlock.statements = tokenDeclVar; + tokenDeclVar.nextToken = tokenStmtFor; - bool ok = ParseStmtFor2 (tokenStmtFor, ref token); - tokenScript.PopVarFrame (); - currentStmtBlock = forStmtBlock.outerStmtBlock; + bool ok = ParseStmtFor2(tokenStmtFor, ref token); + tokenScript.PopVarFrame(); + currentStmtBlock = forStmtBlock.outerStmtBlock; return ok ? forStmtBlock : null; } @@ -2791,21 +3111,29 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * true: successful * token = points just past body statement */ - private bool ParseStmtFor2 (TokenStmtFor tokenStmtFor, ref Token token) + private bool ParseStmtFor2(TokenStmtFor tokenStmtFor, ref Token token) { - if (token is TokenKwSemi) { + if(token is TokenKwSemi) + { token = token.nextToken; - } else { - tokenStmtFor.testRVal = ParseRVal (ref token, semiOnly); - if (tokenStmtFor.testRVal == null) return false; } - if (token is TokenKwParClose) { + 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); + else + { + tokenStmtFor.incrRVal = ParseRVal(ref token, parCloseOnly); + if(tokenStmtFor.incrRVal == null) + return false; + } + tokenStmtFor.bodyStmt = ParseStmt(ref token); return tokenStmtFor.bodyStmt != null; } @@ -2816,80 +3144,93 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: pointer to encapsulated foreach statement token * token = advanced just past for body statement */ - private TokenStmt ParseStmtForEach (ref Token token) + private TokenStmt ParseStmtForEach(ref Token token) { currentDeclFunc.triviality = Triviality.complex; /* * Create encapsulating token and skip past 'foreach (' */ - TokenStmtForEach tokenStmtForEach = new TokenStmtForEach (token); + TokenStmtForEach tokenStmtForEach = new TokenStmtForEach(token); token = token.nextToken; - if (!(token is TokenKwParOpen)) { - ErrorMsg (token, "foreach must be followed by ("); + 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); + 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); + 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); + 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); + 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); + 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; + tokenStmtForEach.bodyStmt = ParseStmt(ref token); + if(tokenStmtForEach.bodyStmt == null) + return null; return tokenStmtForEach; } - private TokenStmtIf ParseStmtIf (ref Token token) + private TokenStmtIf ParseStmtIf(ref Token token) { - TokenStmtIf tokenStmtIf = new TokenStmtIf (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) { + 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; + tokenStmtIf.elseStmt = ParseStmt(ref token); + if(tokenStmtIf.elseStmt == null) + return null; } return tokenStmtIf; } - private TokenStmtJump ParseStmtJump (ref Token token) + private TokenStmtJump ParseStmtJump(ref Token token) { /* * Create jump statement token to encapsulate the whole statement. */ - TokenStmtJump tokenStmtJump = new TokenStmtJump (token); + TokenStmtJump tokenStmtJump = new TokenStmtJump(token); token = token.nextToken; - if (!(token is TokenName) || !(token.nextToken is TokenKwSemi)) { - ErrorMsg (token, "expecting label;"); - token = SkipPastSemi (token); + if(!(token is TokenName) || !(token.nextToken is TokenKwSemi)) + { + ErrorMsg(token, "expecting label;"); + token = SkipPastSemi(token); return null; } tokenStmtJump.label = (TokenName)token; @@ -2900,7 +3241,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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)) { + if(currentDeclFunc.labels.ContainsKey(tokenStmtJump.label.val)) + { currentDeclFunc.labels[tokenStmtJump.label.val].hasBkwdRefs = true; currentDeclFunc.triviality = Triviality.complex; } @@ -2915,116 +3257,149 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: the label * token = advanced just past the ; */ - private TokenStmtLabel ParseStmtLabel (ref Token token) + private TokenStmtLabel ParseStmtLabel(ref Token token) { - if (!(token.nextToken is TokenName) || - !(token.nextToken.nextToken is TokenKwSemi)) { - ErrorMsg (token, "invalid label"); - token = SkipPastSemi (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; + 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); + 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); + currentDeclFunc.labels.Add(stmtLabel.name.val, stmtLabel); token = token.nextToken.nextToken.nextToken; return stmtLabel; } - private TokenStmtNull ParseStmtNull (ref Token token) + private TokenStmtNull ParseStmtNull(ref Token token) { - TokenStmtNull tokenStmtNull = new TokenStmtNull (token); + TokenStmtNull tokenStmtNull = new TokenStmtNull(token); token = token.nextToken; return tokenStmtNull; } - private TokenStmtRet ParseStmtRet (ref Token token) + private TokenStmtRet ParseStmtRet(ref Token token) { - TokenStmtRet tokenStmtRet = new TokenStmtRet (token); + TokenStmtRet tokenStmtRet = new TokenStmtRet(token); token = token.nextToken; - if (token is TokenKwSemi) { + if(token is TokenKwSemi) + { token = token.nextToken; - } else { - tokenStmtRet.rVal = ParseRVal (ref token, semiOnly); - if (tokenStmtRet.rVal == null) return null; + } + else + { + tokenStmtRet.rVal = ParseRVal(ref token, semiOnly); + if(tokenStmtRet.rVal == null) + return null; } return tokenStmtRet; } - private TokenStmtSwitch ParseStmtSwitch (ref Token token) + private TokenStmtSwitch ParseStmtSwitch(ref Token token) { - TokenStmtSwitch tokenStmtSwitch = new TokenStmtSwitch (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); + 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) { + while(!(token is TokenKwBrcClose)) + { + if(token is TokenKwCase) + { + tokenSwitchCase = new TokenSwitchCase(token); + if(tokenStmtSwitch.lastCase == null) + { tokenStmtSwitch.cases = tokenSwitchCase; - } else { + } + else + { tokenStmtSwitch.lastCase.nextCase = tokenSwitchCase; } - tokenStmtSwitch.lastCase = tokenSwitchCase; + tokenStmtSwitch.lastCase = tokenSwitchCase; token = token.nextToken; - tokenSwitchCase.rVal1 = ParseRVal (ref token, colonOrDotDotDot); - if (tokenSwitchCase.rVal1 == null) return null; - if (token is TokenKwDotDotDot) { + 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); + 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) { + } + else if(token is TokenKwDefault) + { + tokenSwitchCase = new TokenSwitchCase(token); + if(tokenStmtSwitch.lastCase == null) + { tokenStmtSwitch.cases = tokenSwitchCase; - } else { + } + else + { tokenStmtSwitch.lastCase.nextCase = tokenSwitchCase; } - tokenStmtSwitch.lastCase = tokenSwitchCase; + tokenStmtSwitch.lastCase = tokenSwitchCase; token = token.nextToken; - if (!(token is TokenKwColon)) { - ErrorMsg (token, "expecting :"); - token = SkipPastSemi (token); + 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) { + } + else if(tokenSwitchCase != null) + { + TokenStmt bodyStmt = ParseStmt(ref token); + if(bodyStmt == null) + return null; + if(tokenSwitchCase.lastStmt == null) + { tokenSwitchCase.stmts = bodyStmt; - } else { + } + else + { tokenSwitchCase.lastStmt.nextToken = bodyStmt; } tokenSwitchCase.lastStmt = bodyStmt; bodyStmt.nextToken = null; - } else if (!haveComplained) { - ErrorMsg (token, "expecting case or default label"); - token = SkipPastSemi (token); + } + else if(!haveComplained) + { + ErrorMsg(token, "expecting case or default label"); + token = SkipPastSemi(token); haveComplained = true; } } @@ -3032,31 +3407,37 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { return tokenStmtSwitch; } - private TokenStmtState ParseStmtState (ref Token token) + private TokenStmtState ParseStmtState(ref Token token) { - TokenStmtState tokenStmtState = new TokenStmtState (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); + if((!(token is TokenName) && !(token is TokenKwDefault)) || !(token.nextToken is TokenKwSemi)) + { + ErrorMsg(token, "expecting state;"); + token = SkipPastSemi(token); return null; } - if (token is TokenName) { + if(token is TokenName) + { tokenStmtState.state = (TokenName)token; } token = token.nextToken.nextToken; return tokenStmtState; } - private TokenStmtThrow ParseStmtThrow (ref Token token) + private TokenStmtThrow ParseStmtThrow(ref Token token) { - TokenStmtThrow tokenStmtThrow = new TokenStmtThrow (token); + TokenStmtThrow tokenStmtThrow = new TokenStmtThrow(token); token = token.nextToken; - if (token is TokenKwSemi) { + if(token is TokenKwSemi) + { token = token.nextToken; - } else { - tokenStmtThrow.rVal = ParseRVal (ref token, semiOnly); - if (tokenStmtThrow.rVal == null) return null; + } + else + { + tokenStmtThrow.rVal = ParseRVal(ref token, semiOnly); + if(tokenStmtThrow.rVal == null) + return null; } return tokenStmtThrow; } @@ -3067,68 +3448,77 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * points past last '}' processed on return * @returns encapsulated try/catch/finally or null if parsing error */ - private TokenStmtTry ParseStmtTry (ref Token token) + private TokenStmtTry ParseStmtTry(ref Token token) { /* * Parse out the 'try { ... }' part */ Token tryKw = token; token = token.nextToken; - TokenStmt body = ParseStmtBlock (ref token); + TokenStmt body = ParseStmtBlock(ref token); - while (true) { + while(true) + { TokenStmtTry tokenStmtTry; - if (token is TokenKwCatch) { - if (!(token.nextToken is TokenKwParOpen) || + 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 ( ) { ... }"); + !(token.nextToken.nextToken.nextToken.nextToken is TokenKwParClose)) + { + ErrorMsg(token, "catch must be followed by ( ) { ... }"); return null; } token = token.nextToken.nextToken; // skip over 'catch' '(' - TokenDeclVar tag = new TokenDeclVar (token.nextToken, currentDeclFunc, tokenScript); + TokenDeclVar tag = new TokenDeclVar(token.nextToken, currentDeclFunc, tokenScript); tag.type = (TokenType)token; - token = token.nextToken; // skip over + token = token.nextToken; // skip over tag.name = (TokenName)token; - token = token.nextToken.nextToken; // skip over ')' + token = token.nextToken.nextToken; // skip over ')' - if ((!(tag.type is TokenTypeExc)) && (!(tag.type is TokenTypeStr))) { - ErrorMsg (tag.type, "must be type 'exception' or 'string'"); + 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 = 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) { + 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 = 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; + tokenStmtTry.finallyStmt.tryStmt = tokenStmtTry; } - else break; + else + break; body = tokenStmtTry; } - if (!(body is TokenStmtTry)) { - ErrorMsg (body, "try must have a matching catch and/or finally"); + if(!(body is TokenStmtTry)) + { + ErrorMsg(body, "try must have a matching catch and/or finally"); return null; } return (TokenStmtTry)body; @@ -3144,33 +3534,38 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @param body = a TokenStmtTry or a TokenStmtBlock * @returns a TokenStmtBlock */ - private TokenStmtBlock WrapTryCatFinInBlock (TokenStmt body) + private TokenStmtBlock WrapTryCatFinInBlock(TokenStmt body) { - if (body is TokenStmtBlock) return (TokenStmtBlock)body; + if(body is TokenStmtBlock) + return (TokenStmtBlock)body; TokenStmtTry innerTry = (TokenStmtTry)body; - TokenStmtBlock wrapper = new TokenStmtBlock (body); - wrapper.statements = innerTry; + TokenStmtBlock wrapper = new TokenStmtBlock(body); + wrapper.statements = innerTry; wrapper.outerStmtBlock = currentStmtBlock; - wrapper.function = currentDeclFunc; + wrapper.function = currentDeclFunc; innerTry.tryStmt.outerStmtBlock = wrapper; - if (innerTry.catchStmt != null) innerTry.catchStmt.outerStmtBlock = wrapper; - if (innerTry.finallyStmt != null) innerTry.finallyStmt.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) + private TokenStmtWhile ParseStmtWhile(ref Token token) { currentDeclFunc.triviality = Triviality.complex; - TokenStmtWhile tokenStmtWhile = new TokenStmtWhile (token); + 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; + tokenStmtWhile.testRVal = ParseRValParen(ref token); + if(tokenStmtWhile.testRVal == null) + return null; + tokenStmtWhile.bodyStmt = ParseStmt(ref token); + if(tokenStmtWhile.bodyStmt == null) + return null; return tokenStmtWhile; } @@ -3186,9 +3581,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * token = advanced just past semi-colon * variables = modified to include the new variable */ - private TokenDeclVar ParseDeclVar (ref Token token, TokenDeclVar initFunc) + private TokenDeclVar ParseDeclVar(ref Token token, TokenDeclVar initFunc) { - TokenDeclVar tokenDeclVar = new TokenDeclVar (token.nextToken, currentDeclFunc, tokenScript); + TokenDeclVar tokenDeclVar = new TokenDeclVar(token.nextToken, currentDeclFunc, tokenScript); /* * Handle constant declaration. @@ -3198,23 +3593,27 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * * constant = ; */ - if (token is TokenKwConst) { + if(token is TokenKwConst) + { token = token.nextToken; - if (!(token is TokenName)) { - ErrorMsg (token, "expecting constant name"); - token = SkipPastSemi (token); + 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); + 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; + TokenRVal rVal = ParseRVal(ref token, semiOnly); + if(rVal == null) + return null; tokenDeclVar.init = rVal; tokenDeclVar.constant = true; } @@ -3222,15 +3621,17 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Otherwise, normal variable declaration with optional initialization value. */ - else { + 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); + if(!(token is TokenName)) + { + ErrorMsg(token, "expecting variable name"); + token = SkipPastSemi(token); return null; } tokenDeclVar.name = (TokenName)token; @@ -3240,24 +3641,34 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * If just a ;, there is no explicit initialization value. * Otherwise, look for an =RVal; expression that has init value. */ - if (token is TokenKwSemi) { + if(token is TokenKwSemi) + { token = token.nextToken; - if (initFunc != null) { - tokenDeclVar.init = TokenRValInitDef.Construct (tokenDeclVar); + if(initFunc != null) + { + tokenDeclVar.init = TokenRValInitDef.Construct(tokenDeclVar); } - } else if (token is TokenKwAssign) { + } + else if(token is TokenKwAssign) + { token = token.nextToken; - if (initFunc != null) { + if(initFunc != null) + { currentDeclFunc = initFunc; - tokenDeclVar.init = ParseRVal (ref token, semiOnly); + 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); + else + { + tokenDeclVar.init = ParseRVal(ref token, semiOnly); + } + if(tokenDeclVar.init == null) + return null; + } + else + { + ErrorMsg(token, "expecting = or ;"); + token = SkipPastSemi(token); return null; } } @@ -3266,15 +3677,17 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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); + 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); + if(!tokenScript.AddVarEntry(tokenDeclVar)) + { + ErrorMsg(tokenDeclVar, "duplicate variable " + tokenDeclVar.name.val); return null; } return tokenDeclVar; @@ -3287,20 +3700,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @param init = null: initialize to default value * else: initialize to this value */ - private void DoVarInit (TokenDeclVar initFunc, TokenLVal left, TokenRVal init) + 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); + 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); + } + else + { + TokenKw op = new TokenKwAssign(left); + TokenStmtRVal tsrv = new TokenStmtRVal(init); + tsrv.rVal = new TokenRValOpBin(left, op, init); stmt = tsrv; } @@ -3310,11 +3726,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * as some doofus scripts depend on it. */ Token lastStmt = initFunc.body.statements; - if (lastStmt == null) { + if(lastStmt == null) + { initFunc.body.statements = stmt; - } else { + } + else + { Token nextStmt; - while ((nextStmt = lastStmt.nextToken) != null) { + while((nextStmt = lastStmt.nextToken) != null) + { lastStmt = nextStmt; } lastStmt.nextToken = stmt; @@ -3328,38 +3748,44 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: points to token with types and names * token = updated past the TokenKw{Brk,Par}Close */ - private TokenArgDecl ParseFuncArgs (ref Token token, Type end) + private TokenArgDecl ParseFuncArgs(ref Token token, Type end) { - TokenArgDecl tokenArgDecl = new TokenArgDecl (token); + TokenArgDecl tokenArgDecl = new TokenArgDecl(token); bool first = true; - do { + do + { token = token.nextToken; - if ((token.GetType () == end) && first) break; - if (!(token is TokenType)) { - ErrorMsg (token, "expecting arg type"); - token = SkipPastSemi (token); + 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); + 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"); + if(!tokenArgDecl.AddArg(type, name)) + { + ErrorMsg(name, "duplicate arg name"); } first = false; - } while (token is TokenKwComma); + } while(token is TokenKwComma); - if (token.GetType () != end) { - ErrorMsg (token, "expecting comma or close bracket/paren"); - token = SkipPastSemi (token); + if(token.GetType() != end) + { + ErrorMsg(token, "expecting comma or close bracket/paren"); + token = SkipPastSemi(token); return null; } token = token.nextToken; @@ -3377,42 +3803,48 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * token = if termTokenType.Length == 1, points just past terminating token * else, points right at terminating token */ - public TokenRVal ParseRVal (ref Token token, Type[] termTokenTypes) + 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; + 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; + while(true) + { + Type tokType = token.GetType(); + for(int i = termTokenTypes.Length; --i >= 0;) + { + if(tokType == termTokenTypes[i]) + goto done; } /* * Special form: * is */ - if (token is TokenKwIs) { - TokenRValIsType tokenRValIsType = new TokenRValIsType (token); + if(token is TokenKwIs) + { + TokenRValIsType tokenRValIsType = new TokenRValIsType(token); token = token.nextToken; /* * Parse the . */ - tokenRValIsType.typeExp = ParseTypeExp (ref token); - if (tokenRValIsType.typeExp == null) return null; + tokenRValIsType.typeExp = ParseTypeExp(ref token); + if(tokenRValIsType.typeExp == null) + return null; /* * Replace top operand with result of is */ - tokenRValIsType.rValExp = operands; + tokenRValIsType.rValExp = operands; tokenRValIsType.nextToken = operands.nextToken; operands = tokenRValIsType; @@ -3425,8 +3857,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Peek at next operator. */ - BinOp binOp = GetOperator (ref token); - if (binOp == null) return null; + BinOp binOp = GetOperator(ref token); + if(binOp == null) + return null; /* * If there are stacked operators of higher or same precedence than new one, @@ -3445,27 +3878,32 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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); + 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; + binOps = binOps.pop; } /* * Handle conditional expression as a special form: * ? : */ - 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); + 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]; @@ -3481,89 +3919,103 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Push next operand on its stack. */ - TokenRVal operand = GetOperand (ref token); - if (operand == null) return null; + TokenRVal operand = GetOperand(ref token); + if(operand == null) + return null; operand.prevToken = operands; operands = operand; } - done: + done: /* * At end of expression, perform any stacked computations. */ - while (binOps != null) { - TokenRVal result = PerformBinOp ((TokenRVal)operands.prevToken, binOps, (TokenRVal)operands); + while(binOps != null) + { + TokenRVal result = PerformBinOp((TokenRVal)operands.prevToken, binOps, (TokenRVal)operands); result.prevToken = operands.prevToken.prevToken; operands = result; - binOps = binOps.pop; + 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(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; + if(termTokenTypes.Length == 1) + token = token.nextToken; return operands; } - private TokenTypeExp ParseTypeExp (ref Token token) + private TokenTypeExp ParseTypeExp(ref Token token) { - TokenTypeExp leftOperand = GetTypeExp (ref token); - if (leftOperand == null) return null; + TokenTypeExp leftOperand = GetTypeExp(ref token); + if(leftOperand == null) + return null; - while ((token is TokenKwAnd) || (token is TokenKwOr)) { + 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; + 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) + private TokenTypeExp GetTypeExp(ref Token token) { - if (token is TokenKwTilde) { - TokenTypeExpNot typeExpNot = new TokenTypeExpNot (token); + if(token is TokenKwTilde) + { + TokenTypeExpNot typeExpNot = new TokenTypeExpNot(token); token = token.nextToken; - typeExpNot.typeExp = GetTypeExp (ref token); - if (typeExpNot.typeExp == null) return null; + typeExpNot.typeExp = GetTypeExp(ref token); + if(typeExpNot.typeExp == null) + return null; return typeExpNot; } - if (token is TokenKwParOpen) { - TokenTypeExpPar typeExpPar = new TokenTypeExpPar (token); + 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); + 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); + if(token is TokenKwUndef) + { + TokenTypeExpUndef typeExpUndef = new TokenTypeExpUndef(token); token = token.nextToken; return typeExpUndef; } - if (token is TokenType) { - TokenTypeExpType typeExpType = new TokenTypeExpType (token); + 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); + ErrorMsg(token, "expected type"); + token = SkipPastSemi(token); return null; } @@ -3574,19 +4026,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: token that bundles or wraps the operand * token = points to token following last operand token */ - private TokenRVal GetOperand (ref Token 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); + 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"); + 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; @@ -3596,19 +4051,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Get the bulk of the operand, ie, without any of the below suffixes. */ - TokenRVal operand = GetOperandNoMods (ref token); - if (operand == null) return null; - modifiers: + 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); + 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"); + if(!(operand is TokenLVal)) + { + ErrorMsg(operand, "can post{in,de}crement only an L-value"); return null; } asnPost.lVal = (TokenLVal)operand; @@ -3618,13 +4076,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * If followed by a '.', it is an instance field or instance method reference. */ - if (token is TokenKwDot) { + if(token is TokenKwDot) + { token = token.nextToken; - if (!(token is TokenName)) { - ErrorMsg (token, ". must be followed by field/method name"); + if(!(token is TokenName)) + { + ErrorMsg(token, ". must be followed by field/method name"); return null; } - TokenLValIField field = new TokenLValIField (token); + TokenLValIField field = new TokenLValIField(token); field.baseRVal = operand; field.fieldName = (TokenName)token; operand = field; @@ -3635,16 +4095,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * If followed by a '[', it is an array subscript. */ - if (token is TokenKwBrkOpen) { - TokenLValArEle tokenLValArEle = new TokenLValArEle (token); + 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"); + tokenLValArEle.subRVal = ParseRVal(ref token, brkCloseOnly); + if(tokenLValArEle.subRVal == null) + { + ErrorMsg(tokenLValArEle, "invalid subscript"); return null; } @@ -3652,13 +4114,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * See if comma-separated list of values. */ TokenRVal subscriptRVals; - int numSubscripts = SplitCommaRVals (tokenLValArEle.subRVal, out subscriptRVals); - if (numSubscripts > 1) { + 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); + TokenRValList rValList = new TokenRValList(tokenLValArEle); rValList.rVal = subscriptRVals; rValList.nItems = numSubscripts; tokenLValArEle.subRVal = rValList; @@ -3676,19 +4139,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * If followed by a '(', it is a function/method call. */ - if (token is TokenKwParOpen) { - operand = ParseRValCall (ref token, operand); + 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; + 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; @@ -3697,66 +4163,76 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief same as GetOperand() except doesn't check for any modifiers */ - private TokenRVal GetOperandNoMods (ref Token token) + private TokenRVal GetOperandNoMods(ref Token token) { /* * Simple unary operators. */ - if ((token is TokenKwSub) || + if((token is TokenKwSub) || (token is TokenKwTilde) || - (token is TokenKwExclam)) { + (token is TokenKwExclam)) + { Token uop = token; token = token.nextToken; - TokenRVal rVal = GetOperand (ref token); - if (rVal == null) return null; - return PerformUnOp (uop, rVal); + TokenRVal rVal = GetOperand(ref token); + if(rVal == null) + return null; + return PerformUnOp(uop, rVal); } /* * Type casting. */ - if ((token is TokenKwParOpen) && + if((token is TokenKwParOpen) && (token.nextToken is TokenType) && - (token.nextToken.nextToken is TokenKwParClose)) { + (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); + 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); + if(token is TokenKwParOpen) + { + return ParseRValParen(ref token); } /* * Constants. */ - if (token is TokenChar) { - TokenRValConst rValConst = new TokenRValConst (token, ((TokenChar)token).val); + 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); + 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); + 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); + 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); + if(token is TokenKwUndef) + { + TokenRValUndef rValUndef = new TokenRValUndef((TokenKwUndef)token); token = token.nextToken; return rValUndef; } @@ -3764,49 +4240,60 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * '<'value,...'>', ie, rotation or vector */ - if (token is TokenKwCmpLT) { + if(token is TokenKwCmpLT) + { Token openBkt = token; token = token.nextToken; - TokenRVal rValAll = ParseRVal (ref token, cmpGTOnly); - if (rValAll == null) return null; + 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; - } + 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); + if(token is TokenKwBrkOpen) + { + TokenRValList rValList = new TokenRValList(token); token = token.nextToken; - if (token is TokenKwBrkClose) { + 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); + } + else + { + TokenRVal rValAll = ParseRVal(ref token, brkCloseOnly); + if(rValAll == null) + return null; + rValList.nItems = SplitCommaRVals(rValAll, out rValList.rVal); } return rValList; } @@ -3814,8 +4301,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Maybe we have . 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); + 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; @@ -3825,13 +4313,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * 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); + 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); + TokenRValThis zhis = new TokenRValThis(token, (TokenDeclSDTypeClass)currentDeclSDType); token = token.nextToken; return zhis; } @@ -3839,20 +4329,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * 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); + 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); + 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, + TokenLValBaseField baseField = new TokenLValBaseField(token, + (TokenName)token.nextToken.nextToken, (TokenDeclSDTypeClass)currentDeclFunc.sdtClass); token = token.nextToken.nextToken.nextToken; return baseField; @@ -3863,15 +4356,17 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * This ends up generating a call to static function .$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); + 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"); + TokenLValSField field = new TokenLValSField(token.nextToken.nextToken); + field.baseType = (TokenType)token.nextToken; + field.fieldName = new TokenName(token, "$new"); token = token.nextToken.nextToken; return field; } @@ -3879,8 +4374,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * All we got left is , eg, arg, function, global or local variable reference */ - if (token is TokenName) { - TokenLValName name = new TokenLValName ((TokenName)token, tokenScript.variablesStack); + if(token is TokenName) + { + TokenLValName name = new TokenLValName((TokenName)token, tokenScript.variablesStack); token = token.nextToken; return name; } @@ -3888,8 +4384,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * Who knows what it is supposed to be? */ - ErrorMsg (token, "invalid operand token"); - token = SkipPastSemi (token); + ErrorMsg(token, "invalid operand token"); + token = SkipPastSemi(token); return null; } @@ -3900,27 +4396,31 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @returns call expression value * token = points just past arg list ')' */ - private TokenRValCall ParseRValCall (ref Token token, TokenRVal meth) + private TokenRValCall ParseRValCall(ref Token token, TokenRVal meth) { /* * Set up basic function call struct with function name. */ - TokenRValCall rValCall = new TokenRValCall (token); + TokenRValCall rValCall = new TokenRValCall(token); rValCall.meth = meth; /* * Parse the call parameters, if any. */ token = token.nextToken; - if (token is TokenKwParClose) { + 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); + } + 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); + currentDeclFunc.unknownTrivialityCalls.AddLast(rValCall); return rValCall; } @@ -3931,25 +4431,30 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @returns null: invalid operator token * else: operator token and precedence */ - private BinOp GetOperator (ref Token token) + private BinOp GetOperator(ref Token token) { - BinOp binOp = new BinOp (); - if (precedence.TryGetValue (token.GetType (), out binOp.preced)) { + 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"); + if((token is TokenKwSemi) || (token is TokenKwBrcOpen) || (token is TokenKwBrcClose)) + { + ErrorMsg(token, "premature expression end"); } - token = SkipPastSemi (token); + else + { + ErrorMsg(token, "invalid operator"); + } + token = SkipPastSemi(token); return null; } - private class BinOp { + private class BinOp + { public BinOp pop; public TokenKw token; public int preced; @@ -3963,9 +4468,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @param right = right-hand operand * @returns resultant expression */ - private TokenRVal PerformBinOp (TokenRVal left, BinOp binOp, TokenRVal right) + private TokenRVal PerformBinOp(TokenRVal left, BinOp binOp, TokenRVal right) { - return new TokenRValOpBin (left, binOp.token, right); + return new TokenRValOpBin(left, binOp.token, right); } /** @@ -3975,9 +4480,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @param right = right-hand operand * @returns resultant constant or expression */ - private TokenRVal PerformUnOp (Token unOp, TokenRVal right) + private TokenRVal PerformUnOp(Token unOp, TokenRVal right) { - return new TokenRValOpUn ((TokenKw)unOp, right); + return new TokenRValOpUn((TokenKw)unOp, right); } /** @@ -3988,52 +4493,62 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * token = points just past terminating '}' * ...or null if parse error */ - private TokenRVal ParseRValNewArIni (ref Token token, TokenLValSField newCall) + private TokenRVal ParseRValNewArIni(ref Token token, TokenLValSField newCall) { - Stack stack = new Stack (); - TokenRValNewArIni arini = new TokenRValNewArIni (token); - arini.arrayType = newCall.baseType; + Stack stack = new Stack(); + TokenRValNewArIni arini = new TokenRValNewArIni(token); + arini.arrayType = newCall.baseType; TokenList values = null; - while (true) { + while(true) + { // open brace means start a (sub-)list - if (token is TokenKwBrcOpen) { - stack.Push (values); - values = new TokenList (token); - token = token.nextToken; + 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 '}' + 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 + 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"); + } + 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); + 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) { + TokenRVal append = ParseRVal(ref token, commaOrBrcClose); + if(append == null) + return null; + values.tl.Add(append); + if(token is TokenKwComma) + { token = token.nextToken; } } @@ -4046,17 +4561,19 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * else: parenthesized expression token or constant token * token = points past the close parenthesis */ - private TokenRValParen ParseRValParen (ref Token token) + private TokenRValParen ParseRValParen(ref Token token) { - if (!(token is TokenKwParOpen)) { - ErrorMsg (token, "expecting ("); - token = SkipPastSemi (token); + if(!(token is TokenKwParOpen)) + { + ErrorMsg(token, "expecting ("); + token = SkipPastSemi(token); return null; } - TokenRValParen tokenRValParen = new TokenRValParen (token); + TokenRValParen tokenRValParen = new TokenRValParen(token); token = token.nextToken; - tokenRValParen.rVal = ParseRVal (ref token, parCloseOnly); - if (tokenRValParen.rVal == null) return null; + tokenRValParen.rVal = ParseRVal(ref token, parCloseOnly); + if(tokenRValParen.rVal == null) + return null; return tokenRValParen; } @@ -4066,19 +4583,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + private int SplitCommaRVals(TokenRVal rValAll, out TokenRVal rVals) { - if (!(rValAll is TokenRValOpBin) || !(((TokenRValOpBin)rValAll).opcode is TokenKwComma)) { + if(!(rValAll is TokenRValOpBin) || !(((TokenRValOpBin)rValAll).opcode is TokenKwComma)) + { rVals = rValAll; - if (rVals.nextToken != null) throw new Exception ("expected null"); + 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); + int leftCount = SplitCommaRVals(opBin.rValLeft, out rValLeft); + int rightCount = SplitCommaRVals(opBin.rValRight, out rValRight); rVals = rValLeft; - while (rValLeft.nextToken != null) rValLeft = (TokenRVal)rValLeft.nextToken; + while(rValLeft.nextToken != null) + rValLeft = (TokenRVal)rValLeft.nextToken; rValLeft.nextToken = rValRight; return leftCount + rightCount; } @@ -4088,13 +4608,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @param token = what token is associated with the error * @param message = error message string */ - private void ErrorMsg (Token token, string message) + private void ErrorMsg(Token token, string message) { - if (!errors || (token.file != lastErrorFile) || (token.line > lastErrorLine)) { + if(!errors || (token.file != lastErrorFile) || (token.line > lastErrorLine)) + { errors = true; lastErrorFile = token.file; lastErrorLine = token.line; - token.ErrorMsg (message); + token.ErrorMsg(message); } } @@ -4103,18 +4624,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @param token = points to token to skip over * @returns token just after the semicolon or close brace */ - private Token SkipPastSemi (Token token) + private Token SkipPastSemi(Token token) { int braceLevel = 0; - while (!(token is TokenEnd)) { - if ((token is TokenKwSemi) && (braceLevel == 0)) { + while(!(token is TokenEnd)) + { + if((token is TokenKwSemi) && (braceLevel == 0)) + { return token.nextToken; } - if (token is TokenKwBrcOpen) { - braceLevel ++; + if(token is TokenKwBrcOpen) + { + braceLevel++; } - if ((token is TokenKwBrcClose) && (-- braceLevel <= 0)) { + if((token is TokenKwBrcClose) && (--braceLevel <= 0)) + { return token.nextToken; } token = token.nextToken; @@ -4126,18 +4651,19 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief Script-defined type declarations */ - public abstract class TokenDeclSDType : Token { - protected const byte CLASS = 0; - protected const byte DELEGATE = 1; + 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; + 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 innerSDTypes = new Dictionary (); - // indexed by shortName + public Dictionary innerSDTypes = new Dictionary(); + // 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 @@ -4146,7 +4672,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { 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 VarDict members = new VarDict(false); // declared fields, methods, properties if any public Dictionary genParams; // list of parameters for generic prototypes // null for non-generic prototypes @@ -4167,22 +4693,29 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { private TokenName _shortName; private TokenName _longName; - public TokenName shortName { - get { + public TokenName shortName + { + get + { return _shortName; } - set { + set + { _shortName = value; - _longName = null; + _longName = null; } } - public TokenName longName { - get { - if (_longName == null) { + public TokenName longName + { + get + { + if(_longName == null) + { _longName = _shortName; - if (outerSDType != null) { - _longName = new TokenName (_shortName, outerSDType.longName.val + "." + _shortName.val); + if(outerSDType != null) + { + _longName = new TokenName(_shortName, outerSDType.longName.val + "." + _shortName.val); } } return _longName; @@ -4195,12 +4728,12 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { */ private Dictionary 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); + 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 @@ -4212,12 +4745,12 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + 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)); + TokenDeclSDType instdecl = this.MakeBlank(new TokenName(this, name)); /* * If the original had an outer type, then so does the new one. @@ -4225,29 +4758,33 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * is 'ValueList' it will always be inside 'Dictionary' * not 'Dictionary' at this point. */ - if ((this.outerSDType != null) && (this.outerSDType.genParams != null)) throw new Exception (); + 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; + 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)); + while((prefix = prefix.nextToken) != this) + { + SpliceSourceToken(prefix.CopyToken(prefix)); } /* * Splice instantiation (instdecl) in just before the beginning token of prototype (this.begToken). */ - SpliceSourceToken (instdecl); + SpliceSourceToken(instdecl); /* * Now for the fun part... Copy the rest of the prototype body to the @@ -4262,8 +4799,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { int index; Token it, pt; TokenDeclSDType innerProto = this; - TokenDeclSDType innerInst = instdecl; - for (pt = this; (pt = pt.nextToken) != this.endToken;) { + TokenDeclSDType innerInst = instdecl; + for(pt = this; (pt = pt.nextToken) != this.endToken;) + { /* * Coming across a sub-type's declaration involves a deep copy of the @@ -4274,13 +4812,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 2) outerSDType is transformed from Dictionary to Dictionary. * 3) innerSDTypes is rebuilt when/if we find classes that are inner to this one. */ - if (pt is TokenDeclSDType) { + 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)); + TokenDeclSDType itSDType = ptSDType.MakeBlank(new TokenName(ptSDType.shortName, ptSDType.shortName.val)); /* * Set up the transformed outerSDType. @@ -4292,7 +4831,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * This clone is an inner type of its next outer level. */ - reduce.CatalogSDTypeDecl (itSDType); + reduce.CatalogSDTypeDecl(itSDType); /* * We need to manually copy any generic parameters of the class declaration being cloned. @@ -4305,7 +4844,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * We are now processing tokens for this cloned type declaration. */ innerProto = ptSDType; - innerInst = itSDType; + innerInst = itSDType; /* * Splice this clone token in. @@ -4316,36 +4855,39 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /* * 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); + 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); + else + it = pt.CopyToken(pt); /* * Whatever we came up with, splice it into the source token stream. */ - SpliceSourceToken (it); + 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) { + if(innerProto.endToken == pt) + { innerInst.endToken = it; innerProto = innerProto.outerSDType; - innerInst = innerInst.outerSDType; + innerInst = innerInst.outerSDType; } } /* * Clone and insert the terminator, either '}' or ';' */ - it = pt.CopyToken (pt); - SpliceSourceToken (it); + it = pt.CopyToken(pt); + SpliceSourceToken(it); instdecl.endToken = it; return instdecl; @@ -4354,7 +4896,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief Splice a source token in just before the type's beginning keyword. */ - private void SpliceSourceToken (Token it) + private void SpliceSourceToken(Token it) { it.nextToken = this.begToken; (it.prevToken = this.begToken.prevToken).nextToken = it; @@ -4368,37 +4910,43 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @param objFileReader = reads from the object file * @param asmFileWriter = writes to the disassembly file (might be null) */ - public static TokenDeclSDType ReadFromFile (Dictionary sdTypes, string name, + public static TokenDeclSDType ReadFromFile(Dictionary 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); + 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 (); + 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); + sdt.sdTypeIndex = objFileReader.ReadInt32(); + sdt.ReadFromFile(objFileReader, asmFileWriter); return sdt; } @@ -4408,50 +4956,59 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * either a script-defined type or an LSL-defined type * @returns type token */ - protected TokenType MakeTypeToken (string name) + protected TokenType MakeTypeToken(string name) { TokenDeclSDType sdtdecl; - if (sdTypes.TryGetValue (name, out sdtdecl)) return sdtdecl.MakeRefToken (this); - return TokenType.FromLSLType (this, name); + if(sdTypes.TryGetValue(name, out sdtdecl)) + return sdtdecl.MakeRefToken(this); + return TokenType.FromLSLType(this, name); } // debugging - returns, eg, 'Dictionary.Enumerator.Node' - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { // get long name broken down into segments from outermost to this - Stack declStack = new Stack (); - for (TokenDeclSDType decl = this; decl != null; decl = decl.outerSDType) { - declStack.Push (decl); + Stack declStack = new Stack(); + 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 ('<'); + 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 kvp in decl.genParams) { + foreach(KeyValuePair 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 (','); + for(int j = 0; j < parms.Length;) + { + sb.Append(parms[j]); + if(++j < parms.Length) + sb.Append(','); } - sb.Append ('>'); + sb.Append('>'); } - if (declStack.Count > 0) sb.Append ('.'); + if(declStack.Count > 0) + sb.Append('.'); } } } - public class TokenDeclSDTypeClass : TokenDeclSDType { - public List implements = new List (); + public class TokenDeclSDTypeClass: TokenDeclSDType + { + public List implements = new List(); public TokenDeclVar instFieldInit; // $instfieldinit function to do instance field initializations public TokenDeclVar staticFieldInit; // $staticfieldinit function to do static field initializations - public Dictionary intfIndices = new Dictionary (); // longname => this.iFaces index + public Dictionary intfIndices = new Dictionary(); // longname => this.iFaces index public TokenDeclSDTypeInterface[] iFaces; // array of implemented interfaces // low-end entries copied from rootward classes public TokenDeclVar[][] iImplFunc; // iImplFunc[i][j]: @@ -4463,15 +5020,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { 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 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 stackedMethods; - private List stackedIFaces; + private List stackedIFaces; public DynamicMethod[] vDynMeths; // virtual method entrypoints public Type[] vMethTypes; // virtual method delegate types @@ -4481,83 +5038,94 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { // i = interface number from this.intfIndices[name] // j = method of interface from iface.methods[name].vTableIndex - public TokenDeclSDTypeClass (TokenName shortName, bool isPartial) : base (shortName) + public TokenDeclSDTypeClass(TokenName shortName, bool isPartial) : base(shortName) { this.shortName = shortName; this.isPartial = isPartial; } - protected override TokenDeclSDType MakeBlank (TokenName shortName) + protected override TokenDeclSDType MakeBlank(TokenName shortName) { - return new TokenDeclSDTypeClass (shortName, false); + return new TokenDeclSDTypeClass(shortName, false); } - public override TokenType MakeRefToken (Token t) + public override TokenType MakeRefToken(Token t) { - return new TokenTypeSDTypeClass (t, this); + return new TokenTypeSDTypeClass(t, this); } - public override Type GetSysType () + public override Type GetSysType() { - return typeof (XMRSDTypeClObj); + 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) + public bool CanCastToIntf(TokenDeclSDTypeInterface intf) { - if (this.implements.Contains (intf)) return true; - if (this.extends == null) return false; - return this.extends.CanCastToIntf (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) + 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); + 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); + this.instSizes.WriteToFile(objFileWriter); + objFileWriter.Write(numVirtFuncs); - if (extends == null) { - objFileWriter.Write (""); - } else { - objFileWriter.Write (extends.longName.val); + if(extends == null) + { + objFileWriter.Write(""); + } + else + { + objFileWriter.Write(extends.longName.val); } - objFileWriter.Write (arrayOfRank); - if (arrayOfRank > 0) objFileWriter.Write (arrayOfType.ToString ()); + 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 ()); + 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); + objFileWriter.Write(-1); int numIFaces = iImplFunc.Length; - objFileWriter.Write (numIFaces); - for (int i = 0; i < numIFaces; i ++) { - objFileWriter.Write (iFaces[i].longName.val); + 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 ++) { + 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 ()); + objFileWriter.Write(meth.vTableIndex); + objFileWriter.Write(meth.GetObjCodeName()); + objFileWriter.Write(meth.GetDelType().decl.GetWholeSig()); } } } @@ -4565,60 +5133,68 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief Reconstruct from the file. */ - public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter) + public override void ReadFromFile(BinaryReader objFileReader, TextWriter asmFileWriter) { - instSizes.ReadFromFile (objFileReader); - numVirtFuncs = objFileReader.ReadInt32 (); + instSizes.ReadFromFile(objFileReader); + numVirtFuncs = objFileReader.ReadInt32(); - extendsStr = objFileReader.ReadString (); - arrayOfRank = objFileReader.ReadInt32 (); - if (arrayOfRank > 0) arrayOfTypeStr = objFileReader.ReadString (); + extendsStr = objFileReader.ReadString(); + arrayOfRank = objFileReader.ReadInt32(); + if(arrayOfRank > 0) + arrayOfTypeStr = objFileReader.ReadString(); - if (asmFileWriter != null) { - instSizes.WriteAsmFile (asmFileWriter, extendsStr + "." + shortName.val + ".numInst"); + if(asmFileWriter != null) + { + instSizes.WriteAsmFile(asmFileWriter, extendsStr + "." + shortName.val + ".numInst"); } - stackedMethods = new List (); + stackedMethods = new List(); int vTableIndex; - while ((vTableIndex = objFileReader.ReadInt32 ()) >= 0) { + while((vTableIndex = objFileReader.ReadInt32()) >= 0) + { StackedMethod sm; - sm.methVTI = vTableIndex; - sm.methName = objFileReader.ReadString (); - sm.methSig = objFileReader.ReadString (); - stackedMethods.Add (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][]; + int numIFaces = objFileReader.ReadInt32(); + if(numIFaces > 0) + { + iDynMeths = new DynamicMethod[numIFaces][]; iMethTypes = new Type[numIFaces][]; - stackedIFaces = new List (); - for (int i = 0; i < numIFaces; i ++) { - string iFaceName = objFileReader.ReadString (); + stackedIFaces = new List(); + for(int i = 0; i < numIFaces; i++) + { + string iFaceName = objFileReader.ReadString(); intfIndices[iFaceName] = i; - int numMeths = objFileReader.ReadInt32 (); - iDynMeths[i] = new DynamicMethod[numMeths]; + int numMeths = objFileReader.ReadInt32(); + iDynMeths[i] = new DynamicMethod[numMeths]; iMethTypes[i] = new Type[numMeths]; - for (int j = 0; j < numMeths; j ++) { + 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); + si.iFaceIndex = i; + si.methIndex = j; + si.vTableIndex = objFileReader.ReadInt32(); + si.methName = objFileReader.ReadString(); + si.methSig = objFileReader.ReadString(); + stackedIFaces.Add(si); } } } } - private struct StackedMethod { + private struct StackedMethod + { public int methVTI; public string methName; public string methSig; } - private struct StackedIFace { + 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 @@ -4630,35 +5206,41 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + public void FillVTables(ScriptObjCode scriptObjCode) { - if (extendsStr != null) { - if (extendsStr != "") { + if(extendsStr != null) + { + if(extendsStr != "") + { extends = (TokenDeclSDTypeClass)scriptObjCode.sdObjTypesName[extendsStr]; - extends.FillVTables (scriptObjCode); + extends.FillVTables(scriptObjCode); } extendsStr = null; } - if (arrayOfTypeStr != null) { - arrayOfType = MakeTypeToken (arrayOfTypeStr); + if(arrayOfTypeStr != null) + { + arrayOfType = MakeTypeToken(arrayOfTypeStr); arrayOfTypeStr = null; } - if ((numVirtFuncs > 0) && (stackedMethods != null)) { + if((numVirtFuncs > 0) && (stackedMethods != null)) + { /* * Allocate arrays big enough for mine plus type we are extending. */ - vDynMeths = new DynamicMethod[numVirtFuncs]; + vDynMeths = new DynamicMethod[numVirtFuncs]; vMethTypes = new Type[numVirtFuncs]; /* * Fill in low parts from type we are extending. */ - if (extends != null) { + if(extends != null) + { int n = extends.numVirtFuncs; - for (int i = 0; i < n; i ++) { - vDynMeths[i] = extends.vDynMeths[i]; + for(int i = 0; i < n; i++) + { + vDynMeths[i] = extends.vDynMeths[i]; vMethTypes[i] = extends.vMethTypes[i]; } } @@ -4667,93 +5249,107 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * Fill in high parts with my own methods. * Might also overwrite lower ones with 'override' methods. */ - foreach (StackedMethod sm in stackedMethods) { + foreach(StackedMethod sm in stackedMethods) + { int i = sm.methVTI; string methName = sm.methName; DynamicMethod dm; - if (scriptObjCode.dynamicMethods.TryGetValue (methName, out dm)) { + if(scriptObjCode.dynamicMethods.TryGetValue(methName, out dm)) + { // method is not abstract - vDynMeths[i] = dm; - vMethTypes[i] = GetDynamicMethodDelegateType (dm, sm.methSig); + 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; + if(stackedIFaces != null) + { + foreach(StackedIFace si in stackedIFaces) + { + int i = si.iFaceIndex; + int j = si.methIndex; int vti = si.vTableIndex; - string methName = si.methName; + string methName = si.methName; DynamicMethod dm = scriptObjCode.dynamicMethods[methName]; - iDynMeths[i][j] = (vti < 0) ? dm : vDynMeths[vti]; - iMethTypes[i][j] = GetDynamicMethodDelegateType (dm, si.methSig); + iDynMeths[i][j] = (vti < 0) ? dm : vDynMeths[vti]; + iMethTypes[i][j] = GetDynamicMethodDelegateType(dm, si.methSig); } stackedIFaces = null; } } - private Type GetDynamicMethodDelegateType (DynamicMethod dm, string methSig) + private Type GetDynamicMethodDelegateType(DynamicMethod dm, string methSig) { Type retType = dm.ReturnType; - ParameterInfo[] pi = dm.GetParameters (); + ParameterInfo[] pi = dm.GetParameters(); Type[] argTypes = new Type[pi.Length]; - for (int j = 0; j < pi.Length; j ++) { + for(int j = 0; j < pi.Length; j++) + { argTypes[j] = pi[j].ParameterType; } - return DelegateCommon.GetType (retType, argTypes, methSig); + return DelegateCommon.GetType(retType, argTypes, methSig); } - public override void DebString (StringBuilder sb) + 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; + if(arrayOfType != null) + return; /* * This class name and extended/implemented type declaration. */ - sb.Append ("class "); - sb.Append (shortName.val); + sb.Append("class "); + sb.Append(shortName.val); bool first = true; - if (extends != null) { - sb.Append (" : "); - sb.Append (extends.longName); + if(extends != null) + { + sb.Append(" : "); + sb.Append(extends.longName); first = false; } - foreach (TokenDeclSDType impld in implements) { - sb.Append (first ? " : " : ", "); - sb.Append (impld.longName); + foreach(TokenDeclSDType impld in implements) + { + sb.Append(first ? " : " : ", "); + sb.Append(impld.longName); first = false; } - sb.Append (" {"); + sb.Append(" {"); /* * Inner type definitions. */ - foreach (TokenDeclSDType subs in innerSDTypes.Values) { - subs.DebString (sb); + 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); + foreach(TokenDeclVar memb in members) + { + if((memb == instFieldInit) || (memb == staticFieldInit)) + { + memb.DebStringInitFields(sb); + } + else if(memb.retType != null) + { + memb.DebString(sb); } } - sb.Append ('}'); + sb.Append('}'); } } - public class TokenDeclSDTypeDelegate : TokenDeclSDType { + public class TokenDeclSDTypeDelegate: TokenDeclSDType + { private TokenType retType; private TokenType[] argTypes; @@ -4766,108 +5362,116 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { private string retStr; private string[] argStrs; - private static Dictionary inlines = new Dictionary (); - private static Dictionary inlrevs = new Dictionary (); + private static Dictionary inlines = new Dictionary(); + private static Dictionary inlrevs = new Dictionary(); - public TokenDeclSDTypeDelegate (TokenName shortName) : base (shortName) + public TokenDeclSDTypeDelegate(TokenName shortName) : base(shortName) { this.shortName = shortName; } - public void SetRetArgTypes (TokenType retType, TokenType[] argTypes) + public void SetRetArgTypes(TokenType retType, TokenType[] argTypes) { - this.retType = retType; + this.retType = retType; this.argTypes = argTypes; } - protected override TokenDeclSDType MakeBlank (TokenName shortName) + protected override TokenDeclSDType MakeBlank(TokenName shortName) { - return new TokenDeclSDTypeDelegate (shortName); + return new TokenDeclSDTypeDelegate(shortName); } - public override TokenType MakeRefToken (Token t) + public override TokenType MakeRefToken(Token t) { - return new TokenTypeSDTypeDelegate (t, this); + return new TokenTypeSDTypeDelegate(t, this); } /** * @brief Get system type for the whole delegate. */ - public override Type GetSysType () + public override Type GetSysType() { - if (sysType == null) FillInStuff (); + if(sysType == null) + FillInStuff(); return sysType; } /** * @brief Get the function's return value type (TokenTypeVoid if void, never null) */ - public TokenType GetRetType () + public TokenType GetRetType() { - if (retType == null) FillInStuff (); + if(retType == null) + FillInStuff(); return retType; } /** * @brief Get the function's argument types */ - public TokenType[] GetArgTypes () + public TokenType[] GetArgTypes() { - if (argTypes == null) FillInStuff (); + if(argTypes == null) + FillInStuff(); return argTypes; } /** * @brief Get signature for the whole delegate, eg, "void(integer,list)" */ - public string GetWholeSig () + public string GetWholeSig() { - if (wholeSig == null) FillInStuff (); + if(wholeSig == null) + FillInStuff(); return wholeSig; } /** * @brief Get signature for the arguments, eg, "(integer,list)" */ - public string GetArgSig () + public string GetArgSig() { - if (argSig == null) FillInStuff (); + if(argSig == null) + FillInStuff(); return argSig; } /** * @brief Find out how to create one of these delegates. */ - public ConstructorInfo GetConstructorInfo () + public ConstructorInfo GetConstructorInfo() { - if (sysType == null) FillInStuff (); - return sysType.GetConstructor (DelegateCommon.constructorArgTypes); + 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 () + public MethodInfo GetInvokerInfo() { - if (sysType == null) FillInStuff (); - return sysType.GetMethod ("Invoke", argSysTypes); + 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) + 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(this.file); + objFileWriter.Write(this.line); + objFileWriter.Write(this.posn); + objFileWriter.Write((byte)DELEGATE); + objFileWriter.Write(this.sdTypeIndex); - objFileWriter.Write (retType.ToString ()); + objFileWriter.Write(retType.ToString()); int nArgs = argTypes.Length; - objFileWriter.Write (nArgs); - for (int i = 0; i < nArgs; i ++) { - objFileWriter.Write (argTypes[i].ToString ()); + objFileWriter.Write(nArgs); + for(int i = 0; i < nArgs; i++) + { + objFileWriter.Write(argTypes[i].ToString()); } } @@ -4875,30 +5479,35 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + public override void ReadFromFile(BinaryReader objFileReader, TextWriter asmFileWriter) { - retStr = objFileReader.ReadString (); - int nArgs = objFileReader.ReadInt32 (); - if (asmFileWriter != null) { - asmFileWriter.Write (" delegate " + retStr + " " + longName.val + "("); + 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]); + 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 (");"); + if(asmFileWriter != null) + { + asmFileWriter.WriteLine(");"); } } /** * @brief Fill in missing internal data. */ - private void FillInStuff () + private void FillInStuff() { int nArgs; @@ -4908,14 +5517,17 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * all definitions have been read from the object file in case * there are forward references. */ - if (retType == null) { - retType = MakeTypeToken (retStr); + if(retType == null) + { + retType = MakeTypeToken(retStr); } - if (argTypes == null) { + if(argTypes == null) + { nArgs = argStrs.Length; argTypes = new TokenType[nArgs]; - for (int i = 0; i < nArgs; i ++) { - argTypes[i] = MakeTypeToken (argStrs[i]); + for(int i = 0; i < nArgs; i++) + { + argTypes[i] = MakeTypeToken(argStrs[i]); } } @@ -4926,24 +5538,26 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { retSysType = retType.ToSysType(); nArgs = argTypes.Length; - StringBuilder sb = new StringBuilder (); + 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('('); + 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; + 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); + sysType = DelegateCommon.GetType(retSysType, argSysTypes, wholeSig); } /** @@ -4952,109 +5566,118 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * shared by all scripts, and it is just used when the * script engine is loaded. */ - public static TokenDeclSDTypeDelegate CreateInline (TokenType retType, TokenType[] argTypes) + 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 ("("); + 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 ()); + 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)) { + 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; + 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); + inlines.Add(inlname, decldel); + inlrevs.Add(decldel.GetSysType(), inlname); } return decldel; } - public static string TryGetInlineName (Type sysType) + public static string TryGetInlineName(Type sysType) { string name; - if (!inlrevs.TryGetValue (sysType, out name)) return null; + if(!inlrevs.TryGetValue(sysType, out name)) + return null; return name; } - public static Type TryGetInlineSysType (string name) + public static Type TryGetInlineSysType(string name) { TokenDeclSDTypeDelegate decl; - if (!inlines.TryGetValue (name, out decl)) return null; - return decl.GetSysType (); + 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 class TokenDeclSDTypeInterface: TokenDeclSDType + { + public VarDict methsNProps = new VarDict(false); + // any class that implements this interface + // must implement all of these methods & properties - public List implements = new List (); - // any class that implements this interface - // must also implement all of the methods & properties - // of all of these interfaces + public List implements = new List(); + // 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) + public TokenDeclSDTypeInterface(TokenName shortName) : base(shortName) { this.shortName = shortName; } - protected override TokenDeclSDType MakeBlank (TokenName shortName) + protected override TokenDeclSDType MakeBlank(TokenName shortName) { - return new TokenDeclSDTypeInterface (shortName); + return new TokenDeclSDTypeInterface(shortName); } - public override TokenType MakeRefToken (Token t) + public override TokenType MakeRefToken(Token t) { - return new TokenTypeSDTypeInterface (t, this); + return new TokenTypeSDTypeInterface(t, this); } - public override Type GetSysType () + 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[]); + return typeof(Delegate[]); } - public override void WriteToFile (BinaryWriter objFileWriter) + 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); + 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) - { } + 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) + public void AddToClassDecl(TokenDeclSDTypeClass tokdeclcl) { - if (!tokdeclcl.implements.Contains (this)) { - tokdeclcl.implements.Add (this); - foreach (TokenDeclSDTypeInterface subimpl in this.implements) { - subimpl.AddToClassDecl (tokdeclcl); + if(!tokdeclcl.implements.Contains(this)) + { + tokdeclcl.implements.Add(this); + foreach(TokenDeclSDTypeInterface subimpl in this.implements) + { + subimpl.AddToClassDecl(tokdeclcl); } } } @@ -5063,11 +5686,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @brief See if the 'this' interface implements the new interface. * Do a recursive (deep) check. */ - public bool Implements (TokenDeclSDTypeInterface newDecl) + public bool Implements(TokenDeclSDTypeInterface newDecl) { - foreach (TokenDeclSDTypeInterface ii in this.implements) { - if (ii == newDecl) return true; - if (ii.Implements (newDecl)) return true; + foreach(TokenDeclSDTypeInterface ii in this.implements) + { + if(ii == newDecl) + return true; + if(ii.Implements(newDecl)) + return true; } return false; } @@ -5080,45 +5706,51 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + 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; + 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 class TokenDeclSDTypeTypedef: TokenDeclSDType + { - public TokenDeclSDTypeTypedef (TokenName shortName) : base (shortName) + public TokenDeclSDTypeTypedef(TokenName shortName) : base(shortName) { this.shortName = shortName; } - protected override TokenDeclSDType MakeBlank (TokenName shortName) + protected override TokenDeclSDType MakeBlank(TokenName shortName) { - return new TokenDeclSDTypeTypedef (shortName); + return new TokenDeclSDTypeTypedef(shortName); } - public override TokenType MakeRefToken (Token t) + 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"); + 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); + return (TokenType)this.nextToken.CopyToken(t); } - public override Type GetSysType () + public override Type GetSysType() { // we are just a macro // we are asked for system type because we are cataloged @@ -5126,17 +5758,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { return null; } - public override void WriteToFile (BinaryWriter objFileWriter) + 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); + 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) - { } + public override void ReadFromFile(BinaryReader objFileReader, TextWriter asmFileWriter) + { + } } /** @@ -5144,54 +5777,57 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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 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 class TokenTypeSDTypeClass: TokenTypeSDType + { + private static readonly FieldInfo iarSDTClObjsFieldInfo = typeof(XMRInstArrays).GetField("iarSDTClObjs"); public TokenDeclSDTypeClass decl; - public TokenTypeSDTypeClass (Token t, TokenDeclSDTypeClass decl) : base (t) + public TokenTypeSDTypeClass(Token t, TokenDeclSDTypeClass decl) : base(t) { this.decl = decl; } - public override TokenDeclSDType GetDecl () + public override TokenDeclSDType GetDecl() { return decl; } - public override void SetDecl (TokenDeclSDType decl) + public override void SetDecl(TokenDeclSDType decl) { this.decl = (TokenDeclSDTypeClass)decl; } - public override string ToString () + public override string ToString() { return decl.longName.val; } - public override Type ToSysType () + public override Type ToSysType() { - return typeof (XMRSDTypeClObj); + return typeof(XMRSDTypeClObj); } - public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias) + public override void AssignVarSlot(TokenDeclVar declVar, XMRInstArSizes ias) { declVar.vTableArray = iarSDTClObjsFieldInfo; - declVar.vTableIndex = ias.iasSDTClObjs ++; + declVar.vTableIndex = ias.iasSDTClObjs++; } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append (decl.longName); + sb.Append(decl.longName); } } - public class TokenTypeSDTypeDelegate : TokenTypeSDType { - private static readonly FieldInfo iarObjectsFieldInfo = typeof (XMRInstArrays).GetField ("iarObjects"); + public class TokenTypeSDTypeDelegate: TokenTypeSDType + { + private static readonly FieldInfo iarObjectsFieldInfo = typeof(XMRInstArrays).GetField("iarObjects"); public TokenDeclSDTypeDelegate decl; @@ -5200,15 +5836,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + public TokenTypeSDTypeDelegate(Token t, TokenDeclSDTypeDelegate decl) : base(t) { this.decl = decl; } - public override TokenDeclSDType GetDecl () + public override TokenDeclSDType GetDecl() { return decl; } - public override void SetDecl (TokenDeclSDType decl) + public override void SetDecl(TokenDeclSDType decl) { this.decl = (TokenDeclSDTypeDelegate)decl; } @@ -5220,7 +5856,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + public TokenTypeSDTypeDelegate(Token t, TokenType retType, TokenType[] argTypes, TokenScript tokenScript) : base(t) { TokenDeclSDTypeDelegate decldel; @@ -5228,48 +5864,56 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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; + 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:; + nomatch: + ; } /* * No such luck, create a new anonymous declaration. */ - StringBuilder sb = new StringBuilder ("$anondel$"); - sb.Append (retType.ToString ()); - sb.Append ("("); + 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 ()); + 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); + 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 () + public override Type ToSysType() { - return decl.GetSysType (); + return decl.GetSysType(); } - public override string ToString () + public override string ToString() { return decl.longName.val; } @@ -5278,98 +5922,103 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + public override void AssignVarSlot(TokenDeclVar declVar, XMRInstArSizes ias) { declVar.vTableArray = iarObjectsFieldInfo; - declVar.vTableIndex = ias.iasObjects ++; + declVar.vTableIndex = ias.iasObjects++; } /** * @brief create delegate reference token for inline functions. */ - public TokenTypeSDTypeDelegate (TokenType retType, TokenType[] argTypes) : base (null) + public TokenTypeSDTypeDelegate(TokenType retType, TokenType[] argTypes) : base(null) { - this.decl = TokenDeclSDTypeDelegate.CreateInline (retType, argTypes); + this.decl = TokenDeclSDTypeDelegate.CreateInline(retType, argTypes); } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append (decl.longName); + sb.Append(decl.longName); } } - public class TokenTypeSDTypeInterface : TokenTypeSDType { - private static readonly FieldInfo iarSDTIntfObjsFieldInfo = typeof (XMRInstArrays).GetField ("iarSDTIntfObjs"); + public class TokenTypeSDTypeInterface: TokenTypeSDType + { + private static readonly FieldInfo iarSDTIntfObjsFieldInfo = typeof(XMRInstArrays).GetField("iarSDTIntfObjs"); public TokenDeclSDTypeInterface decl; - public TokenTypeSDTypeInterface (Token t, TokenDeclSDTypeInterface decl) : base (t) + public TokenTypeSDTypeInterface(Token t, TokenDeclSDTypeInterface decl) : base(t) { this.decl = decl; } - public override TokenDeclSDType GetDecl () + public override TokenDeclSDType GetDecl() { return decl; } - public override void SetDecl (TokenDeclSDType decl) + public override void SetDecl(TokenDeclSDType decl) { this.decl = (TokenDeclSDTypeInterface)decl; } - public override string ToString () + public override string ToString() { return decl.longName.val; } - public override Type ToSysType () + public override Type ToSysType() { - return typeof (Delegate[]); + return typeof(Delegate[]); } /** * @brief Assign slots in the gblSDTIntfObjs[] array * Likewise with the sdtcSDTIntfObjs[] array. */ - public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias) + public override void AssignVarSlot(TokenDeclVar declVar, XMRInstArSizes ias) { declVar.vTableArray = iarSDTIntfObjsFieldInfo; - declVar.vTableIndex = ias.iasSDTIntfObjs ++; + declVar.vTableIndex = ias.iasSDTIntfObjs++; } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append (decl.longName); + sb.Append(decl.longName); } } /** * @brief function argument list declaration */ - public class TokenArgDecl : Token + public class TokenArgDecl: Token { - public VarDict varDict = new VarDict (false); + public VarDict varDict = new VarDict(false); - public TokenArgDecl (Token original) : base (original) { } + public TokenArgDecl(Token original) : base(original) { } - public bool AddArg (TokenType type, TokenName name) + public bool AddArg(TokenType type, TokenName name) { - TokenDeclVar var = new TokenDeclVar (name, null, null); + TokenDeclVar var = new TokenDeclVar(name, null, null); var.name = name; var.type = type; var.vTableIndex = varDict.Count; - return varDict.AddEntry (var); + return varDict.AddEntry(var); } /** * @brief Get an array of the argument types. */ private TokenType[] _types; - public TokenType[] types { - get { - if (_types == null) { + public TokenType[] types + { + get + { + if(_types == null) + { _types = new TokenType[varDict.Count]; - foreach (TokenDeclVar var in varDict) { + foreach(TokenDeclVar var in varDict) + { _types[var.vTableIndex] = var.type; } } @@ -5381,11 +6030,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @brief Access the arguments as an array of variables. */ private TokenDeclVar[] _vars; - public TokenDeclVar[] vars { - get { - if (_vars == null) { + public TokenDeclVar[] vars + { + get + { + if(_vars == null) + { _vars = new TokenDeclVar[varDict.Count]; - foreach (TokenDeclVar var in varDict) { + foreach(TokenDeclVar var in varDict) + { _vars[var.vTableIndex] = var; } } @@ -5397,10 +6050,11 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @brief Get argument signature string, eg, "(list,vector,integer)" */ private string argSig = null; - public string GetArgSig () + public string GetArgSig() { - if (argSig == null) { - argSig = ScriptCodeGen.ArgSigString (types); + if(argSig == null) + { + argSig = ScriptCodeGen.ArgSigString(types); } return argSig; } @@ -5409,22 +6063,26 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief encapsulate a state declaration in a single token */ - public class TokenDeclState : Token { + public class TokenDeclState: Token + { public TokenName name; // null for default state public TokenStateBody body; - public TokenDeclState (Token original) : base (original) { } + public TokenDeclState(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - if (name == null) { - sb.Append ("default"); - } else { - sb.Append ("state "); - sb.Append (name); + if(name == null) + { + sb.Append("default"); } - body.DebString (sb); + else + { + sb.Append("state "); + sb.Append(name); + } + body.DebString(sb); } } @@ -5432,8 +6090,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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 + 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 @@ -5446,7 +6105,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { analyzing // triviality is being analyzed (used to detect recursive loops) }; - public class TokenDeclVar : TokenStmt { + 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 @@ -5474,18 +6134,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { 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 labels = new Dictionary (); - // all labels defined in the function - public LinkedList localVars = new LinkedList (); - // all local variables declared by this function - // - doesn't include argument variables + public Dictionary labels = new Dictionary(); + // all labels defined in the function + public LinkedList localVars = new LinkedList(); + // 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 unknownTrivialityCalls = new LinkedList (); - // reduction puts all calls here - // compilation sorts it all out + // vars: unknown (not used for any thing); funcs: unknown/trivial/complex + public LinkedList unknownTrivialityCalls = new LinkedList(); + // reduction puts all calls here + // compilation sorts it all out public ScriptObjWriter ilGen; // codegen stores emitted code here @@ -5496,10 +6156,11 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @param func = null: global variable * else: local to the given function */ - public TokenDeclVar (Token original, TokenDeclVar func, TokenScript ts) : base (original) + public TokenDeclVar(Token original, TokenDeclVar func, TokenScript ts) : base(original) { - if (func != null) { - func.localVars.AddLast (this); + if(func != null) + { + func.localVars.AddLast(this); } tokenScript = ts; } @@ -5510,14 +6171,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * For funcs, this is the delegate type */ private TokenType _type; - public TokenType type { - get { - if (_type == null) { - GetDelType (); + public TokenType type + { + get + { + if(_type == null) + { + GetDelType(); } return _type; } - set { + set + { _type = value; } } @@ -5527,14 +6192,19 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * () missing for fields/variables * . missing for top-level functions/variables */ - public string fullName { - get { - if (sdtClass == null) { - if (retType == null) return name.val; + 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; + if(retType == null) + return ln + "." + name.val; return ln + "." + funcNameSig.val; } } @@ -5545,12 +6215,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * as in 'someDelegate = SomeFunction;', not calling it as such. * The triviality of actually calling the function is IsFuncTrivial(). */ - public bool IsVarTrivial (ScriptCodeGen scg) + 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; + 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 @@ -5562,11 +6234,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { \***************************/ 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 ()); + public TokenName funcNameSig + { + get + { + if(_funcNameSig == null) + { + if(argDecl == null) + return null; + _funcNameSig = new TokenName(name, name.val + argDecl.GetArgSig()); } return _funcNameSig; } @@ -5575,7 +6251,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief The bare function name, ie, without any signature info */ - public string GetSimpleName () + public string GetSimpleName() { return name.val; } @@ -5586,13 +6262,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * bare function name and argument signature, * eg, "MyClass.PrintStuff(string)" */ - public string GetObjCodeName () + public string GetObjCodeName() { - string objCodeName = ""; - if (sdtClass != null) { - objCodeName += sdtClass.longName.val + "."; - } - objCodeName += funcNameSig.val; + string objCodeName = ""; + if(sdtClass != null) + { + objCodeName += sdtClass.longName.val + "."; + } + objCodeName += funcNameSig.val; return objCodeName; } @@ -5602,19 +6279,25 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * It includes return type and all script-visible argument types. * @returns null for vars; else delegate type for funcs */ - public TokenTypeSDTypeDelegate GetDelType () + public TokenTypeSDTypeDelegate GetDelType() { - if (argDecl == null) return null; - if (_type == null) { - if (tokenScript == null) { + 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 { + _type = new TokenTypeSDTypeDelegate(retType, argDecl.types); + } + else + { // used for normal script processing - _type = new TokenTypeSDTypeDelegate (this, retType, argDecl.types, tokenScript); + _type = new TokenTypeSDTypeDelegate(this, retType, argDecl.types, tokenScript); } } - if (!(_type is TokenTypeSDTypeDelegate)) return null; + if(!(_type is TokenTypeSDTypeDelegate)) + return null; return (TokenTypeSDTypeDelegate)_type; } @@ -5624,28 +6307,31 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * If it calls anything that is not trivial, it is not trivial. * Otherwise it is trivial. */ - public bool IsFuncTrivial (ScriptCodeGen scg) + 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; + 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) { + if((sdtFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE | + ScriptReduce.SDT_VIRTUAL)) != 0) + { return false; } /* * Check the triviality status of the function. */ - switch (triviality) { + switch(triviality) + { /* * Don't yet know if it is trivial. @@ -5653,85 +6339,100 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * But if it calls something that has loops, it isn't trivial. * Otherwise it is trivial. */ - case Triviality.unknown: { + 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; + /* + * 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; + /* + * 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; - } + /* + * 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; - } + 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; + default: + return false; } } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - DebStringSDTFlags (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); + 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 (' '); + 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); + 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); + sb.Append(')'); + if(body == null) + sb.Append(';'); + else + { + sb.Append(' '); + body.DebString(sb); } } } @@ -5740,12 +6441,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { // - 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) + 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)"); + 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) { + 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). @@ -5756,78 +6460,94 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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); + 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) { + 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); + var = sdt.members.FindExact(ifield.fieldName.val, null); } - if (left is TokenLValName) { + if(left is TokenLValName) + { TokenLValName global = (TokenLValName)left; - var = global.stack.FindExact (global.name.val, null); + var = global.stack.FindExact(global.name.val, null); } - if (left is TokenLValSField) { + 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); + var = decl.members.FindExact(sfield.fieldName.val, null); } - if (var == null) throw new Exception ("unknown var type " + left.GetType ().Name); + 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); + 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); + if((var.init != null) && !(var.init is TokenRValInitDef)) + { + sb.Append(" = "); + var.init.DebString(sb); } /* * End of declaration statement. */ - sb.Append (';'); + sb.Append(';'); } } - private void DebStringSDTFlags (StringBuilder sb) + 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 "); + 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 class TokenIntfImpl: Token + { public TokenTypeSDTypeInterface intfType; public TokenName methName; // simple name, no arg signature - public TokenIntfImpl (TokenTypeSDTypeInterface intfType, TokenName methName) : base (intfType) + public TokenIntfImpl(TokenTypeSDTypeInterface intfType, TokenName methName) : base(intfType) { this.intfType = intfType; this.methName = methName; @@ -5837,149 +6557,168 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @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); + 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 class TokenLValArEle: TokenLVal + { public TokenRVal baseRVal; public TokenRVal subRVal; - public TokenLValArEle (Token original) : base (original) { } + public TokenLValArEle(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { - TokenType baseType = baseRVal.GetRValType (scg, null); + TokenType baseType = baseRVal.GetRValType(scg, null); /* * Maybe referencing element of a fixed-dimension array. */ - if ((baseType is TokenTypeSDTypeClass) && (((TokenTypeSDTypeClass)baseType).decl.arrayOfType != null)) { + 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) { + 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; + TokenDeclVar idxProp = scg.FindSingleMember(sdtDecl.members, new TokenName(this, "$idxprop"), null); + if(idxProp != null) + return idxProp.type; } - if (baseType is TokenTypeSDTypeInterface) { + 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; + 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); + 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); + if((baseType is TokenTypeArray) || (baseType is TokenTypeList)) + { + return new TokenTypeObject(this); } - scg.ErrorMsg (this, "unknown array reference"); - return new TokenTypeVoid (this); + scg.ErrorMsg(this, "unknown array reference"); + return new TokenTypeVoid(this); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - return baseRVal.IsRValTrivial (scg, null) && subRVal.IsRValTrivial (scg, null); + return baseRVal.IsRValTrivial(scg, null) && subRVal.IsRValTrivial(scg, null); } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - baseRVal.DebString (sb); - sb.Append ('['); - subRVal.DebString (sb); - sb.Append (']'); + 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 class TokenLValBaseField: TokenLVal + { public TokenName fieldName; private TokenDeclSDTypeClass thisClass; - public TokenLValBaseField (Token original, TokenName fieldName, TokenDeclSDTypeClass thisClass) : base (original) + public TokenLValBaseField(Token original, TokenName fieldName, TokenDeclSDTypeClass thisClass) : base(original) { this.fieldName = fieldName; this.thisClass = thisClass; } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + 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); + 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) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig); - return (var != null) && var.IsVarTrivial (scg); + TokenDeclVar var = scg.FindThisMember(thisClass.extends, fieldName, argsig); + return (var != null) && var.IsVarTrivial(scg); } - public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsCallTrivial(ScriptCodeGen scg, TokenType[] argsig) { - TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig); - return (var != null) && var.IsFuncTrivial (scg); + 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 class TokenLValIField: TokenLVal + { public TokenRVal baseRVal; public TokenName fieldName; - public TokenLValIField (Token original) : base (original) { } + public TokenLValIField(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + 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; + 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) { + if(baseType is TokenTypeSDTypeInterface) + { TokenDeclSDTypeInterface baseIntfDecl = ((TokenTypeSDTypeInterface)baseType).decl; - TokenDeclVar var = baseIntfDecl.FindIFaceMember (scg, fieldName, argsig, out baseIntfDecl); - if (var != null) return var.type; + 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 TokenTypeArray) + { + return XMR_Array.GetRValType(fieldName); } - if ((baseType is TokenTypeRot) || (baseType is TokenTypeVec)) { - return new TokenTypeFloat (fieldName); + if((baseType is TokenTypeRot) || (baseType is TokenTypeVec)) + { + return new TokenTypeFloat(fieldName); } - scg.ErrorMsg (fieldName, "unknown member of " + baseType.ToString ()); - return new TokenTypeVoid (fieldName); + scg.ErrorMsg(fieldName, "unknown member of " + baseType.ToString()); + return new TokenTypeVoid(fieldName); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + 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; + if(!baseRVal.IsRValTrivial(scg, null)) + return false; /* * Accessing a member of a class depends on the member. @@ -5988,10 +6727,11 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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); + TokenType baseType = baseRVal.GetRValType(scg, null); + if(baseType is TokenTypeSDTypeClass) + { + TokenDeclVar var = scg.FindThisMember((TokenTypeSDTypeClass)baseType, fieldName, argsig); + return (var != null) && var.IsVarTrivial(scg); } /* @@ -6006,20 +6746,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + 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; + 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); + TokenType baseType = baseRVal.GetRValType(scg, null); + if(baseType is TokenTypeSDTypeClass) + { + TokenDeclVar var = scg.FindThisMember((TokenTypeSDTypeClass)baseType, fieldName, argsig); + return (var != null) && var.IsFuncTrivial(scg); } /* @@ -6030,7 +6772,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * ??? 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; + if(baseType is TokenTypeSDTypeInterface) + return false; /* * Calling a method of anything else (arrays, rotations, vectors) is always trivial. @@ -6040,27 +6783,28 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - baseRVal.DebString (sb); - sb.Append ('.'); - sb.Append (fieldName.val); + baseRVal.DebString(sb); + sb.Append('.'); + sb.Append(fieldName.val); } } /** * @brief a name is being used as an L-value */ - public class TokenLValName : TokenLVal { + public class TokenLValName: TokenLVal + { public TokenName name; public VarDict stack; - public TokenLValName (TokenName name, VarDict stack) : base (name) + public TokenLValName(TokenName name, VarDict stack) : base(name) { /* * Save name of variable/method/function/field. */ - this.name = name; + this.name = name; /* * Save where in the stack it can be looked up. @@ -6074,21 +6818,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * If it is not locals, allow forward references. * this allows function X() to call Y() and Y() to call X(). */ - this.stack = stack.FreezeLocals (); + this.stack = stack.FreezeLocals(); } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + 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); + 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) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - TokenDeclVar var = scg.FindNamedVar (this, argsig); - return (var != null) && var.IsVarTrivial (scg); + TokenDeclVar var = scg.FindNamedVar(this, argsig); + return (var != null) && var.IsVarTrivial(scg); } /** @@ -6097,39 +6842,42 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + public override bool IsCallTrivial(ScriptCodeGen scg, TokenType[] argsig) { - TokenDeclVar var = scg.FindNamedVar (this, argsig); - return (var != null) && var.IsFuncTrivial (scg); + TokenDeclVar var = scg.FindNamedVar(this, argsig); + return (var != null) && var.IsFuncTrivial(scg); } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append (name.val); + sb.Append(name.val); } } /** * @brief a static field within a struct is an L-value */ - public class TokenLValSField : TokenLVal { + public class TokenLValSField: TokenLVal + { public TokenType baseType; public TokenName fieldName; - public TokenLValSField (Token original) : base (original) { } + public TokenLValSField(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + 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; + 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); + scg.ErrorMsg(fieldName, "unknown member of " + baseType.ToString()); + return new TokenTypeVoid(fieldName); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { /* * Accessing a member of a class depends on the member. @@ -6138,9 +6886,10 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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); + if(baseType is TokenTypeSDTypeClass) + { + TokenDeclVar var = scg.FindThisMember((TokenTypeSDTypeClass)baseType, fieldName, argsig); + return (var != null) && var.IsVarTrivial(scg); } /* @@ -6155,14 +6904,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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) + 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); + if(baseType is TokenTypeSDTypeClass) + { + TokenDeclVar var = scg.FindThisMember((TokenTypeSDTypeClass)baseType, fieldName, argsig); + return (var != null) && var.IsFuncTrivial(scg); } /* @@ -6171,15 +6921,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { return true; } - public override void DebString (StringBuilder sb) + 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); + if(fieldName.val == "$new") + { + sb.Append("new "); + baseType.DebString(sb); + } + else + { + baseType.DebString(sb); + sb.Append('.'); + fieldName.DebString(sb); } } } @@ -6187,14 +6940,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @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) { } + 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); + public abstract TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig); /** * @brief Tell us if reading and writing the value is trivial. @@ -6205,7 +6959,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * will always be trivial (no looping or CheckRun() calls possible). * false: otherwise */ - public abstract bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig); + public abstract bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig); /** * @brief Tell us if calling the method is trivial. @@ -6226,7 +6980,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * calls possible). * false: otherwise */ - public virtual bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig) + public virtual bool IsCallTrivial(ScriptCodeGen scg, TokenType[] argsig) { return false; } @@ -6236,70 +6990,73 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) + public virtual TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne) { - return lookup (this, ref didOne); + return lookup(this, ref didOne); } } /** * @brief a postfix operator and corresponding L-value */ - public class TokenRValAsnPost : TokenRVal { + public class TokenRValAsnPost: TokenRVal + { public TokenLVal lVal; public Token postfix; - public TokenRValAsnPost (Token original) : base (original) { } + public TokenRValAsnPost(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { - return lVal.GetRValType (scg, argsig); + return lVal.GetRValType(scg, argsig); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - return lVal.IsRValTrivial (scg, null); + return lVal.IsRValTrivial(scg, null); } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - lVal.DebString (sb); - sb.Append (' '); - postfix.DebString (sb); + lVal.DebString(sb); + sb.Append(' '); + postfix.DebString(sb); } } /** * @brief a prefix operator and corresponding L-value */ - public class TokenRValAsnPre : TokenRVal { + public class TokenRValAsnPre: TokenRVal + { public Token prefix; public TokenLVal lVal; - public TokenRValAsnPre (Token original) : base (original) { } + public TokenRValAsnPre(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { - return lVal.GetRValType (scg, argsig); + return lVal.GetRValType(scg, argsig); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - return lVal.IsRValTrivial (scg, null); + return lVal.IsRValTrivial(scg, null); } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - prefix.DebString (sb); - sb.Append (' '); - lVal.DebString (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 class TokenRValCall: TokenRVal + { public TokenRVal meth; // points to the function to be called // - might be a reference to a global function (TokenLValName) @@ -6309,39 +7066,42 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { public TokenRVal args; // null-terminated TokenRVal list public int nArgs; // number of elements in args - public TokenRValCall (Token original) : base (original) { } + 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) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { /* * Build type signature so we select correct overloaded function. */ - if (myArgSig == null) { + 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); + 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); + 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 (); + return ((TokenTypeSDTypeDelegate)delType).decl.GetRetType(); } /** @@ -6350,132 +7110,170 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { /* * Build type signature so we select correct overloaded function. */ - if (myArgSig == null) { + 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); + 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; + 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); + return meth.IsCallTrivial(scg, myArgSig); } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - meth.DebString (sb); - sb.Append (" ("); + meth.DebString(sb); + sb.Append(" ("); bool first = true; - for (Token t = args; t != null; t = t.nextToken) { - if (!first) sb.Append (", "); - t.DebString (sb); + for(Token t = args; t != null; t = t.nextToken) + { + if(!first) + sb.Append(", "); + t.DebString(sb); first = false; } - sb.Append (")"); + sb.Append(")"); } } /** * @brief encapsulates a typecast, ie, (type) */ - public class TokenRValCast : TokenRVal { + public class TokenRValCast: TokenRVal + { public TokenType castTo; public TokenRVal rVal; - public TokenRValCast (TokenType type, TokenRVal value) : base (type) + public TokenRValCast(TokenType type, TokenRVal value) : base(type) { castTo = type; - rVal = value; + rVal = value; } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { return castTo; } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { argsig = null; - if (castTo is TokenTypeSDTypeDelegate) { - argsig = ((TokenTypeSDTypeDelegate)castTo).decl.GetArgTypes (); + if(castTo is TokenTypeSDTypeDelegate) + { + argsig = ((TokenTypeSDTypeDelegate)castTo).decl.GetArgTypes(); } - return rVal.IsRValTrivial (scg, argsig); + 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) + public override TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne) { - rVal = rVal.TryComputeConstant (lookup, ref didOne); - if (rVal is TokenRValConst) { - try { + 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 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 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 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 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 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(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); + if(nval != null) + { + TokenRVal rValConst = new TokenRValConst(castTo, nval); didOne = true; return rValConst; } - } catch { + } + catch + { } } return this; } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ('('); - castTo.DebString (sb); - sb.Append (')'); - rVal.DebString (sb); + sb.Append('('); + castTo.DebString(sb); + sb.Append(')'); + rVal.DebString(sb); } } @@ -6483,185 +7281,249 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @brief Encapsulate a conditional expression: * ? : */ - public class TokenRValCondExpr : TokenRVal { + 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) + public TokenRValCondExpr(Token original) : base(original) { - 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"); + } + + 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) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - return condExpr.IsRValTrivial (scg, null) && - trueExpr.IsRValTrivial (scg, argsig) && - falseExpr.IsRValTrivial (scg, 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) + public override TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne) { - TokenRVal rValCond = condExpr.TryComputeConstant (lookup, ref didOne); - if (rValCond is TokenRValConst) { + 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); + bool isTrue = ((TokenRValConst)rValCond).IsConstBoolTrue(); + return (isTrue ? trueExpr : falseExpr).TryComputeConstant(lookup, ref didOne); } return this; } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - condExpr.DebString (sb); - sb.Append (" ? "); - trueExpr.DebString (sb); - sb.Append (" : "); - falseExpr.DebString (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 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) + public TokenRValConst(Token original, object value) : base(original) { val = value; TokenType tt = null; - if (val is char) { + if(val is char) + { type = TokenRValConstType.CHAR; - tt = new TokenTypeChar (this); - } else if (val is int) { + tt = new TokenTypeChar(this); + } + else if(val is int) + { type = TokenRValConstType.INT; - tt = new TokenTypeInt (this); - } else if (val is double) { + tt = new TokenTypeInt(this); + } + else if(val is double) + { type = TokenRValConstType.FLOAT; - tt = new TokenTypeFloat (this); - } else if (val is string) { + 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 ()); + tt = new TokenTypeStr(this); + } + else + { + throw new Exception("invalid constant type " + val.GetType()); } tokType = (original is TokenType) ? (TokenType)original : tt; - if (tokType is TokenTypeKey) { + if(tokType is TokenTypeKey) + { type = TokenRValConstType.KEY; } } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { return tokType; } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { return true; } - public CompValu GetCompValu () + 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); } + 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"); + case TokenRValConstType.STRING: + { + return new CompValuString(tokType, (string)val); + } + default: + throw new Exception("unknown type"); } } - public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne) + public override TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne) { // gotta end somewhere return this; } - public bool IsConstBoolTrue () + 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"); + 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) + 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"); + 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 ()); + } + 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) + 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; - } + 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; + } } } } @@ -6670,209 +7532,231 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief Default initialization value for the corresponding variable. */ - public class TokenRValInitDef : TokenRVal { + public class TokenRValInitDef: TokenRVal + { public TokenType type; - public static TokenRValInitDef Construct (TokenDeclVar tokenDeclVar) + public static TokenRValInitDef Construct(TokenDeclVar tokenDeclVar) { - TokenRValInitDef zhis = new TokenRValInitDef (tokenDeclVar); + TokenRValInitDef zhis = new TokenRValInitDef(tokenDeclVar); zhis.type = tokenDeclVar.type; return zhis; } - private TokenRValInitDef (Token original) : base (original) { } + private TokenRValInitDef(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { return type; } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + 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) + public override void DebString(StringBuilder sb) { - sb.Append ("'); + sb.Append("'); } } /** * @brief encapsulation of is */ - public class TokenRValIsType : TokenRVal { - public TokenRVal rValExp; + public class TokenRValIsType: TokenRVal + { + public TokenRVal rValExp; public TokenTypeExp typeExp; - public TokenRValIsType (Token original) : base (original) { } + public TokenRValIsType(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { - return new TokenTypeBool (rValExp); + return new TokenTypeBool(rValExp); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - return rValExp.IsRValTrivial (scg, argsig); + return rValExp.IsRValTrivial(scg, argsig); } } /** * @brief an R-value enclosed in brackets is an LSLList */ - public class TokenRValList : TokenRVal { + public class TokenRValList: TokenRVal + { public TokenRVal rVal; // null-terminated list of TokenRVal objects public int nItems; - public TokenRValList (Token original) : base (original) { } + public TokenRValList(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { - return new TokenTypeList (rVal); + return new TokenTypeList(rVal); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + 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; + 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) + 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); + sb.Append('['); + for(Token t = rVal; t != null; t = t.nextToken) + { + if(!first) + sb.Append(','); + sb.Append(' '); + t.DebString(sb); first = false; } - sb.Append (" ]"); + sb.Append(" ]"); } } /** * @brief encapsulates '$new' arraytype '{' ... '}' */ - public class TokenRValNewArIni : TokenRVal { + 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) + public TokenRValNewArIni(Token original) : base(original) { - valueList = new TokenList (original); + valueList = new TokenList(original); } // type of the expression = the array type allocated by $new() - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + 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) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - return ListIsTrivial (scg, valueList); + return ListIsTrivial(scg, valueList); } - private bool ListIsTrivial (ScriptCodeGen scg, TokenList valList) + 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; + 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; + if(val is TokenList) + { + if(!ListIsTrivial(scg, (TokenList)val)) + return false; } } return true; } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("new "); - arrayType.DebString (sb); - sb.Append (' '); - valueList.DebString (sb); + sb.Append("new "); + arrayType.DebString(sb); + sb.Append(' '); + valueList.DebString(sb); } } - public class TokenList : Token { - public List tl = new List (); - public TokenList (Token original) : base (original) { } + public class TokenList: Token + { + public List tl = new List(); + public TokenList(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ('{'); + sb.Append('{'); bool first = true; - foreach (Token t in tl) { - if (!first) sb.Append (", "); - t.DebString (sb); + foreach(Token t in tl) + { + if(!first) + sb.Append(", "); + t.DebString(sb); first = false; } - sb.Append ('}'); + sb.Append('}'); } } /** * @brief a binary operator and its two operands */ - public class TokenRValOpBin : TokenRVal { + public class TokenRValOpBin: TokenRVal + { public TokenRVal rValLeft; public TokenKw opcode; public TokenRVal rValRight; - public TokenRValOpBin (TokenRVal left, TokenKw op, TokenRVal right) : base (op) + public TokenRValOpBin(TokenRVal left, TokenKw op, TokenRVal right) : base(op) { - rValLeft = left; - opcode = op; + rValLeft = left; + opcode = op; rValRight = right; } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + 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); + 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); + 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); + 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); + 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); + if((opstr == "+") && ((lType == "key") || (lType == "string") || + (rType == "key") || (rType == "string"))) + { + return new TokenTypeStr(opcode); } /* @@ -6880,87 +7764,98 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { */ string key = lType + opstr + rType; BinOpStr binOpStr; - if (BinOpStr.defined.TryGetValue (key, out binOpStr)) { - return TokenType.FromSysType (opcode, binOpStr.outtype); + if(BinOpStr.defined.TryGetValue(key, out binOpStr)) + { + return TokenType.FromSysType(opcode, binOpStr.outtype); } - scg.ErrorMsg (opcode, "undefined operation " + key); - return new TokenTypeVoid (opcode); + scg.ErrorMsg(opcode, "undefined operation " + key); + return new TokenTypeVoid(opcode); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - return rValLeft.IsRValTrivial (scg, null) && rValRight.IsRValTrivial (scg, null); + 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) + 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, + 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); + TokenRVal rValConst = new TokenRValConst(opcode, val); didOne = true; return rValConst; - } catch { + } + catch + { } } return this; } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - rValLeft.DebString (sb); - sb.Append (' '); - sb.Append (opcode.ToString ()); - sb.Append (' '); - rValRight.DebString (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 class TokenRValOpUn: TokenRVal + { public TokenKw opcode; public TokenRVal rVal; - public TokenRValOpUn (TokenKw op, TokenRVal right) : base (op) + public TokenRValOpUn(TokenKw op, TokenRVal right) : base(op) { opcode = op; - rVal = right; + rVal = right; } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { - if (opcode is TokenKwExclam) return new TokenTypeInt (opcode); - return rVal.GetRValType (scg, null); + if(opcode is TokenKwExclam) + return new TokenTypeInt(opcode); + return rVal.GetRValType(scg, null); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - return rVal.IsRValTrivial (scg, null); + 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) + 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); + 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 { + } + catch + { } } return this; @@ -6969,123 +7864,127 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief Serialization/Deserialization. */ - public TokenRValOpUn (Token original) : base (original) { } + public TokenRValOpUn(Token original) : base(original) { } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append (opcode.ToString ()); - rVal.DebString (sb); + sb.Append(opcode.ToString()); + rVal.DebString(sb); } } /** * @brief an R-value enclosed in parentheses */ - public class TokenRValParen : TokenRVal { + public class TokenRValParen: TokenRVal + { public TokenRVal rVal; - public TokenRValParen (Token original) : base (original) { } + public TokenRValParen(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + 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); + return rVal.GetRValType(scg, argsig); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] 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); + 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) + public override TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne) { - rVal = rVal.TryComputeConstant (lookup, ref didOne); - if (rVal is TokenRValConst) { + rVal = rVal.TryComputeConstant(lookup, ref didOne); + if(rVal is TokenRValConst) + { didOne = true; return rVal; } return this; } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ('('); - rVal.DebString (sb); - sb.Append (')'); + sb.Append('('); + rVal.DebString(sb); + sb.Append(')'); } } - public class TokenRValRot : TokenRVal { + public class TokenRValRot: TokenRVal + { public TokenRVal xRVal; public TokenRVal yRVal; public TokenRVal zRVal; public TokenRVal wRVal; - public TokenRValRot (Token original) : base (original) { } + public TokenRValRot(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { - return new TokenTypeRot (xRVal); + return new TokenTypeRot(xRVal); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + 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); + return xRVal.IsRValTrivial(scg, null) && + yRVal.IsRValTrivial(scg, null) && + zRVal.IsRValTrivial(scg, null) && + wRVal.IsRValTrivial(scg, null); } - public override void DebString (StringBuilder sb) + 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 ('>'); + 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 class TokenRValThis: TokenRVal + { public Token original; public TokenDeclSDTypeClass sdtClass; - public TokenRValThis (Token original, TokenDeclSDTypeClass sdtClass) : base (original) + public TokenRValThis(Token original, TokenDeclSDTypeClass sdtClass) : base(original) { this.original = original; this.sdtClass = sdtClass; } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { - return sdtClass.MakeRefToken (original); + return sdtClass.MakeRefToken(original); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + 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) + public override void DebString(StringBuilder sb) { - sb.Append ("this"); + sb.Append("this"); } } @@ -7093,145 +7992,153 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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 { + public class TokenRValUndef: TokenRVal + { Token original; - public TokenRValUndef (Token original) : base (original) + public TokenRValUndef(Token original) : base(original) { this.original = original; } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { - return new TokenTypeUndef (original); + return new TokenTypeUndef(original); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { return true; } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("undef"); + sb.Append("undef"); } } /** * @brief put 3 RVals together as a Vector value. */ - public class TokenRValVec : TokenRVal { + public class TokenRValVec: TokenRVal + { public TokenRVal xRVal; public TokenRVal yRVal; public TokenRVal zRVal; - public TokenRValVec (Token original) : base (original) { } + public TokenRValVec(Token original) : base(original) { } - public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig) + public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig) { - return new TokenTypeVec (xRVal); + return new TokenTypeVec(xRVal); } - public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig) + public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig) { - return xRVal.IsRValTrivial (scg, null) && - yRVal.IsRValTrivial (scg, null) && - zRVal.IsRValTrivial (scg, null); + return xRVal.IsRValTrivial(scg, null) && + yRVal.IsRValTrivial(scg, null) && + zRVal.IsRValTrivial(scg, null); } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ('<'); - xRVal.DebString (sb); - sb.Append (','); - yRVal.DebString (sb); - sb.Append (','); - zRVal.DebString (sb); - sb.Append ('>'); + 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 class TokenScript: Token + { public int expiryDays = Int32.MaxValue; public TokenDeclState defaultState; - public Dictionary states = new Dictionary (); - public VarDict variablesStack = new VarDict (false); // initial one is used for global functions and variables + public Dictionary states = new Dictionary(); + 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 sdSrcTypes = new Dictionary (); + private Dictionary sdSrcTypes = new Dictionary(); private bool sdSrcTypesSealed = false; - public TokenScript (Token original) : base (original) { } + 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) + public void PushVarFrame(bool locals) { - PushVarFrame (new VarDict (locals)); + PushVarFrame(new VarDict(locals)); } - public void PushVarFrame (VarDict newFrame) + public void PushVarFrame(VarDict newFrame) { newFrame.outerVarDict = variablesStack; variablesStack = newFrame; } - public void PopVarFrame () + public void PopVarFrame() { variablesStack = variablesStack.outerVarDict; } - public bool AddVarEntry (TokenDeclVar var) + public bool AddVarEntry(TokenDeclVar var) { - return variablesStack.AddEntry (var); + return variablesStack.AddEntry(var); } /* * Handle list of script-defined types. */ - public void sdSrcTypesSeal () + public void sdSrcTypesSeal() { sdSrcTypesSealed = true; } - public bool sdSrcTypesContainsKey (string key) + public bool sdSrcTypesContainsKey(string key) { - return sdSrcTypes.ContainsKey (key); + return sdSrcTypes.ContainsKey(key); } - public bool sdSrcTypesTryGetValue (string key, out TokenDeclSDType value) + public bool sdSrcTypesTryGetValue(string key, out TokenDeclSDType value) { - return sdSrcTypes.TryGetValue (key, out value); + return sdSrcTypes.TryGetValue(key, out value); } - public void sdSrcTypesAdd (string key, TokenDeclSDType value) + public void sdSrcTypesAdd(string key, TokenDeclSDType value) { - if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed"); + if(sdSrcTypesSealed) + throw new Exception("sdSrcTypes is sealed"); value.sdTypeIndex = sdSrcTypes.Count; - sdSrcTypes.Add (key, value); + sdSrcTypes.Add(key, value); } - public void sdSrcTypesRep (string key, TokenDeclSDType value) + public void sdSrcTypesRep(string key, TokenDeclSDType value) { - if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed"); + if(sdSrcTypesSealed) + throw new Exception("sdSrcTypes is sealed"); value.sdTypeIndex = sdSrcTypes[key].sdTypeIndex; sdSrcTypes[key] = value; } - public void sdSrcTypesReplace (string key, TokenDeclSDType value) + public void sdSrcTypesReplace(string key, TokenDeclSDType value) { - if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed"); + if(sdSrcTypesSealed) + throw new Exception("sdSrcTypes is sealed"); sdSrcTypes[key] = value; } public Dictionary.ValueCollection sdSrcTypesValues { - get { + get + { return sdSrcTypes.Values; } } public int sdSrcTypesCount { - get { + get + { return sdSrcTypes.Count; } } @@ -7239,42 +8146,50 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief Debug output. */ - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { /* * Script-defined types. */ - foreach (TokenDeclSDType srcType in sdSrcTypes.Values) { - srcType.DebString (sb); + 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); + 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); + 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); + defaultState.DebString(sb); + foreach(TokenDeclState st in states.Values) + { + st.DebString(sb); } } } @@ -7282,21 +8197,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { /** * @brief state body declaration */ - public class TokenStateBody : Token { + public class TokenStateBody: Token + { public TokenDeclVar eventFuncs; public int index = -1; // (codegen) row in ScriptHandlerEventTable (0=default) - public TokenStateBody (Token original) : base (original) { } + public TokenStateBody(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append (" { "); - for (Token t = eventFuncs; t != null; t = t.nextToken) { - t.DebString (sb); + sb.Append(" { "); + for(Token t = eventFuncs; t != null; t = t.nextToken) + { + t.DebString(sb); } - sb.Append (" } "); + sb.Append(" } "); } } @@ -7307,14 +8224,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * 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) { } + public class TokenStmt: Token + { + public TokenStmt(Token original) : base(original) { } } /** * @brief a group of statements enclosed in braces */ - public class TokenStmtBlock : TokenStmt { + 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 @@ -7324,23 +8243,25 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { 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) { } + public TokenStmtBlock(Token original) : base(original) { } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("{ "); - for (Token stmt = statements; stmt != null; stmt = stmt.nextToken) { - stmt.DebString (sb); + sb.Append("{ "); + for(Token stmt = statements; stmt != null; stmt = stmt.nextToken) + { + stmt.DebString(sb); } - sb.Append ("} "); + sb.Append("} "); } } /** * @brief definition of branch target name */ - public class TokenStmtLabel : TokenStmt { + public class TokenStmtLabel: TokenStmt + { public TokenName name; // the label's name public TokenStmtBlock block; // which block it is defined in @@ -7349,13 +8270,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { public bool labelTagged; // code gen: location of label public ScriptMyLabel labelStruct; - public TokenStmtLabel (Token original) : base (original) { } + public TokenStmtLabel(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ('@'); - name.DebString (sb); - sb.Append (';'); + sb.Append('@'); + name.DebString(sb); + sb.Append(';'); } } @@ -7363,206 +8284,227 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @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 class TokenStmtRVal: TokenStmt + { public TokenRVal rVal; - public TokenStmtRVal (Token original) : base (original) { } + public TokenStmtRVal(Token original) : base(original) { } // debugging - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - rVal.DebString (sb); - sb.Append ("; "); + rVal.DebString(sb); + sb.Append("; "); } } - public class TokenStmtBreak : TokenStmt { - public TokenStmtBreak (Token original) : base (original) { } + public class TokenStmtBreak: TokenStmt + { + public TokenStmtBreak(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("break;"); + sb.Append("break;"); } } - public class TokenStmtCont : TokenStmt { - public TokenStmtCont (Token original) : base (original) { } + public class TokenStmtCont: TokenStmt + { + public TokenStmtCont(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("continue;"); + sb.Append("continue;"); } } /** * @brief "do" statement */ - public class TokenStmtDo : TokenStmt { + public class TokenStmtDo: TokenStmt + { public TokenStmt bodyStmt; public TokenRValParen testRVal; - public TokenStmtDo (Token original) : base (original) { } + public TokenStmtDo(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("do "); - bodyStmt.DebString (sb); - sb.Append (" while "); - testRVal.DebString (sb); - sb.Append (';'); + sb.Append("do "); + bodyStmt.DebString(sb); + sb.Append(" while "); + testRVal.DebString(sb); + sb.Append(';'); } } /** * @brief "for" statement */ - public class TokenStmtFor : TokenStmt { + 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 TokenStmtFor(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + 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); + 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 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 TokenStmtForEach(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + 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); + 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 class TokenStmtIf: TokenStmt + { public TokenRValParen testRVal; public TokenStmt trueStmt; public TokenStmt elseStmt; - public TokenStmtIf (Token original) : base (original) { } + 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 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 class TokenStmtJump: TokenStmt + { public TokenName label; - public TokenStmtJump (Token original) : base (original) { } + public TokenStmtJump(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("jump "); - label.DebString (sb); - sb.Append (';'); + sb.Append("jump "); + label.DebString(sb); + sb.Append(';'); } } - public class TokenStmtNull : TokenStmt { + public class TokenStmtNull: TokenStmt + { - public TokenStmtNull (Token original) : base (original) { } + public TokenStmtNull(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append (';'); + sb.Append(';'); } } - public class TokenStmtRet : TokenStmt { + public class TokenStmtRet: TokenStmt + { public TokenRVal rVal; // null if void - public TokenStmtRet (Token original) : base (original) { } + public TokenStmtRet(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("return"); - if (rVal != null) { - sb.Append (' '); - rVal.DebString (sb); + sb.Append("return"); + if(rVal != null) + { + sb.Append(' '); + rVal.DebString(sb); } - sb.Append (';'); + sb.Append(';'); } } /** * @brief statement that changes the current state. */ - public class TokenStmtState : TokenStmt { + public class TokenStmtState: TokenStmt + { public TokenName state; // null for default - public TokenStmtState (Token original) : base (original) { } + public TokenStmtState(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("state "); - sb.Append ((state == null) ? "default" : state.val); - sb.Append (';'); + 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 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 TokenStmtSwitch(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + 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("switch "); + testRVal.DebString(sb); + sb.Append('{'); + for(TokenSwitchCase kase = cases; kase != null; kase = kase.nextCase) + { + kase.DebString(sb); } - sb.Append ('}'); + sb.Append('}'); } } @@ -7570,7 +8512,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { * @brief Encapsulates a case/default clause from a switch statement including the * two values and the corresponding body statements. */ - public class TokenSwitchCase : Token { + 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:' @@ -7587,133 +8530,151 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { public TokenSwitchCase lowerCase; public TokenSwitchCase higherCase; - public TokenSwitchCase (Token original) : base (original) { } + public TokenSwitchCase(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + 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 (": "); + if(rVal1 == null) + { + sb.Append("default: "); } - for (Token t = stmts; t != null; t = t.nextToken) { - t.DebString (sb); + 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 class TokenStmtThrow: TokenStmt + { public TokenRVal rVal; // null if rethrow style - public TokenStmtThrow (Token original) : base (original) { } + public TokenStmtThrow(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("throw "); - rVal.DebString (sb); - sb.Append (';'); + sb.Append("throw "); + rVal.DebString(sb); + sb.Append(';'); } } /** * @brief Encapsulates related try, catch and finally statements. */ - public class TokenStmtTry : TokenStmt { + 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 iLeaves = new Dictionary (); + public Dictionary iLeaves = new Dictionary(); - public TokenStmtTry (Token original) : base (original) { } + public TokenStmtTry(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + 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); + 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); + if(finallyStmt != null) + { + sb.Append("finally "); + finallyStmt.DebString(sb); } } } - public class IntermediateLeave { + public class IntermediateLeave + { public ScriptMyLabel jumpIntoLabel; public ScriptMyLabel jumpAwayLabel; } - public class TokenStmtVarIniDef : TokenStmt { + public class TokenStmtVarIniDef: TokenStmt + { public TokenLVal var; - public TokenStmtVarIniDef (Token original) : base (original) { } + public TokenStmtVarIniDef(Token original) : base(original) { } } - public class TokenStmtWhile : TokenStmt { + public class TokenStmtWhile: TokenStmt + { public TokenRValParen testRVal; public TokenStmt bodyStmt; - public TokenStmtWhile (Token original) : base (original) { } + public TokenStmtWhile(Token original) : base(original) { } - public override void DebString (StringBuilder sb) + public override void DebString(StringBuilder sb) { - sb.Append ("while "); - testRVal.DebString (sb); - sb.Append (' '); - bodyStmt.DebString (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 TokenTypeExp: Token + { + public TokenTypeExp(Token original) : base(original) { } } - public class TokenTypeExpBinOp : TokenTypeExp { + public class TokenTypeExpBinOp: TokenTypeExp + { public TokenTypeExp leftOp; - public Token binOp; + public Token binOp; public TokenTypeExp rightOp; - public TokenTypeExpBinOp (Token original) : base (original) { } + public TokenTypeExpBinOp(Token original) : base(original) { } } - public class TokenTypeExpNot : TokenTypeExp { + public class TokenTypeExpNot: TokenTypeExp + { public TokenTypeExp typeExp; - public TokenTypeExpNot (Token original) : base (original) { } + public TokenTypeExpNot(Token original) : base(original) { } } - public class TokenTypeExpPar : TokenTypeExp { + public class TokenTypeExpPar: TokenTypeExp + { public TokenTypeExp typeExp; - public TokenTypeExpPar (Token original) : base (original) { } + public TokenTypeExpPar(Token original) : base(original) { } } - public class TokenTypeExpType : TokenTypeExp { + public class TokenTypeExpType: TokenTypeExp + { public TokenType typeToken; - public TokenTypeExpType (Token original) : base (original) { } + public TokenTypeExpType(Token original) : base(original) { } } - public class TokenTypeExpUndef : TokenTypeExp { - public TokenTypeExpUndef (Token original) : base (original) { } + public class TokenTypeExpUndef: TokenTypeExp + { + public TokenTypeExpUndef(Token original) : base(original) { } } } diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptTokenize.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptTokenize.cs new file mode 100644 index 0000000000..1bcb5b6935 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptTokenize.cs @@ -0,0 +1,2972 @@ +/* + * 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.Yengine +{ + + 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 + { + 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 == '.') && (i + 1 < source.Length) && (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, "yoption") == 0)) + { + string opt = ((TokenName)lastToken).val; + if(strcasecmp(opt, "allowall") == 0) + { + options.arrays = true; + options.advFlowCtl = true; + options.tryCatch = true; + options.objects = true; + options.chars = true; + // options.noRightToLeft = true; + options.dollarsigns = true; + } + else 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 YOption"); + + lastToken = lastToken.prevToken.prevToken; + lastToken.nextToken = null; + 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 keywords = BuildKeywords(); + private static Dictionary arrayKeywords = BuildArrayKeywords(); + private static Dictionary advFlowCtlKeywords = BuildAdvFlowCtlKeywords(); + private static Dictionary tryCatchKeywords = BuildTryCatchKeywords(); + private static Dictionary objectsKeywords = BuildObjectsKeywords(); + private static Dictionary charsKeywords = BuildCharsKeywords(); + + private static Dictionary BuildKeywords() + { + Dictionary kws = new Dictionary(); + + 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 BuildArrayKeywords() + { + Dictionary kws = new Dictionary(); + + 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 BuildAdvFlowCtlKeywords() + { + Dictionary kws = new Dictionary(); + + 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 BuildTryCatchKeywords() + { + Dictionary kws = new Dictionary(); + + 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 BuildObjectsKeywords() + { + Dictionary kws = new Dictionary(); + + 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 BuildCharsKeywords() + { + Dictionary kws = new Dictionary(); + + kws.Add("char", GetTokenCtor(typeof(TokenTypeChar))); + + return kws; + } + } + + /** + * @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. + * null indicates there is no heap tracker for the type. + */ + public virtual Type ToHeapTrackerType() + { + return null; + } + public virtual ConstructorInfo GetHeapTrackerCtor() + { + throw new ApplicationException("no GetHeapTrackerCtor for " + this.GetType()); + } + public virtual void CallHeapTrackerPopMeth(Token errorAt, ScriptMyILGen ilGen) + { + throw new ApplicationException("no CallHeapTrackerPopMeth for " + this.GetType()); + } + public virtual void CallHeapTrackerPushMeth(Token errorAt, ScriptMyILGen ilGen) + { + throw new ApplicationException("no CallHeapTrackerPushMeth for " + this.GetType()); + } + } + + 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) }); + + 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 void CallHeapTrackerPopMeth(Token errorAt, ScriptMyILGen ilGen) + { + HeapTrackerList.GenPop(errorAt, ilGen); + } + public override void CallHeapTrackerPushMeth(Token errorAt, ScriptMyILGen ilGen) + { + HeapTrackerList.GenPush(errorAt, ilGen); + } + } + 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) }); + + 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 void CallHeapTrackerPopMeth(Token errorAt, ScriptMyILGen ilGen) + { + HeapTrackerObject.GenPop(errorAt, ilGen); + } + public override void CallHeapTrackerPushMeth(Token errorAt, ScriptMyILGen ilGen) + { + HeapTrackerObject.GenPush(errorAt, ilGen); + } + } + 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) }); + + 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 void CallHeapTrackerPopMeth(Token errorAt, ScriptMyILGen ilGen) + { + HeapTrackerString.GenPop(errorAt, ilGen); + } + public override void CallHeapTrackerPushMeth(Token errorAt, ScriptMyILGen ilGen) + { + HeapTrackerString.GenPush(errorAt, ilGen); + } + } + 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); + } + } +} diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptTypeCast.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptTypeCast.cs new file mode 100644 index 0000000000..8761e7acae --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptTypeCast.cs @@ -0,0 +1,1012 @@ +/* + * 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.Yengine; +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.Yengine +{ + + 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 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 for IMPLICIT casting. + * Key is of the form * for EXPLICIT casting. + * Value is a delegate that generates code to perform the type cast. + */ + private static Dictionary CreateLegalTypeCasts() + { + Dictionary ltc = new Dictionary(); + + // 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; + } + } +} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptVarDict.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptVarDict.cs similarity index 61% rename from OpenSim/Region/ScriptEngine/XMREngine/MMRScriptVarDict.cs rename to OpenSim/Region/ScriptEngine/YEngine/MMRScriptVarDict.cs index 67e1c34449..2561d0290b 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptVarDict.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptVarDict.cs @@ -33,63 +33,80 @@ using System.Collections.Generic; * @brief Collection of variable/function/method definitions */ -namespace OpenSim.Region.ScriptEngine.XMREngine +namespace OpenSim.Region.ScriptEngine.Yengine { - public class VarDict : IEnumerable { + 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 { + private struct ArgTypes + { public TokenType[] argTypes; - public bool CanBeCalledBy (TokenType[] calledBy) + 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; + 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) + public override bool Equals(Object that) { - if (that == null) return false; - if (that.GetType () != typeof (ArgTypes)) return false; + 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; + 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 () + public override int GetHashCode() { TokenType[] at = this.argTypes; - if (at == null) return -1; + if(at == null) + return -1; int hc = 0; - for (int i = at.Length; -- i >= 0;) { + for(int i = at.Length; --i >= 0;) + { int c = (hc < 0) ? 1 : 0; - hc = hc * 2 + c; - hc ^= at[i].ToString ().GetHashCode (); + hc = hc * 2 + c; + hc ^= at[i].ToString().GetHashCode(); } return hc; } } - private struct TDVEntry { + private struct TDVEntry + { public int count; public TokenDeclVar var; } private bool isFrozen = false; private bool locals; - private Dictionary> master = new Dictionary> (); + private Dictionary> master = new Dictionary>(); private int count = 0; private VarDict frozenLocals = null; @@ -98,7 +115,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @param locals = false: cannot be frozen, allows forward references * true: can be frozen, thus forbidding forward references */ - public VarDict (bool locals) + public VarDict(bool locals) { this.locals = locals; } @@ -106,19 +123,21 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Add new variable to the dictionary. */ - public bool AddEntry (TokenDeclVar var) + public bool AddEntry(TokenDeclVar var) { - if (isFrozen) { - throw new Exception ("var dict is frozen"); + 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 typedic; - if (!master.TryGetValue (var.name.val, out typedic)) { - typedic = new Dictionary (); - master.Add (var.name.val, typedic); + if(!master.TryGetValue(var.name.val, out typedic)) + { + typedic = new Dictionary(); + master.Add(var.name.val, typedic); } /* @@ -127,41 +146,50 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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; + 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); + entry.count = ++count; + entry.var = var; + typedic.Add(types, entry); return true; } - public int Count { get { return count; } } + 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 () + 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(!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)) { + if((frozenLocals == null) || (frozenLocals.count != this.count)) + { /* * Make a copy of the current var dictionary frame. @@ -169,12 +197,12 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * contain additions made after this point, those additions * will have a count .gt. frozen count and will be ignored. */ - frozenLocals = new VarDict (true); + frozenLocals = new VarDict(true); frozenLocals.outerVarDict = this.outerVarDict; - frozenLocals.thisClass = this.thisClass; - frozenLocals.master = this.master; - frozenLocals.count = this.count; + frozenLocals.thisClass = this.thisClass; + frozenLocals.master = this.master; + frozenLocals.count = this.count; frozenLocals.frozenLocals = frozenLocals; /* @@ -196,23 +224,27 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * else: list of matching functions/variables * for variables, always of length 1 */ - private List found = new List (); - public TokenDeclVar[] FindCallables (string name, TokenType[] argTypes) + private List found = new List(); + public TokenDeclVar[] FindCallables(string name, TokenType[] argTypes) { - argTypes = KeyTypesToStringTypes (argTypes); - TokenDeclVar var = FindExact (name, argTypes); - if (var != null) return new TokenDeclVar[] { var }; + argTypes = KeyTypesToStringTypes(argTypes); + TokenDeclVar var = FindExact(name, argTypes); + if(var != null) + return new TokenDeclVar[] { var }; Dictionary typedic; - if (!master.TryGetValue (name, out typedic)) return null; + if(!master.TryGetValue(name, out typedic)) + return null; - found.Clear (); - foreach (KeyValuePair kvp in typedic) { - if ((kvp.Value.count <= this.count) && kvp.Key.CanBeCalledBy (argTypes)) { - found.Add (kvp.Value.var); + found.Clear(); + foreach(KeyValuePair 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; + return (found.Count > 0) ? found.ToArray() : null; } /** @@ -223,19 +255,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @returns null: no matching function/variable found * else: the matching function/variable */ - public TokenDeclVar FindExact (string name, TokenType[] argTypes) + public TokenDeclVar FindExact(string name, TokenType[] argTypes) { /* * Look for list of stuff that matches the given name. */ Dictionary typedic; - if (!master.TryGetValue (name, out typedic)) return null; + 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; + foreach(TDVEntry entry in typedic.Values) + { + if(entry.count > this.count) + continue; TokenDeclVar var = entry.var; /* @@ -248,12 +283,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /* * Convert any key args to string args. */ - declArgs = KeyTypesToStringTypes (declArgs); + 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((declArgs == null) && (argTypes == null)) + return var; /* * If calling a delegate, it is a match, regardless of delegate arg types. @@ -261,29 +297,38 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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)) { + if((declArgs == null) && (argTypes != null)) + { TokenType fieldType = var.type; - if (fieldType is TokenTypeSDTypeDelegate) return var; + if(fieldType is TokenTypeSDTypeDelegate) + return var; } /* * If not both null, no match, keep looking. */ - if ((declArgs == null) || (argTypes == null)) continue; + 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 != 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; + if(i < 0) + return var; } /* @@ -299,20 +344,26 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @param argTypes = argument types as declared in source code * @returns argTypes with any key replaced by string */ - private static TokenType[] KeyTypesToStringTypes (TokenType[] argTypes) + private static TokenType[] KeyTypesToStringTypes(TokenType[] argTypes) { - if (argTypes != null) { + if(argTypes != null) + { int i; int nats = argTypes.Length; - for (i = nats; -- i >= 0;) { - if (argTypes[i] is TokenTypeKey) break; + for(i = nats; --i >= 0;) + { + if(argTypes[i] is TokenTypeKey) + break; } - if (i >= 0) { + if(i >= 0) + { TokenType[] at = new TokenType[nats]; - for (i = nats; -- i >= 0;) { + for(i = nats; --i >= 0;) + { at[i] = argTypes[i]; - if (argTypes[i] is TokenTypeKey) { - at[i] = new TokenTypeStr (argTypes[i]); + if(argTypes[i] is TokenTypeKey) + { + at[i] = new TokenTypeStr(argTypes[i]); } } return at; @@ -324,48 +375,60 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // foreach goes through all the TokenDeclVars that were added // IEnumerable - public IEnumerator GetEnumerator () + public IEnumerator GetEnumerator() { - return new VarDictEnumerator (this.master, this.count); + return new VarDictEnumerator(this.master, this.count); } - private class VarDictEnumerator : IEnumerator { + private class VarDictEnumerator: IEnumerator + { private IEnumerator masterEnum; private IEnumerator typedicEnum; private int count; - public VarDictEnumerator (Dictionary> master, int count) + public VarDictEnumerator(Dictionary> master, int count) { - masterEnum = master.Values.GetEnumerator (); + masterEnum = master.Values.GetEnumerator(); this.count = count; } // IEnumerator - public void Reset () + public void Reset() { - masterEnum.Reset (); + masterEnum.Reset(); typedicEnum = null; } // IEnumerator - public bool MoveNext () + public bool MoveNext() { - while (true) { - if (typedicEnum != null) { - while (typedicEnum.MoveNext ()) { - if (((TDVEntry)typedicEnum.Current).count <= this.count) return true; + while(true) + { + if(typedicEnum != null) + { + while(typedicEnum.MoveNext()) + { + if(((TDVEntry)typedicEnum.Current).count <= this.count) + return true; } typedicEnum = null; } - if (!masterEnum.MoveNext ()) return false; + if(!masterEnum.MoveNext()) + return false; Dictionary ctd; ctd = (Dictionary)masterEnum.Current; - typedicEnum = ctd.Values.GetEnumerator (); + typedicEnum = ctd.Values.GetEnumerator(); } } // IEnumerator - public object Current { get { return ((TDVEntry)typedicEnum.Current).var; } } + public object Current + { + get + { + return ((TDVEntry)typedicEnum.Current).var; + } + } } } } diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRArray.cs similarity index 58% rename from OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMRArray.cs index 36d95d3a1f..b79722483b 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRArray.cs @@ -40,12 +40,13 @@ using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; // This class exists in the main app domain // -namespace OpenSim.Region.ScriptEngine.XMREngine +namespace OpenSim.Region.ScriptEngine.Yengine { /** * @brief Array objects. */ - public class XMR_Array { + public class XMR_Array + { private const int EMPTYHEAP = 64; private const int ENTRYHEAP = 24; @@ -53,36 +54,40 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // false: array[0..arrayValid-1] is all there is private SortedDictionary dnary; private SortedDictionary.Enumerator enumr; - // enumerator used to fill 'array' past arrayValid to end of dictionary + // 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[] 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 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) + public XMR_Array(XMRInstAbstract inst) { this.inst = inst; - dnary = new SortedDictionary (XMRArrayKeyComparer.singleton); - heapUse = inst.UpdateHeapUse (0, EMPTYHEAP); + dnary = new SortedDictionary(XMRArrayKeyComparer.singleton); + heapUse = inst.UpdateHeapUse(0, EMPTYHEAP); } - ~XMR_Array () + ~XMR_Array() { - heapUse = inst.UpdateHeapUse (heapUse, 0); + heapUse = inst.UpdateHeapUse(heapUse, 0); } - public static TokenType GetRValType (TokenName name) + 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); + 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); } /** @@ -93,44 +98,51 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public object GetByKey(object key) { object val; - key = FixKey (key); - if (!dnary.TryGetValue (key, out val)) val = null; + key = FixKey(key); + if(!dnary.TryGetValue(key, out val)) + val = null; return val; } public void SetByKey(object key, object value) { - key = FixKey (key); + key = FixKey(key); /* * Update heap use throwing an exception on failure * before making any changes to the array. */ - int keysize = HeapTrackerObject.Size (key); + int keysize = HeapTrackerObject.Size(key); int newheapuse = heapUse; object oldval; - if (dnary.TryGetValue (key, out oldval)) { - newheapuse -= keysize + HeapTrackerObject.Size (oldval); + if(dnary.TryGetValue(key, out oldval)) + { + newheapuse -= keysize + HeapTrackerObject.Size(oldval); } - if (value != null) { - newheapuse += keysize + HeapTrackerObject.Size (value); + if(value != null) + { + newheapuse += keysize + HeapTrackerObject.Size(value); } - heapUse = inst.UpdateHeapUse (heapUse, newheapuse); + 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) { + if(value != null) + { dnary[key] = value; - } else { - dnary.Remove (key); + } + 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> (ref array, array.Length / 2); + if((array != null) && (dnary.Count < array.Length / 2)) + { + Array.Resize>(ref array, array.Length / 2); } } @@ -148,44 +160,48 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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) + public static XMR_Array Obj2Array(object obj) { - if (obj == null) throw new NullReferenceException (); + if(obj == null) + throw new NullReferenceException(); return (XMR_Array)obj; } - public static LSL_Key Obj2Key (object obj) + public static LSL_Key Obj2Key(object obj) { - if (obj == null) throw new NullReferenceException (); + if(obj == null) + throw new NullReferenceException(); return (LSL_Key)obj; } - public static LSL_List Obj2List (object obj) + public static LSL_List Obj2List(object obj) { - if (obj == null) throw new NullReferenceException (); + if(obj == null) + throw new NullReferenceException(); return (LSL_List)obj; } - public static LSL_String Obj2String (object obj) + public static LSL_String Obj2String(object obj) { - if (obj == null) throw new NullReferenceException (); - return obj.ToString (); + 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 () + public void __pub_clear() { - heapUse = inst.UpdateHeapUse (heapUse, EMPTYHEAP); - dnary.Clear (); + heapUse = inst.UpdateHeapUse(heapUse, EMPTYHEAP); + dnary.Clear(); enumrValid = false; arrayValid = 0; - array = null; + array = null; } /** * @brief return number of elements in the array. */ - public int __pub_count () + public int __pub_count() { return dnary.Count; } @@ -196,9 +212,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @returns null: array doesn't have that many elements * else: index (key) for that element */ - public object __pub_index (int number) + public object __pub_index(int number) { - return ForEach (number) ? UnfixKey (array[number].Key) : null; + return ForEach(number) ? UnfixKey(array[number].Key) : null; } /** @@ -207,9 +223,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @returns null: array doesn't have that many elements * else: value for that element */ - public object __pub_value (int number) + public object __pub_value(int number) { - return ForEach (number) ? array[number].Value : null; + return ForEach(number) ? array[number].Value : null; } /** @@ -218,14 +234,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @returns false: element does not exist * true: element exists */ - private bool ForEach (int number) + 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) { + if(array == null) + { array = new KeyValuePair[dnary.Count]; arrayValid = 0; } @@ -233,17 +250,20 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /* * If dictionary modified since last enumeration, get a new enumerator. */ - if (arrayValid == 0) { - enumr = dnary.GetEnumerator (); + 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> (ref array, dnary.Count); + while((arrayValid <= number) && enumrValid && enumr.MoveNext()) + { + if(arrayValid >= array.Length) + { + Array.Resize>(ref array, dnary.Count); } array[arrayValid++] = enumr.Current; } @@ -258,17 +278,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @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) + 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 kvp in dnary) { - sendObj (UnfixKey (kvp.Key)); - sendObj (kvp.Value); + sendObj(dnary.Count); + foreach(KeyValuePair kvp in dnary) + { + sendObj(UnfixKey(kvp.Key)); + sendObj(kvp.Value); } } @@ -278,10 +299,10 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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) + public delegate object RecvArrayObjDelegate(); + public void RecvArrayObj(RecvArrayObjDelegate recvObj) { - heapUse = inst.UpdateHeapUse (heapUse, EMPTYHEAP); + heapUse = inst.UpdateHeapUse(heapUse, EMPTYHEAP); /* * Cause any enumeration to refill the array from the sorted dictionary. @@ -294,14 +315,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /* * 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); + 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); } } @@ -310,16 +332,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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) + 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) { + 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); + 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 } @@ -329,99 +357,119 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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) + private static object UnfixKey(object key) { - if (key is XMRArrayListKey) key = ((XMRArrayListKey)key).GetOriginal (); + if(key is XMRArrayListKey) + key = ((XMRArrayListKey)key).GetOriginal(); return key; } } - public class XMRArrayKeyComparer : IComparer { + public class XMRArrayKeyComparer: IComparer + { - public static XMRArrayKeyComparer singleton = new XMRArrayKeyComparer (); + public static XMRArrayKeyComparer singleton = new XMRArrayKeyComparer(); /** * @brief Compare two keys */ - public int Compare (object x, object y) // IComparer + public int Compare(object x, object y) // IComparer { /* * 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; + 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); + if(!comparers.TryGetValue(xtn, out cd)) + { + throw new Exception("unsupported key type " + xtn); } - return cd (x, y); + return cd(x, y); } - private delegate int ComparerDelegate (object a, object b); + private delegate int ComparerDelegate(object a, object b); - private static Dictionary comparers = BuildComparers (); + private static Dictionary comparers = BuildComparers(); - private static Dictionary BuildComparers () + private static Dictionary BuildComparers() { - Dictionary cmps = new Dictionary (); - 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); + Dictionary cmps = new Dictionary(); + 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) + 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; + if(af < bf) + return -1; + if(af > bf) + return 1; return 0; } - private static int MyIntComparer (object a, object b) + private static int MyIntComparer(object a, object b) { return (int)a - (int)b; } - private static int MyListKeyComparer (object a, object b) + private static int MyListKeyComparer(object a, object b) { XMRArrayListKey alk = (XMRArrayListKey)a; XMRArrayListKey blk = (XMRArrayListKey)b; - return XMRArrayListKey.Compare (alk, blk); + return XMRArrayListKey.Compare(alk, blk); } - private static int MyRotationComparer (object a, object b) + 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; + 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) + private static int MyStringComparer(object a, object b) { - return String.CompareOrdinal ((string)a, (string)b); + return String.CompareOrdinal((string)a, (string)b); } - private static int MyVectorComparer (object a, object 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; + 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; } } @@ -433,7 +481,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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 { + public class XMRArrayListKey + { private LSL_List original; private object[] cleaned; private int length; @@ -443,18 +492,19 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @brief Construct a sanitized object[] from a list. * Also save the original list in case we need it later. */ - public XMRArrayListKey (LSL_List key) + 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 (); + 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; @@ -463,8 +513,10 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Get heap tracking size. */ - public int Size { - get { + public int Size + { + get + { return original.Size; } } @@ -472,15 +524,20 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief See if the given object is an XMRArrayListKey and every value is equal to our own. */ - public override bool Equals (object o) + public override bool Equals(object o) { - if (!(o is XMRArrayListKey)) return false; + 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; + 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; } @@ -488,7 +545,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Get an hash code. */ - public override int GetHashCode () + public override int GetHashCode() { return hashCode; } @@ -496,15 +553,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Compare for key sorting. */ - public static int Compare (XMRArrayListKey x, XMRArrayListKey y) + 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 ++) { + 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; + j = XMRArrayKeyComparer.singleton.Compare(xo, yo); + if(j != 0) + break; } } return j; @@ -513,7 +573,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Get the original LSL_List we were built from. */ - public LSL_List GetOriginal () + public LSL_List GetOriginal() { return original; } @@ -521,14 +581,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Debugging */ - public override string ToString () + 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 ()); + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < length; i++) + { + if(i > 0) + sb.Append(','); + sb.Append(cleaned[i].ToString()); } - return sb.ToString (); + return sb.ToString(); } } } diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMREngXmrTestLs.cs b/OpenSim/Region/ScriptEngine/YEngine/XMREngXmrTestLs.cs new file mode 100644 index 0000000000..6b752bdcff --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMREngXmrTestLs.cs @@ -0,0 +1,578 @@ +/* + * 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.Yengine +{ + public partial class Yengine + { + + 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("[YEngine]: xmr ls -full -max= -out= -queues -topcpu"); + return; + } + if(args[i].StartsWith("-max=")) + { + try + { + maxScripts = Convert.ToInt32(args[i].Substring(5)); + } + catch(Exception e) + { + m_log.Error("[YEngine]: 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("[YEngine]: 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("[YEngine]: error creating " + outName + ": " + e.Message); + return; + } + } + else + { + outFile = new LogInfoTextWriter(m_log); + } + + try + { + + /* + * Scan instance list to find those that match selection criteria. + */ + if(!Monitor.TryEnter(m_InstancesDict, 100)) + { + m_log.Error("[YEngine]: 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(instances, CompareInstancesByCPUTime); + } + + /* + * Print the entries. + */ + if(!flagFull) + { + outFile.WriteLine(" ItemID" + + " CPU(ms)" + + " NumEvents" + + " Status " + + " World Position " + + " :"); + } + 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 selargs = new List(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("[YEngine]: xmr pev -all | "); + return; + } + if(arg[0] == '-') + { + m_log.Error("[YEngine]: 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("[YEngine]: missing , 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("[YEngine]: 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("[YEngine]: 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 valuelist = new List(); + 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 valuelist = new List(); + 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("[YEngine]: " + 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("[YEngine]: 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("[YEngine]: xmr reset -all | "); + return; + } + if(args[i][0] == '-') + { + m_log.Error("[YEngine]: 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("[YEngine]: 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("[YEngine]: 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("[YEngine]: " + sb.ToString()); + sb.Remove(0, sb.Length); + } + else + { + sb.Append(c); + } + } + public override void Close() + { + } + public override Encoding Encoding + { + get + { + return Encoding.UTF8; + } + } + } +} diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs b/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs new file mode 100644 index 0000000000..b522344535 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs @@ -0,0 +1,1901 @@ +/* + * 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. + */ + +// based on YEngine from Mike Rieker (Dreamnation) and Melanie Thielker +// but with several changes to be more cross platform. + +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 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("YEngine", OpenSim.VersionInfo.VersionNumber)] +[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)] + +namespace OpenSim.Region.ScriptEngine.Yengine +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "YEngine")] + public partial class Yengine: 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 bool m_ScriptDebugSaveSource; + public bool m_ScriptDebugSaveIL; + 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; + private Dictionary m_ScriptErrors = + new Dictionary(); + private Dictionary> m_ObjectItemList = + new Dictionary>(); + private Dictionary m_ObjectInstArray = + new Dictionary(); + public Dictionary m_XMRInstanceApiCtxFieldInfos = + new Dictionary(); + public int m_StackSize; + private int m_HeapSize; + private Thread m_SleepThread = 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 m_InstancesDict = + new Dictionary(); + public Queue m_ThunkQueue = new Queue(); + public XMRInstQueue m_StartQueue = new XMRInstQueue(); + public XMRInstQueue m_YieldQueue = new XMRInstQueue(); + public XMRInstQueue m_SleepQueue = new XMRInstQueue(); + private string m_LockedDict = "nobody"; + + public Yengine() + { + } + + public string Name + { + get + { + return "YEngine"; + } + } + + public Type ReplaceableInterface + { + get + { + return null; + } + } + + public string ScriptEnginePath + { + get + { + return m_ScriptBasePath; + } + } + + public string ScriptClassName + { + get + { + return "YEngineScript"; + } + } + + 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("[YEngine]: Initialize entry"); + m_ConfigSource = config; + + ////foreach (IConfig icfg in config.Configs) { + //// m_log.Debug("[YEngine]: Initialise: configs[" + icfg.Name + "]"); + //// foreach (string key in icfg.GetKeys ()) { + //// m_log.Debug("[YEngine]: Initialise: " + key + "=" + icfg.GetExpanded (key)); + //// } + ////} + + m_Enabled = false; + m_Config = config.Configs["YEngine"]; + if(m_Config == null) + { + m_log.Info("[YEngine]: no config, assuming disabled"); + return; + } + m_Enabled = m_Config.GetBoolean("Enabled", false); + m_log.InfoFormat("[YEngine]: config enabled={0}", m_Enabled); + if(!m_Enabled) + return; + + m_UseSourceHashCode = m_Config.GetBoolean("UseSourceHashCode", false); + numThreadScriptWorkers = m_Config.GetInt("NumThreadScriptWorkers", 1); + + m_TraceCalls = m_Config.GetBoolean("TraceCalls", false); + m_Verbose = m_Config.GetBoolean("Verbose", false); + m_ScriptDebug = m_Config.GetBoolean("ScriptDebug", false); + m_ScriptDebugSaveSource = m_Config.GetBoolean("ScriptDebugSaveSource", false); + m_ScriptDebugSaveIL = m_Config.GetBoolean("ScriptDebugSaveIL", false); + + m_StackSize = m_Config.GetInt("ScriptStackSize", 2048) << 10; + m_HeapSize = m_Config.GetInt("ScriptHeapSize", 1024) << 10; + + // 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("[YEngine]: {0} mycode={1}, oscode={2}", i, mycode, oscode); + err = true; + } + } + if(err) + { + m_Enabled = false; + return; + } + + m_SleepThread = StartMyThread(RunSleepThread, "Yengine sleep", ThreadPriority.Normal); + for(int i = 0; i < numThreadScriptWorkers; i++) + StartThreadWorker(i); + + m_log.InfoFormat("[YEngine]: 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("[YEngine]: ... {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("yeng", false, + "yeng", + "yeng [...|help|...] ...", + "Run Yengine script engine commands", + RunTest); + + TraceCalls("[YEngine]: Initialize successful"); + } + + public void AddRegion(Scene scene) + { + if(!m_Enabled) + return; + + TraceCalls("[YEngine]: YEngine.AddRegion({0})", scene.RegionInfo.RegionName); + + m_Scene = scene; + + m_Scene.RegisterModuleInterface(this); + + m_ScriptBasePath = m_Config.GetString("ScriptBasePath", "ScriptEngines"); + m_ScriptBasePath = Path.Combine(m_ScriptBasePath, "Yengine"); + m_ScriptBasePath = Path.Combine(m_ScriptBasePath, scene.RegionInfo.RegionID.ToString()); + + Directory.CreateDirectory(m_ScriptBasePath); + + m_Scene.EventManager.OnRezScript += OnRezScript; + + m_Scene.StackModuleInterface(this); + } + + private void OneTimeLateInitialization() + { + // Build list of defined APIs and their 'this' types and define a field in XMRInstanceSuperType. + ApiManager am = new ApiManager(); + Dictionary apiCtxTypes = new Dictionary(); + foreach(string api in am.GetApis()) + { + m_log.Debug("[YEngine]: 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_ 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_ field of XMRInstanceSuperType. + foreach(KeyValuePair 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(); + 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("[YEngine]: added comms function " + cccg.fullName); + } + catch(Exception e) + { + m_log.Error("[YEngine]: failed to add comms function " + mi.Name); + m_log.Error("[YEngine]: - " + e.ToString()); + } + } + + // Add constants to list of built-in constants. + Dictionary consts = comms.GetConstants(); + foreach(KeyValuePair kvp in consts) + { + try + { + ScriptConst sc = ScriptConst.AddConstant(kvp.Key, kvp.Value); + Verbose("[YEngine]: added comms constant " + sc.name); + } + catch(Exception e) + { + m_log.Error("[YEngine]: failed to add comms constant " + kvp.Key); + m_log.Error("[YEngine]: - " + e.Message); + } + } + } + else + { + Verbose("[YEngine]: 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("[YEngine]: YEngine.RemoveRegion({0})", scene.RegionInfo.RegionName); + + if(m_MaintenanceTimer != null) + { + m_MaintenanceTimer.Stop(); + m_MaintenanceTimer.Dispose(); + } + + // 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; + if(m_SleepThread != null) + { + lock(m_SleepQueue) + Monitor.PulseAll(m_SleepQueue); + + if(!m_SleepThread.Join(250)) + m_SleepThread.Abort(); + m_SleepThread = null; + } + + StopThreadWorkers(); + + 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("[YEngine]: YEngine.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("[YEngine]: StartProcessing entry"); + m_StartProcessing = true; + ResumeThreads(); + m_log.Debug("[YEngine]: StartProcessing return"); + m_Scene.EventManager.TriggerEmptyScriptCompileQueue(0, ""); + } + + public void Close() + { + TraceCalls("[YEngine]: YEngine.Close()"); + } + + private void RunTest(string module, string[] args) + { + if(args.Length < 2) + { + m_log.Info("[YEngine]: missing command, try 'xmr help'"); + return; + } + + m_log.Info("[YEngine]: " + m_Scene.RegionInfo.RegionName); + + switch(args[1]) + { + case "cvv": + m_log.InfoFormat("[YEngine]: compiled version value = {0}", + ScriptCodeGen.COMPILED_VERSION_VALUE); + break; + + case "help": + case "?": + m_log.Info("[YEngine]: xmr cvv - show compiler version value"); + m_log.Info("[YEngine]: xmr ls [-help ...]"); + m_log.Info("[YEngine]: xmr mv [] - show migration version value"); + m_log.Info("[YEngine]: xmr pev [-help ...] - post event"); + m_log.Info("[YEngine]: xmr reset [-help ...]"); + m_log.Info("[YEngine]: xmr resume - resume script processing"); + m_log.Info("[YEngine]: xmr suspend - suspend script processing"); + m_log.Info("[YEngine]: xmr tracecalls [yes | no]"); + m_log.Info("[YEngine]: xmr verbose [yes | no]"); + break; + + case "ls": + XmrTestLs(args, 2); + break; + + case "mvv": + m_log.InfoFormat("[YEngine]: migration version value = {0}", + XMRInstance.migrationVersion); + break; + + case "pev": + XmrTestPev(args, 2); + break; + + case "reset": + XmrTestReset(args, 2); + break; + + case "resume": + m_log.Info("[YEngine]: resuming scripts"); + ResumeThreads(); + break; + + case "suspend": + m_log.Info("[YEngine]: suspending scripts"); + SuspendThreads(); + break; + + case "tracecalls": + if(args.Length > 2) + m_TraceCalls = (args[2][0] & 1) != 0; + m_log.Info("[YEngine]: tracecalls " + (m_TraceCalls ? "yes" : "no")); + break; + + case "verbose": + if(args.Length > 2) + m_Verbose = (args[2][0] & 1) != 0; + m_log.Info("[YEngine]: verbose " + (m_Verbose ? "yes" : "no")); + break; + + default: + m_log.Error("[YEngine]: 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("[YEngine]: YEngine.SaveAllState() called!!"); + } + +#pragma warning disable 0067 + public event ScriptRemoved OnScriptRemoved; + public event ObjectRemoved OnObjectRemoved; +#pragma warning restore 0067 + + // 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("[YEngine]: YEngine.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("[YEngine]: YEngine.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("[YEngine]: YEngine.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(); + 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 "YEngine"; + } + } + + 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("[YEngine]: YEngine.GetXMLState({0})", itemID.ToString()); + + if(!instance.m_HasRun) + return String.Empty; + + XmlDocument doc = new XmlDocument(); + + /* + * Set up 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 ... 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("[YEngine]: YEngine.SetXMLState({0})", itemID.ToString()); + + // Make sure 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; + + // ... 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 ... 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("[YEngine]: YEngine.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("[YEngine]: YEngine.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("[YEngine]: 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(); + 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("[YEngine]: " + itemID.ToString() + " requests undefined/disabled engine " + engineName); + m_log.Info("[YEngine]: - " + part.GetWorldPosition()); + m_log.Info("[YEngine]: first line: " + firstline); + if(defEngine != ScriptEngineName) + { + m_log.Info("[YEngine]: leaving it to the default script engine (" + defEngine + ") to process it"); + return; + } + m_log.Info("[YEngine]: 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 itemIDList; + if(!m_ObjectItemList.TryGetValue(instance.m_PartUUID, out itemIDList)) + { + itemIDList = new List(); + 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("[YEngine]: {0}/{2} first load failed ({1}), retrying after recompile", + itemID.ToString(), initerr.Message, instance.m_Item.AssetID.ToString()); + Verbose("[YEngine]:\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("[YEngine]: Error starting script {0}/{2}: {1}", + itemID.ToString(), initerr.Message, instance.m_Item.AssetID.ToString()); + if(initerr.Message != "compilation errors") + { + Verbose("[YEngine]: - " + instance.m_Part.GetWorldPosition() + " " + instance.m_DescName); + Verbose("[YEngine]: 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("[YEngine]: {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("[YEngine]: 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 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("[YEngine]: YEngine.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("[YEngine]: YEngine.OnGetScriptRunning({0},{1})", objectID.ToString(), itemID.ToString()); + + IEventQueue eq = World.RequestModuleInterface(); + 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("[YEngine]: YEngine.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) + { + if(inst.m_IState != XMRInstState.ONSTARTQ) + throw new Exception("bad state"); + + lock(m_StartQueue) + m_StartQueue.InsertTail(inst); + + 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. + 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)) + { + inst.m_IState = XMRInstState.ONSTARTQ; + Monitor.Exit(inst.m_QueueLock); + lock(m_StartQueue) + 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); + } + WakeUpOne(); + } + } + + 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("[YEngine]: YEngine.Die({0})", itemID.ToString()); + instance.Die(); + } + } + + /** + * @brief Get specific script instance for which OnRezScript() + * has been called for an YEngine 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 GetObjectScriptsExecutionTimes() + { + Dictionary topScripts = new Dictionary(); + 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 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("[YEngine]: YEngine.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("[YEngine]: YEngine.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 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("[YEngine]: starting thread " + name); + Thread thread = WorkManager.StartThread(start, name, priority, true, false, false); + return thread; + } + + public static void UpdateMyThread() + { + Watchdog.UpdateThread(); + } + + public static void MyThreadExiting() + { + Watchdog.RemoveThread(true); + } + } +} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMREvents.cs b/OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs similarity index 81% rename from OpenSim/Region/ScriptEngine/XMREngine/XMREvents.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs index f6c2d73b22..b4e92b9d1c 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMREvents.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs @@ -45,41 +45,41 @@ 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 +namespace OpenSim.Region.ScriptEngine.Yengine { /// /// Prepares events so they can be directly executed upon a script by EventQueueManager, then queues it. /// - public partial class XMREngine + public partial class Yengine { 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; + m_log.Info("[YEngine] 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(); - if (money != null) + 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(); + if(money != null) { - money.OnObjectPaid+=HandleObjectPaid; + money.OnObjectPaid += HandleObjectPaid; } } @@ -106,15 +106,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine SceneObjectPart part = this.World.GetSceneObjectPart(objectID); - if (part == null) + if(part == null) return; - if ((part.ScriptEvents & scriptEvents.money) == 0) + if((part.ScriptEvents & scriptEvents.money) == 0) part = part.ParentGroup.RootPart; - Verbose ("Paid: " + objectID + " from " + agentID + ", amount " + amount); + Verbose("Paid: " + objectID + " from " + agentID + ", amount " + amount); - if (part != null) + if(part != null) { money(part.LocalId, agentID, amount, det); } @@ -141,7 +141,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine touches(localID, originalID, offsetPos, remoteClient, surfaceArgs, "touch"); } - private static Vector3 zeroVec3 = new Vector3(0,0,0); + private static Vector3 zeroVec3 = new Vector3(0, 0, 0); public void touch_end(uint localID, uint originalID, IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs) { @@ -152,10 +152,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs, string eventname) { SceneObjectPart part; - if (originalID == 0) { + if(originalID == 0) + { part = this.World.GetSceneObjectPart(localID); - if (part == null) return; - } else { + if(part == null) + return; + } + else + { part = this.World.GetSceneObjectPart(originalID); } @@ -167,7 +171,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine offsetPos.Z); det.LinkNum = part.LinkNum; - if (surfaceArgs != null) { + if(surfaceArgs != null) + { det.SurfaceTouchArgs = surfaceArgs; } @@ -182,7 +187,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine int ch = (int)change; // Add to queue for all scripts in localID, Object pass change. this.PostObjectEvent(localID, new EventParams( - "changed",new object[] { ch }, + "changed", new object[] { ch }, zeroDetectParams)); } @@ -216,15 +221,17 @@ namespace OpenSim.Region.ScriptEngine.XMREngine private void collisions(uint localID, ColliderArgs col, string eventname) { int dc = col.Colliders.Count; - if (dc > 0) { + if(dc > 0) + { DetectParams[] det = new DetectParams[dc]; int i = 0; - foreach (DetectedObject detobj in col.Colliders) { + foreach(DetectedObject detobj in col.Colliders) + { DetectParams d = new DetectParams(); det[i++] = d; d.Key = detobj.keyUUID; - d.Populate (this.World); + d.Populate(this.World); /* not done by XEngine... d.Position = detobj.posVector; @@ -257,12 +264,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine private void land_collisions(uint localID, ColliderArgs col, string eventname) { - foreach (DetectedObject detobj in col.Colliders) { + 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 }, + EventParams eps = new EventParams(eventname, + new Object[] { vec }, zeroDetectParams); this.PostObjectEvent(localID, eps); } @@ -274,7 +282,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public void control(UUID itemID, UUID agentID, uint held, uint change) { this.PostScriptEvent(itemID, new EventParams( - "control",new object[] { + "control", new object[] { agentID.ToString(), (int)held, (int)change}, @@ -285,7 +293,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine string address, string subject, string message, int numLeft) { this.PostObjectEvent(localID, new EventParams( - "email",new object[] { + "email", new object[] { timeSent, address, subject, @@ -308,18 +316,18 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public void not_at_target(uint localID) { this.PostObjectEvent(localID, new EventParams( - "not_at_target",zeroObjectArray, + "not_at_target", zeroObjectArray, zeroDetectParams)); } public void at_rot_target(uint localID, uint handle, OpenMetaverse.Quaternion targetrot, OpenMetaverse.Quaternion atrot) { this.PostObjectEvent( - localID, + localID, new EventParams( "at_rot_target", new object[] { - new LSL_Integer(handle), + 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) }, @@ -331,7 +339,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public void not_at_rot_target(uint localID) { this.PostObjectEvent(localID, new EventParams( - "not_at_rot_target",zeroObjectArray, + "not_at_rot_target", zeroObjectArray, zeroDetectParams)); } @@ -340,7 +348,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public void attach(uint localID, UUID itemID, UUID avatar) { this.PostObjectEvent(localID, new EventParams( - "attach",new object[] { + "attach", new object[] { avatar.ToString() }, zeroDetectParams)); } @@ -351,14 +359,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public void moving_start(uint localID) { this.PostObjectEvent(localID, new EventParams( - "moving_start",zeroObjectArray, + "moving_start", zeroObjectArray, zeroDetectParams)); } public void moving_end(uint localID) { this.PostObjectEvent(localID, new EventParams( - "moving_end",zeroObjectArray, + "moving_end", zeroObjectArray, zeroDetectParams)); } diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRHeapTracker.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRHeapTracker.cs similarity index 60% rename from OpenSim/Region/ScriptEngine/XMREngine/XMRHeapTracker.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMRHeapTracker.cs index 0e7d3038e9..33eb8bf5e0 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRHeapTracker.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRHeapTracker.cs @@ -37,7 +37,7 @@ 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 +namespace OpenSim.Region.ScriptEngine.Yengine { /** * One instance of this class for lsl base objects that take a variable @@ -48,32 +48,35 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * Note that the xmr arrays and script-defined objects have their own * heap tracking built in so do not need any of this stuff. */ - public class HeapTrackerBase { + public class HeapTrackerBase + { protected int usage; // num bytes used by object protected XMRInstAbstract instance; // what script it is in - public HeapTrackerBase (XMRInstAbstract inst) + public HeapTrackerBase(XMRInstAbstract inst) { - if (inst == null) throw new ArgumentNullException ("inst"); + if(inst == null) + throw new ArgumentNullException("inst"); instance = inst; } - ~HeapTrackerBase () + ~HeapTrackerBase() { - usage = instance.UpdateHeapUse (usage, 0); + usage = instance.UpdateHeapUse(usage, 0); } } /** * Wrapper around lists to keep track of how much memory they use. */ - public class HeapTrackerList : HeapTrackerBase { - private static FieldInfo listValueField = typeof (HeapTrackerList).GetField ("value"); - private static MethodInfo listSaveMethod = typeof (HeapTrackerList).GetMethod ("Save"); + public class HeapTrackerList: HeapTrackerBase + { + private static FieldInfo listValueField = typeof(HeapTrackerList).GetField("value"); + private static MethodInfo listSaveMethod = typeof(HeapTrackerList).GetMethod("Save"); public LSL_List value; - public HeapTrackerList (XMRInstAbstract inst) : base (inst) { } + public HeapTrackerList(XMRInstAbstract inst) : base(inst) { } // generate CIL code to pop the value from the CIL stack // input: @@ -83,7 +86,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // 'this' pointer popped from stack // new value popped from CIL stack // heap usage updated - public static void GenPop (Token errorAt, ScriptMyILGen ilGen) + public static void GenPop(Token errorAt, ScriptMyILGen ilGen) { ilGen.Emit(errorAt, OpCodes.Call, listSaveMethod); } @@ -95,21 +98,21 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // 'this' pointer popped from stack // value pushed on CIL stack replacing 'this' pointer // returns typeof value pushed on stack - public static Type GenPush (Token errorAt, ScriptMyILGen ilGen) + public static Type GenPush(Token errorAt, ScriptMyILGen ilGen) { - ilGen.Emit (errorAt, OpCodes.Ldfld, listValueField); - return typeof (LSL_List); + ilGen.Emit(errorAt, OpCodes.Ldfld, listValueField); + return typeof(LSL_List); } - public void Save (LSL_List lis) + public void Save(LSL_List lis) { - int newuse = Size (lis); - usage = instance.UpdateHeapUse (usage, newuse); + int newuse = Size(lis); + usage = instance.UpdateHeapUse(usage, newuse); value = lis; } //private static int counter = 5; - public static int Size (LSL_List lis) + public static int Size(LSL_List lis) { // VS2017 in debug mode seems to have a problem running this statement quickly: //SLOW: return (!typeof(LSL_List).IsValueType && (lis == null)) ? 0 : lis.Size; @@ -120,9 +123,12 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // VS2017 in debug mode seems content to run this quickly though: - try { + try + { return lis.Size; - } catch { + } + catch + { return 0; } } @@ -131,22 +137,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * Wrapper around objects to keep track of how much memory they use. */ - public class HeapTrackerObject : HeapTrackerBase { - private static FieldInfo objectValueField = typeof (HeapTrackerObject).GetField ("value"); - private static MethodInfo objectSaveMethod = typeof (HeapTrackerObject).GetMethod ("Save"); + public class HeapTrackerObject: HeapTrackerBase + { + private static FieldInfo objectValueField = typeof(HeapTrackerObject).GetField("value"); + private static MethodInfo objectSaveMethod = typeof(HeapTrackerObject).GetMethod("Save"); 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; + public const int HT_INT = 4; + public const int HT_VEC = HT_DOUB * 3; + public const int HT_ROT = HT_DOUB * 4; public object value; - public HeapTrackerObject (XMRInstAbstract inst) : base (inst) { } + public HeapTrackerObject(XMRInstAbstract inst) : base(inst) { } // generate CIL code to pop the value from the CIL stack // input: @@ -156,7 +163,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // 'this' pointer popped from stack // new value popped from CIL stack // heap usage updated - public static void GenPop (Token errorAt, ScriptMyILGen ilGen) + public static void GenPop(Token errorAt, ScriptMyILGen ilGen) { ilGen.Emit(errorAt, OpCodes.Call, objectSaveMethod); } @@ -168,67 +175,88 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // 'this' pointer popped from stack // value pushed on CIL stack replacing 'this' pointer // returns typeof value pushed on stack - public static Type GenPush (Token errorAt, ScriptMyILGen ilGen) + public static Type GenPush(Token errorAt, ScriptMyILGen ilGen) { - ilGen.Emit (errorAt, OpCodes.Ldfld, objectValueField); - return typeof (object); + ilGen.Emit(errorAt, OpCodes.Ldfld, objectValueField); + return typeof(object); } - public void Save (object obj) + public void Save(object obj) { - int newuse = Size (obj); - usage = instance.UpdateHeapUse (usage, newuse); + int newuse = Size(obj); + usage = instance.UpdateHeapUse(usage, newuse); value = obj; } // public so it can be used by XMRArray - public static int Size (object obj) + public static int Size(object obj) { - if (obj == null) return 0; + 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 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) { + 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; + 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)); + for(int i = 0; i < len; i++) + { + size += Size(ar.GetValue(i)); } return size; } - throw new Exception ("unknown size of type " + obj.GetType ().Name); + throw new Exception("unknown size of type " + obj.GetType().Name); } } /** * Wrapper around strings to keep track of how much memory they use. */ - public class HeapTrackerString : HeapTrackerBase { - private static FieldInfo stringValueField = typeof (HeapTrackerString).GetField ("value"); - private static MethodInfo stringSaveMethod = typeof (HeapTrackerString).GetMethod ("Save"); + public class HeapTrackerString: HeapTrackerBase + { + private static FieldInfo stringValueField = typeof(HeapTrackerString).GetField("value"); + private static MethodInfo stringSaveMethod = typeof(HeapTrackerString).GetMethod("Save"); public string value; - public HeapTrackerString (XMRInstAbstract inst) : base (inst) { } + public HeapTrackerString(XMRInstAbstract inst) : base(inst) { } // generate CIL code to pop the value from the CIL stack // input: @@ -238,9 +266,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // 'this' pointer popped from stack // new value popped from CIL stack // heap usage updated - public static void GenPop (Token errorAt, ScriptMyILGen ilGen) + public static void GenPop(Token errorAt, ScriptMyILGen ilGen) { - ilGen.Emit (errorAt, OpCodes.Call, stringSaveMethod); + ilGen.Emit(errorAt, OpCodes.Call, stringSaveMethod); } // generate CIL code to push the value on the CIL stack @@ -250,20 +278,20 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // 'this' pointer popped from stack // value pushed on CIL stack replacing 'this' pointer // returns typeof value pushed on stack - public static Type GenPush (Token errorAt, ScriptMyILGen ilGen) + public static Type GenPush(Token errorAt, ScriptMyILGen ilGen) { - ilGen.Emit (errorAt, OpCodes.Ldfld, stringValueField); - return typeof (string); + ilGen.Emit(errorAt, OpCodes.Ldfld, stringValueField); + return typeof(string); } - public void Save (string str) + public void Save(string str) { - int newuse = Size (str); - usage = instance.UpdateHeapUse (usage, newuse); + int newuse = Size(str); + usage = instance.UpdateHeapUse(usage, newuse); value = str; } - public static int Size (string str) + public static int Size(string str) { return (str == null) ? 0 : str.Length * HeapTrackerObject.HT_CHAR; } diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstAbstract.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstAbstract.cs new file mode 100644 index 0000000000..a0bb3e0122 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstAbstract.cs @@ -0,0 +1,2348 @@ +/* + * 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.Yengine +{ + 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 int m_StackLeft; // total number of stack bytes yet to be used (init to stacksize) + + 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(); + + [xmrMethodCallsCheckRunAttribute] // calls CheckRun() + [xmrMethodIsNoisyAttribute] // calls Stub() + public abstract LSL_List xmrEventDequeue(double timeout, int returnMask1, int returnMask2, + int backgroundMask1, int backgroundMask2); + + [xmrMethodIsNoisyAttribute] // calls Stub() + public abstract void xmrEventEnqueue(LSL_List ev); + + [xmrMethodIsNoisyAttribute] // calls Stub() + public abstract LSL_List xmrEventSaveDets(); + + [xmrMethodIsNoisyAttribute] // calls Stub() + public abstract void xmrEventLoadDets(LSL_List dpList); + + + /**************************************************\ + * 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 ;' 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(m_StackLeft < 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 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) is the same. + * And there must be a '[' in each meaning that it is a script-defined array type. + */ + int i = srctypename.IndexOf('['); + int j = dsttypename.IndexOf('['); + if((i < 0) || (j < 0)) + throw new InvalidCastException("non-array passed: " + srctypename + " and/or " + dsttypename); + if((i != j) || !srctypename.StartsWith(dsttypename.Substring(0, j))) + throw new ArrayTypeMismatchException(srctypename + " vs " + dsttypename); + + /* + * The number of brackets must match exactly. + * This permits copying from something like a float[,][] to something like a float[][]. + * But you cannot copy from a float[][] to a float[] or wisa wersa. + * Counting either '[' or ']' would work equally well. + */ + int srclen = srctypename.Length; + int dstlen = dsttypename.Length; + int srcjags = 0; + int dstjags = 0; + while(++i < srclen) + if(srctypename[i] == ']') + srcjags++; + while(++j < dstlen) + if(dsttypename[j] == ']') + dstjags++; + if(dstjags != srcjags) + throw new ArrayTypeMismatchException(srctypename + " vs " + dsttypename); + + /* + * Perform the copy. + */ + Array srcarray = (Array)srcsdt.instVars.iarObjects[0]; + Array dstarray = (Array)dstsdt.instVars.iarObjects[0]; + Array.Copy(srcarray, srcstart, dstarray, dststart, count); + } + + /** + * @brief Copy from an array to a list. + * @param srcar = the array to copy from + * @param start = where to start in the array + * @param count = number of elements + * @returns the list + */ + public static LSL_List xmrArray2List(object srcar, int start, int count) + { + /* + * Get the script-visible type of the array. + * We only do arrays. + */ + XMRSDTypeClObj array = (XMRSDTypeClObj)srcar; + TokenDeclSDTypeClass sdtClass = array.sdtcClass; + if(sdtClass.arrayOfRank == 0) + throw new InvalidCastException("only do arrays not " + sdtClass.longName.val); + + /* + * Validate objects they want to put in the list. + * We can't allow anything funky that OpenSim runtime doesn't expect. + */ + Array srcarray = (Array)array.instVars.iarObjects[0]; + object[] output = new object[count]; + for(int i = 0; i < count; i++) + { + object src = srcarray.GetValue(i + start); + if(src == null) + throw new NullReferenceException("null element " + i); + if(src is double) + { + output[i] = new LSL_Float((double)src); + continue; + } + if(src is int) + { + output[i] = new LSL_Integer((int)src); + continue; + } + if(src is LSL_Rotation) + { + output[i] = src; + continue; + } + if(src is LSL_Vector) + { + output[i] = src; + continue; + } + if(src is string) + { + output[i] = new LSL_String((string)src); + continue; + } + throw new InvalidCastException("invalid element " + i + " type " + src.GetType().Name); + } + + /* + * Make a list out of that now immutable array. + */ + return new LSL_List(output); + } + + /** + * @brief Copy from a list to an array. + * @param srclist = list to copy from + * @param srcstart = where to start in the list + * @param dstobj = array to copy to + * @param dststart = where to start in the array + * @param count = number of elements + */ + public static void xmrList2Array(LSL_List srclist, int srcstart, object dstobj, int dststart, int count) + { + /* + * Get the script-visible type of the destination. + * We only do arrays. + */ + XMRSDTypeClObj dstarray = (XMRSDTypeClObj)dstobj; + TokenDeclSDTypeClass sdtClass = dstarray.sdtcClass; + if(sdtClass.arrayOfType == null) + throw new InvalidCastException("only do arrays not " + sdtClass.longName.val); + + /* + * Copy from the immutable array to the mutable array. + * Strip off any LSL wrappers as the script code doesn't expect any. + */ + object[] srcarr = srclist.Data; + Array dstarr = (Array)dstarray.instVars.iarObjects[0]; + + for(int i = 0; i < count; i++) + { + object obj = srcarr[i + srcstart]; + if(obj is LSL_Float) + obj = ((LSL_Float)obj).value; + else if(obj is LSL_Integer) + obj = ((LSL_Integer)obj).value; + else if(obj is LSL_String) + obj = ((LSL_String)obj).m_string; + dstarr.SetValue(obj, i + dststart); + } + } + + /** + * @brief Copy from an array of characters to a string. + * @param srcar = the array to copy from + * @param start = where to start in the array + * @param count = number of elements + * @returns the string + */ + public static string xmrChars2String(object srcar, int start, int count) + { + /* + * Make sure they gave us a script-defined array object. + */ + XMRSDTypeClObj array = (XMRSDTypeClObj)srcar; + TokenDeclSDTypeClass sdtClass = array.sdtcClass; + if(sdtClass.arrayOfRank == 0) + throw new InvalidCastException("only do arrays not " + sdtClass.longName.val); + + /* + * We get a type cast error from mono if they didn't give us a character array. + * But if it is ok, create a string from the requested characters. + */ + char[] srcarray = (char[])array.instVars.iarObjects[0]; + return new string(srcarray, start, count); + } + + /** + * @brief Copy from a string to a character array. + * @param srcstr = string to copy from + * @param srcstart = where to start in the string + * @param dstobj = array to copy to + * @param dststart = where to start in the array + * @param count = number of elements + */ + public static void xmrString2Chars(string srcstr, int srcstart, object dstobj, int dststart, int count) + { + /* + * Make sure they gave us a script-defined array object. + */ + XMRSDTypeClObj dstarray = (XMRSDTypeClObj)dstobj; + TokenDeclSDTypeClass sdtClass = dstarray.sdtcClass; + if(sdtClass.arrayOfType == null) + throw new InvalidCastException("only do arrays not " + sdtClass.longName.val); + + /* + * We get a type cast error from mono if they didn't give us a character array. + * But if it is ok, copy from the string to the character array. + */ + char[] dstarr = (char[])dstarray.instVars.iarObjects[0]; + for(int i = 0; i < count; i++) + dstarr[i + dststart] = srcstr[i + srcstart]; + } + + /** + * @brief Implement osParseJSON() so we return an array to the script. + * No coherent example of its use in scripts on web found. + * see http://www.json.org/ for more details on JSON + */ + private static LSL_List nullList = new LSL_List(new object[0]); + public new XMR_Array osParseJSON(string json) + { + XMR_Array dict = new XMR_Array(this); + int idx = ParseJSON(dict, nullList, json, 0); + while(idx < json.Length) + { + if(json[idx] > ' ') + throw new Exception("left-over json " + json); + idx++; + } + return dict; + } + + private static int ParseJSON(XMR_Array dict, LSL_List keys, string json, int idx) + { + char c; + + while((c = json[idx++]) <= ' ') + { + } + switch(c) + { + + // '{' ':' [ ',' ':' ... ] '}' + 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; + + + // '[' [ ',' ... ] ']' + 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; + + + // '"''"' + 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(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 + // Mono ex.StackTrace: + // at OpenSim.Region.ScriptEngine.YEngine.TypeCast.ObjectToInteger (System.Object x) [0x0005e] in /home/kunta/opensim-0.9/addon-modules/YEngine/Module/MMRScriptTypeCast.cs:750 + // at (wrapper dynamic-method) System.Object:default state_entry (OpenSim.Region.ScriptEngine.YEngine.XMRInstAbstract) [0x00196] + + // Microsoft ex.StackTrace: + // at OpenSim.Region.ScriptEngine.YEngine.TypeCast.ObjectToInteger(Object x) in C:\Users\mrieker\opensim-0.9-source\addon-modules\YEngine\Module\MMRScriptTypeCast.cs:line 750 + // at default state_entry (XMRInstAbstract ) + public string XMRExceptionStackString(Exception ex) + { + string stwhole = ex.StackTrace; + string[] stlines = stwhole.Split(new char[] { '\n' }); + StringBuilder sb = new StringBuilder(); + foreach(string st in stlines) + { + string stline = st.Trim(); + if(stline == "") + continue; + + // strip 'at' off the front of line + if(stline.StartsWith("at ")) + { + stline = stline.Substring(3); + } + + // strip '(wrapper ...' off front of line + if(stline.StartsWith("(wrapper dynamic-method) System.Object:")) + { + stline = stline.Substring(39); + } + + // strip the (systemargtypes...) from our dynamic method names cuz it's messy + // 'default state_entry (XMRInstAbstract )' + // => 'default state_entry' + // 'CallSomethingThatThrows(string) (OpenSim.Region.ScriptEngine.YEngine.XMRInstance,string)' + // => 'CallSomethingThatThrows(string)' + int kwin = stline.IndexOf(" in "); + int br0x = stline.IndexOf(" [0x"); + int pastCloseParen = stline.Length; + if((kwin >= 0) && (br0x >= 0)) + pastCloseParen = Math.Min(kwin, br0x); + else if(kwin >= 0) + pastCloseParen = kwin; + else if(br0x >= 0) + pastCloseParen = br0x; + else + pastCloseParen = stline.Length; + int endFuncName = pastCloseParen; + while(endFuncName > 0) + { + if(stline[--endFuncName] == '(') + break; + } + while(endFuncName > 0) + { + if(stline[endFuncName - 1] != ' ') + break; + --endFuncName; + } + string funcName = stline.Substring(0, endFuncName); + KeyValuePair[] srcLocs; + if(m_ObjCode.scriptSrcLocss.TryGetValue(funcName, out srcLocs)) + { + stline = stline.Substring(0, endFuncName) + stline.Substring(pastCloseParen); + kwin = stline.IndexOf(" in "); + br0x = stline.IndexOf(" [0x"); + } + + // keyword 'in' is just before filename:linenumber that goes to end of line + // trim up the corresponding filename (ie, remove useless path info) + if(kwin >= 0) + { + int begfn = kwin + 4; + int slash = begfn; + for(int i = begfn; i < stline.Length; i++) + { + char c = stline[i]; + if((c == '/') || (c == '\\')) + slash = i + 1; + } + stline = stline.Substring(0, begfn) + stline.Substring(slash); + } + else if(srcLocs != null) + { + + // no filename:linenumber info, try to convert IL offset + if(br0x >= 0) + { + try + { + int begiloffs = br0x + 4; + int endiloffs = stline.IndexOf("]", begiloffs); + int iloffset = int.Parse(stline.Substring(begiloffs, endiloffs - begiloffs), + System.Globalization.NumberStyles.HexNumber); + + int srcLocIdx; + int srcLocLen = srcLocs.Length; + for(srcLocIdx = 0; ++srcLocIdx < srcLocLen;) + { + if(iloffset < srcLocs[srcLocIdx].Key) + break; + } + ScriptSrcLoc srcLoc = srcLocs[--srcLocIdx].Value; + + stline = stline.Substring(0, br0x) + " <" + + srcLoc.file + '(' + srcLoc.line + ',' + srcLoc.posn + ")>"; + } + catch + { + } + } + } + + // put edited line in output string + if(sb.Length > 0) + sb.AppendLine(); + sb.Append(" at "); + sb.Append(stline); + } + 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(); + this.migrateOutLists = new Dictionary(); + 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 migrateOutObjects; + private Dictionary 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 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(); + 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 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 w = mir.ReadDouble(); + return new LSL_Rotation(x, y, z, w); + } + 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; + } + } + + // 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() 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 + { + } +} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs similarity index 62% rename from OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs index a24036a546..fb5c75ea26 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs @@ -42,13 +42,13 @@ 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 +namespace OpenSim.Region.ScriptEngine.Yengine { /****************************************************\ * This file contains routines called by scripts. * \****************************************************/ - public class XMRLSL_Api : LSL_Api + public class XMRLSL_Api: LSL_Api { public AsyncCommandManager acm; private XMRInstance inst; @@ -83,38 +83,38 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * -2: no av granted perms * -3: av not in region */ -/* engines should not have own API - public int xmrSeatAvatar (bool owner) - { - // Get avatar to be seated and make sure they have given us ANIMATION permission + /* engines should not have own API + 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; + 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; } - 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 @@ -125,80 +125,80 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @param landmark = inventory name or UUID of a landmark object * @param lookat = looking direction after teleport */ -/* engines should not have own API - 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"); + /* engines should not have own API + 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"); + 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"); + // 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); } - } 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 */ -/* engines should not have own API - public string xmrSetParcelMusicURLGroup (string newurl) - { - string groupname = m_ScriptEngine.Config.GetString ("SetParcelMusicURLGroup", ""); - if (groupname == "") throw new ApplicationException ("no SetParcelMusicURLGroup config param set"); + /* engines should not have own API + 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 (); - if (igm == null) throw new ApplicationException ("no GroupsModule loaded"); + IGroupsModule igm = World.RequestModuleInterface (); + if (igm == null) throw new ApplicationException ("no GroupsModule loaded"); - GroupRecord grouprec = igm.GetGroupRecord (groupname); - if (grouprec == null) throw new ApplicationException ("no such group " + groupname); + 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); + 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; - } -*/ + 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 @@ -222,9 +222,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public DetectParams GetDetectParams(int number) { DetectParams dp = null; - if ((number >= 0) && (m_DetectParams != null) && (number < m_DetectParams.Length)) { + if((number >= 0) && (m_DetectParams != null) && (number < m_DetectParams.Length)) dp = m_DetectParams[number]; - } + return dp; } @@ -236,7 +236,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public void Die() { // llDie doesn't work in attachments! - if (m_Part.ParentGroup.IsAttachment || m_DetachQuantum > 0) + if(m_Part.ParentGroup.IsAttachment || m_DetachQuantum > 0) return; throw new ScriptDieException(); @@ -247,8 +247,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ public void Sleep(int ms) { - lock (m_QueueLock) { - + lock(m_QueueLock) + { /* * Say how long to sleep. */ @@ -306,12 +306,12 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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]); + private static LSL_List emptyList = new LSL_List(new object[0]); - public override LSL_List xmrEventDequeue (double timeout, int returnMask1, int returnMask2, + public override LSL_List xmrEventDequeue(double timeout, int returnMask1, int returnMask2, int backgroundMask1, int backgroundMask2) { - DateTime sleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds (timeout * 1000.0); + DateTime sleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds(timeout * 1000.0); EventParams evt = null; int callNo, evc2; int evc1 = 0; @@ -324,45 +324,47 @@ namespace OpenSim.Region.ScriptEngine.XMREngine callNo = -1; try { - if (callMode == CallMode_NORMAL) goto findevent; + if(callMode == CallMode_NORMAL) + goto findevent; /* * Stack frame is being restored as saved via CheckRun...(). * Restore necessary values then jump to __call label to resume processing. */ - sv = RestoreStackFrame ("xmrEventDequeue", out callNo); - sleepUntil = DateTime.Parse ((string)sv[0]); + 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) + mask1 = (int)sv[3]; + mask2 = (int)sv[4]; + switch(callNo) { - case 0: goto __call0; + 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; - } + { + 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); + 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) + 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); + 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)) || + if((((uint)evc1 < (uint)32) && (((mask1 >> evc1) & 1) != 0)) || (((uint)evc2 < (uint)32) && (((mask2 >> evc2) & 1) != 0))) goto remfromq; } @@ -373,51 +375,50 @@ namespace OpenSim.Region.ScriptEngine.XMREngine m_SleepUntil = sleepUntil; m_SleepEventMask1 = mask1; m_SleepEventMask2 = mask2; - Monitor.Exit (m_QueueLock); + Monitor.Exit(m_QueueLock); suspendOnCheckRunTemp = true; callNo = 0; - __call0: - CheckRunQuick (); + __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] --; + remfromq: + m_EventQueue.Remove(lln); + if((uint)evc1 < (uint)m_EventCounts.Length) + m_EventCounts[evc1]--; - Monitor.Exit (m_QueueLock); - m_InstEHEvent ++; + Monitor.Exit(m_QueueLock); + m_InstEHEvent++; /* * See if returnable or background event. */ - if ((((uint)evc1 < (uint)32) && (((returnMask1 >> evc1) & 1) != 0)) || + 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]; + object[] plist = new object[plen + 1]; plist[0] = (LSL_Integer)evc1; - for (int i = 0; i < plen;) + for(int i = 0; i < plen;) { object ob = evt.Params[i]; - if (ob is int) + if(ob is int) ob = (LSL_Integer)(int)ob; - else if (ob is double) + else if(ob is double) ob = (LSL_Float)(double)ob; - else if (ob is string) + else if(ob is string) ob = (LSL_String)(string)ob; plist[++i] = ob; } m_DetectParams = evt.DetectParams; - return new LSL_List (plist); + return new LSL_List(plist); } /* @@ -425,35 +426,35 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * then check event queue again. */ callNo = 1; - __call1: - ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode,evc1]; - if (seh == null) + __call1: + ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode, evc1]; + if(seh == null) goto checktmo; - DetectParams[] saveDetParams = this.m_DetectParams; - object[] saveEHArgs = this.ehArgs; + 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; + this.ehArgs = evt.Params; + this.eventCode = evc; try { - seh (this); + seh(this); } finally { - m_DetectParams = saveDetParams; - ehArgs = saveEHArgs; - eventCode = saveEventCode; + 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) + checktmo: + if(DateTime.UtcNow < sleepUntil) goto findevent; /* @@ -463,7 +464,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine } finally { - if (callMode != CallMode_NORMAL) + if(callMode != CallMode_NORMAL) { /* @@ -471,17 +472,17 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * Save everything we need at the __call labels so we can restore it * when we need to. */ - sv = CaptureStackFrame ("xmrEventDequeue", callNo, 9); - sv[0] = sleepUntil.ToString (); // needed at __call0,__call1 + 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) + 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[7] = DetPrmsToObjArr(evt.DetectParams); // needed at __call1 sv[8] = evt.Params; // needed at __call1 } } @@ -495,16 +496,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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) + public override void xmrEventEnqueue(LSL_List ev) { object[] data = ev.Data; - ScriptEventCode evc = (ScriptEventCode)ListInt (data[0]); + ScriptEventCode evc = (ScriptEventCode)ListInt(data[0]); int nargs = data.Length - 1; object[] args = new object[nargs]; - Array.Copy (data, 1, args, 0, nargs); + Array.Copy(data, 1, args, 0, nargs); - PostEvent (new EventParams (evc.ToString (), args, m_DetectParams)); + PostEvent(new EventParams(evc.ToString(), args, m_DetectParams)); } /** @@ -513,19 +514,19 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ private const int saveDPVer = 1; - public override LSL_List xmrEventSaveDets () + public override LSL_List xmrEventSaveDets() { - object[] obs = DetPrmsToObjArr (m_DetectParams); - return new LSL_List (obs); + object[] obs = DetPrmsToObjArr(m_DetectParams); + return new LSL_List(obs); } - private static object[] DetPrmsToObjArr (DetectParams[] dps) + private static object[] DetPrmsToObjArr(DetectParams[] dps) { int len = dps.Length; - object[] obs = new object[len*16+1]; + object[] obs = new object[len * 16 + 1]; int j = 0; obs[j++] = (LSL_Integer)saveDPVer; - for (int i = 0; i < len; i ++) + for(int i = 0; i < len; i++) { DetectParams dp = dps[i]; obs[j++] = (LSL_String)dp.Key.ToString(); // UUID @@ -548,54 +549,52 @@ namespace OpenSim.Region.ScriptEngine.XMREngine return obs; } - /** * @brief Load current detect params from a list * @param dpList = as returned by xmrEventSaveDets() */ - public override void xmrEventLoadDets (LSL_List dpList) + public override void xmrEventLoadDets(LSL_List dpList) { - m_DetectParams = ObjArrToDetPrms (dpList.Data); + m_DetectParams = ObjArrToDetPrms(dpList.Data); } - private static DetectParams[] ObjArrToDetPrms (object[] objs) + 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"); + 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 ++) + for(int i = 0; i < len; i++) { - DetectParams dp = new DetectParams (); + 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++]; + 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 (); + 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++]); + 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; } @@ -623,21 +622,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /* * Clear out any old events from the queue. */ - lock (m_QueueLock) + lock(m_QueueLock) { m_EventQueue.Clear(); - for (int i = m_EventCounts.Length; -- i >= 0;) + for(int i = m_EventCounts.Length; --i >= 0;) m_EventCounts[i] = 0; } } - - /** - * @brief Script is calling xmrStackLeft(). - */ - public override int xmrStackLeft () - { - return microthread.StackLeft (); - } } /** @@ -646,12 +637,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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 { } + 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 { } + public class ScriptDieException: Exception, IXMRUncatchable + { + } } diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstCapture.cs similarity index 63% rename from OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMRInstCapture.cs index ed331082e6..e90d83bae4 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstCapture.cs @@ -26,7 +26,6 @@ */ using System; -using System.Threading; using System.IO; using System.Xml; using OpenSim.Region.ScriptEngine.Shared; @@ -41,7 +40,7 @@ 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 +namespace OpenSim.Region.ScriptEngine.Yengine { public partial class XMRInstance { @@ -54,7 +53,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Create an XML element that gives the current state of the script. - * + * * globalsandstackdump * m_Running * - // - // ... ... - // ... ... - // - // ... - // + // Save any events we have in the queue. + // + // + // ... ... + // ... ... + // + // ... + // XmlElement queuedEventsN = doc.CreateElement("", "EventQueue", ""); - lock (m_QueueLock) + lock(m_QueueLock) { - foreach (EventParams evt in m_EventQueue) + foreach(EventParams evt in m_EventQueue) { XmlElement singleEventN = doc.CreateElement("", "Event", ""); singleEventN.SetAttribute("Name", evt.EventName); @@ -154,26 +158,28 @@ namespace OpenSim.Region.ScriptEngine.XMREngine } } scriptStateN.AppendChild(queuedEventsN); - m_RunOnePhase = "GetExecutionState G"; CheckRunLockInvariants(true); + m_RunOnePhase = "GetExecutionState G"; + CheckRunLockInvariants(true); - // "Plugins" indicate enabled timers and listens, etc. - Object[] pluginData = + // "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); + m_RunOnePhase = "GetExecutionState H"; + CheckRunLockInvariants(true); - // Let script run again. + // 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. + // 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); @@ -185,116 +191,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @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) + private void MigrateOutEventHandler(Stream stream) { - moehexcep = null; - - // do all the work in the MigrateOutEventHandlerThread() method below - moehstream = stream; - - XMRScriptThread cst = m_Engine.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 - m_Engine.QueueToTrunk(this.MigrateOutEventHandlerThread); - - // 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; -// this.suspendOnCheckRunTemp = 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); - } - } + // Write script state out, frames and all, to the stream. + // Does not change script state. + stream.WriteByte(migrationVersion); + stream.WriteByte((byte)16); + this.MigrateOut(new BinaryWriter(stream)); } /** @@ -304,7 +210,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ private static void AppendXMLDetectArray(XmlDocument doc, XmlElement parent, DetectParams[] detect) { - foreach (DetectParams d in detect) + foreach(DetectParams d in detect) { XmlElement detectParamsN = GetXMLDetect(doc, d); parent.AppendChild(detectParamsN); @@ -367,7 +273,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ private static void AppendXMLObjectArray(XmlDocument doc, XmlNode parent, object[] array, string tag) { - foreach (object o in array) + foreach(object o in array) { XmlElement element = GetXMLObject(doc, o, tag); parent.AppendChild(element); @@ -385,7 +291,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine XmlAttribute typ = doc.CreateAttribute("", "type", ""); XmlElement n = doc.CreateElement("", tag, ""); - if (o is LSL_List) + if(o is LSL_List) { typ.Value = "list"; n.Attributes.Append(typ); diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs similarity index 53% rename from OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs index 7ae8c47243..68ec322061 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs @@ -41,7 +41,7 @@ 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.ScriptEngine.Yengine; using OpenSim.Region.Framework.Scenes; using log4net; @@ -53,7 +53,7 @@ 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 +namespace OpenSim.Region.ScriptEngine.Yengine { public partial class XMRInstance { @@ -65,78 +65,82 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Initializer, loads script in memory and all ready for running. - * @param engine = XMREngine instance this is part of + * @param engine = YEngine 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, + public void Initialize(Yengine engine, string scriptBasePath, int stackSize, int heapSize, ArrayList errors) { - if (stackSize < 16384) stackSize = 16384; - if (heapSize < 16384) heapSize = 16384; + if(stackSize < 16384) + stackSize = 16384; + if(heapSize < 16384) + heapSize = 16384; - // Save all call parameters in instance vars for easy access. - m_Engine = engine; + // Save all call parameters in instance vars for easy access. + m_Engine = engine; m_ScriptBasePath = scriptBasePath; - m_StackSize = stackSize; - m_HeapSize = heapSize; + m_StackSize = stackSize; + m_StackLeft = stackSize; + m_HeapSize = heapSize; m_CompilerErrors = errors; - m_StateFileName = GetStateFileName(scriptBasePath, m_ItemID); + m_StateFileName = GetStateFileName(scriptBasePath, m_ItemID); - // Not in any XMRInstQueue. + // 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. + // 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()) + foreach(string api in am.GetApis()) { - // Instantiate the API for this script instance. - if (api != "LSL") { + // Instantiate the API for this script instance. + if(api != "LSL") scriptApi = am.CreateApi(api); - } else { + else scriptApi = m_XMRLSLApi = new XMRLSL_Api(); - } - // Connect it up to the instance. - InitScriptApi (engine, api, scriptApi); + // 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. + // 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"); + 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. + // 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) + private void InitScriptApi(Yengine engine, string api, IScriptApi scriptApi) { - // Set up m_ApiManager_ = instance pointer. - engine.m_XMRInstanceApiCtxFieldInfos[api].SetValue (this, scriptApi); + // Set up m_ApiManager_ = instance pointer. + engine.m_XMRInstanceApiCtxFieldInfos[api].SetValue(this, scriptApi); - // Initialize the API instance. + // Initialize the API instance. scriptApi.Initialize(m_Engine, m_Part, m_Item); - this.InitApi (api, scriptApi); + 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 @@ -146,152 +150,151 @@ namespace OpenSim.Region.ScriptEngine.XMREngine 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). + // 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 == null) + m_SourceCode = String.Empty; + for(len = m_SourceCode.Length; len > 0; --len) { - if (m_SourceCode[len-1] > ' ') + if(m_SourceCode[len - 1] > ' ') break; } - for (i = 0; i < len; i ++) + for(i = 0; i < len; i++) { char c = m_SourceCode[i]; - if (c <= ' ') + if(c <= ' ') continue; - if (c != '/') + if(c != '/') break; - if ((i + 1 >= len) || (m_SourceCode[i+1] != '/')) + if((i + 1 >= len) || (m_SourceCode[i + 1] != '/')) break; - i = m_SourceCode.IndexOf ('\n', i); - if (i < 0) + i = m_SourceCode.IndexOf('\n', i); + if(i < 0) i = len - 1; } - if ((i >= len) || !m_Engine.m_UseSourceHashCode) + 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) + // 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 (); + // 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) + // 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;) + for(j = len; --j >= i;) { - if (sixbit.IndexOf (m_SourceCode[j]) < 0) + if(sixbit.IndexOf(m_SourceCode[j]) < 0) break; } - if (j < i) + if(j < i) { - m_ScriptObjCodeKey = m_SourceCode.Substring (i, len - 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) + // 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) + if(!m_CompiledScriptObjCode.TryGetValue(m_ScriptObjCodeKey, out objCode) || m_ForceRecomp) { - objCode = TryToCompile (); + objCode = TryToCompile(); compiledIt = true; } - // Loaded successfully, increment reference count. + // 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 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) + if(compiledIt) { m_CompiledScriptObjCode[m_ScriptObjCodeKey] = objCode; objCode.refCount = 0; } - objCode.refCount ++; + objCode.refCount++; - // Now set up to decrement ref count on dispose. + // 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. + { + + // 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 + // 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 (); + + // 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) + private static void ByteArrayToSixbitStr(StringBuilder sb, byte[] bytes) { int bit = 0; int val = 0; - foreach (byte b in bytes) + foreach(byte b in bytes) { val |= (int)((uint)b << bit); bit += 8; - while (bit >= 6) + while(bit >= 6) { - sb.Append (sixbit[val&63]); + sb.Append(sixbit[val & 63]); val >>= 6; - bit -= 6; + bit -= 6; } } - if (bit > 0) - sb.Append (sixbit[val&63]); + if(bit > 0) + sb.Append(sixbit[val & 63]); } - /* - * Try to create object code from source code - * If error, just throw exception - */ - private ScriptObjCode TryToCompile () + // 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(); + // 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"); + ScriptObjCode objCode = Compile(); + if(m_CompilerErrors.Count != 0) + throw new Exception("compilation errors"); - if (objCode == null) - throw new Exception ("compilation failed"); + if(objCode == null) + throw new Exception("compilation failed"); return objCode; } @@ -299,20 +302,20 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /* * Retrieve source from asset server. */ - private string FetchSource (string cameFrom) + 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); + m_log.Debug("[YEngine]: fetching source " + cameFrom); + if(!cameFrom.StartsWith("asset://")) + throw new Exception("unable to retrieve source from " + cameFrom); - string assetID = cameFrom.Substring (8); + string assetID = cameFrom.Substring(8); AssetBase asset = m_Engine.World.AssetService.Get(assetID); - if (asset == null) - throw new Exception ("source not found " + cameFrom); + 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); + string source = Encoding.UTF8.GetString(asset.Data); + if(EmptySource(source)) + throw new Exception("fetched source empty " + cameFrom); return source; } @@ -321,33 +324,29 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * Fill in script object initial contents. * Set the initial state to "default". */ - private void LoadObjCode () + private void LoadObjCode() { - // Script must leave this much stack remaining on calls to CheckRun(). - + // 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 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); + // Allocate global variable arrays. + this.glblVars.AllocVarArrays(m_ObjCode.glblSizes); - // Script can handle these event codes. + // 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 i = m_ObjCode.scriptEventHandlerTable.GetLength(0); --i >= 0;) { - for (int j = m_ObjCode.scriptEventHandlerTable.GetLength(1); -- j >= 0;) + for(int j = m_ObjCode.scriptEventHandlerTable.GetLength(1); --j >= 0;) { - if (m_ObjCode.scriptEventHandlerTable[i,j] != null) - { + 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 }); } /* @@ -360,9 +359,10 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ 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)) + // 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 @@ -371,14 +371,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine doGblInit = true; stateCode = 0; - PostEvent(new EventParams("state_entry", + PostEvent(new EventParams("state_entry", zeroObjectArray, zeroDetectParams)); } else { - FileStream fs = File.Open(m_StateFileName, - FileMode.Open, + FileStream fs = File.Open(m_StateFileName, + FileMode.Open, FileAccess.Read); StreamReader ss = new StreamReader(fs); string xml = ss.ReadToEnd(); @@ -390,48 +390,51 @@ namespace OpenSim.Region.ScriptEngine.XMREngine LoadScriptState(doc); } - // Post event(s) saying what caused the script to start. - if (m_PostOnRez) + /* + * Post event(s) saying what caused the script to start. + */ + if(m_PostOnRez) { PostEvent(new EventParams("on_rez", - new Object[] { m_StartParam }, + new Object[] { m_StartParam }, zeroDetectParams)); } - switch (m_StateSource) + switch(m_StateSource) { case StateSource.AttachedRez: -// PostEvent(new EventParams("attach", -// new object[] { m_Part.ParentGroup.AttachedAvatar.ToString() }, -// zeroDetectParams)); + // PostEvent(new EventParams("attach", + // new object[] { m_Part.ParentGroup.AttachedAvatar.ToString() }, + // zeroDetectParams)); break; case StateSource.PrimCrossing: PostEvent(new EventParams("changed", - sbcCR, + sbcCR, zeroDetectParams)); break; + case StateSource.Teleporting: PostEvent(new EventParams("changed", - sbcCR, + sbcCR, zeroDetectParams)); PostEvent(new EventParams("changed", - sbcCT, + sbcCT, zeroDetectParams)); break; case StateSource.RegionStart: PostEvent(new EventParams("changed", - sbcCRS, + 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 }; + 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 @@ -439,24 +442,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ private void ErrorHandler(Token token, string message) { - if (token != null) + if(token != null) { string srcloc = token.SrcLoc; - if (srcloc.StartsWith (m_CameFrom)) - srcloc = srcloc.Substring (m_CameFrom.Length); + if(srcloc.StartsWith(m_CameFrom)) + srcloc = srcloc.Substring(m_CameFrom.Length); m_CompilerErrors.Add(srcloc + " Error: " + message); } - else if (message != null) + 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 - * + * * ... * ... * @@ -476,25 +478,24 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // Everything we know is enclosed in ... XmlElement scriptStateN = (XmlElement)doc.SelectSingleNode("ScriptState"); - if (scriptStateN == null) + if(scriptStateN == null) throw new Exception("no tag"); string sen = scriptStateN.GetAttribute("Engine"); - if ((sen == null) || (sen != m_Engine.ScriptEngineName)) - throw new Exception(" missing Engine=\"XMREngine\" attribute"); - + if((sen == null) || (sen != m_Engine.ScriptEngineName)) + throw new Exception(" missing Engine=\"YEngine\" 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()) + if(assetID != m_Item.AssetID.ToString()) throw new Exception(" 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 (" SourceHash mismatch"); + string sourceHash = scriptStateN.GetAttribute("SourceHash"); + if((sourceHash == null) || (sourceHash != m_ObjCode.sourceHash)) + throw new Exception(" SourceHash mismatch"); // Get various attributes XmlElement runningN = (XmlElement)scriptStateN.SelectSingleNode("Running"); @@ -519,7 +520,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine Object[] pluginData = ExtractXMLObjectArray(pluginN, "plugin"); // Script's global variables and stack contents - XmlElement snapshotN = (XmlElement)scriptStateN.SelectSingleNode("Snapshot"); + XmlElement snapshotN = + (XmlElement)scriptStateN.SelectSingleNode("Snapshot"); Byte[] data = Convert.FromBase64String(snapshotN.InnerText); MemoryStream ms = new MemoryStream(); @@ -530,17 +532,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // Restore event queues, preserving any events that queued // whilst we were restoring the state - lock (m_QueueLock) + lock(m_QueueLock) { m_DetectParams = detParams; - foreach (EventParams evt in m_EventQueue) - eventQueue.AddLast (evt); + foreach(EventParams evt in m_EventQueue) + eventQueue.AddLast(evt); m_EventQueue = eventQueue; - for (int i = m_EventCounts.Length; -- i >= 0;) + for(int i = m_EventCounts.Length; --i >= 0;) m_EventCounts[i] = 0; - - foreach (EventParams evt in m_EventQueue) + foreach(EventParams evt in m_EventQueue) { ScriptEventCode eventCode = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName); @@ -566,17 +567,19 @@ namespace OpenSim.Region.ScriptEngine.XMREngine private LinkedList RestoreEventQueue(XmlNode eventsN) { LinkedList eventQueue = new LinkedList(); - if (eventsN != null) + if(eventsN != null) { XmlNodeList eventL = eventsN.SelectNodes("Event"); - foreach (XmlNode evnt in eventL) + foreach(XmlNode evnt in eventL) { - string name = ((XmlElement)evnt).GetAttribute("Name"); - object[] parms = ExtractXMLObjectArray(evnt, "param"); + 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; + if(parms == null) + parms = zeroObjectArray; + if(detects == null) + detects = zeroDetectParams; EventParams evt = new EventParams(name, parms, detects); eventQueue.AddLast(evt); @@ -596,38 +599,39 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ private DetectParams[] RestoreDetectParams(XmlNode detectedN) { - if (detectedN == null) return null; + if(detectedN == null) + return null; List detected = new List(); XmlNodeList detectL = detectedN.SelectNodes("DetectParams"); DetectParams detprm = new DetectParams(); - foreach (XmlNode detxml in detectL) + 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.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.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.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.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); + detprm.Rotation = new LSL_Types.Quaternion(detxml.Attributes.GetNamedItem("rotation").Value); detected.Add(detprm); detprm = new DetectParams(); } - catch (Exception e) + catch(Exception e) { - m_log.Warn("[XMREngine]: RestoreDetectParams bad XML: " + detxml.ToString()); - m_log.Warn("[XMREngine]: ... " + e.ToString()); + m_log.Warn("[YEngine]: RestoreDetectParams bad XML: " + detxml.ToString()); + m_log.Warn("[YEngine]: ... " + e.ToString()); } } @@ -646,8 +650,10 @@ namespace OpenSim.Region.ScriptEngine.XMREngine List olist = new List(); XmlNodeList itemL = parent.SelectNodes(tag); - foreach (XmlNode item in itemL) + foreach(XmlNode item in itemL) + { olist.Add(ExtractXMLObjectValue(item)); + } return olist.ToArray(); } @@ -656,10 +662,12 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { string itemType = item.Attributes.GetNamedItem("type").Value; - if (itemType == "list") + if(itemType == "list") + { return new LSL_List(ExtractXMLObjectArray(item, "item")); + } - if (itemType == "OpenMetaverse.UUID") + if(itemType == "OpenMetaverse.UUID") { UUID val = new UUID(); UUID.TryParse(item.InnerText, out val); @@ -667,15 +675,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine } Type itemT = Type.GetType(itemType); - if (itemT == null) + if(itemT == null) { Object[] args = new Object[] { item.InnerText }; string assembly = itemType + ", OpenSim.Region.ScriptEngine.Shared"; itemT = Type.GetType(assembly); - if (itemT == null) + if(itemT == null) + { return null; - + } return Activator.CreateInstance(itemT, args); } @@ -688,89 +697,27 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * Input: * stream = as generated by MigrateOutEventHandler() */ - private void MigrateInEventHandler (Stream stream) + private void MigrateInEventHandler(Stream stream) { - miehexcep = null; + int mv = stream.ReadByte(); + if(mv != migrationVersion) + throw new Exception("incoming migration version " + mv + " but accept only " + migrationVersion); - // do all the work in the MigrateInEventHandlerThread() method below - miehstream = stream; + stream.ReadByte(); // ignored - XMRScriptThread cst = m_Engine.CurrentScriptThread (); - if (cst != null) + /* + * 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. If it restores this.eventCode = + * None, the the script was idle. + */ + lock(m_RunLock) { - // in case we are getting called inside some LSL Api function - MigrateInEventHandlerThread (); - } - else - { - // some other thread, do migration via a script thread - m_Engine.QueueToTrunk(this.MigrateInEventHandlerThread); + BinaryReader br = new BinaryReader(stream); + this.MigrateIn(br); - // 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); - } + m_RunOnePhase = "MigrateInEventHandler finished"; + CheckRunLockInvariants(true); } } } diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstMain.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstMain.cs similarity index 82% rename from OpenSim/Region/ScriptEngine/XMREngine/XMRInstMain.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMRInstMain.cs index 3573be349d..9eb05f763e 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstMain.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstMain.cs @@ -41,7 +41,7 @@ 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.ScriptEngine.Yengine; using OpenSim.Region.Framework.Scenes; using log4net; @@ -55,7 +55,7 @@ using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; // This class exists in the main app domain // -namespace OpenSim.Region.ScriptEngine.XMREngine +namespace OpenSim.Region.ScriptEngine.Yengine { /** * @brief Which queue it is in as far as running is concerned, @@ -75,7 +75,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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 { + 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 @@ -89,7 +90,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine DISPOSED // has been disposed } - public partial class XMRInstance : XMRInstAbstract, IDisposable + public partial class XMRInstance: XMRInstAbstract, IDisposable { /******************************************************************\ * This module contains the instance variables for XMRInstance. * @@ -103,44 +104,45 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public XMRInstance m_NextInst; // used by XMRInstQueue + public XMRInstance m_PrevInst; + // 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 m_CompiledScriptObjCode = new Dictionary(); + private static Dictionary m_CompiledScriptObjCode = new Dictionary(); - public XMRInstance m_NextInst; // used by XMRInstQueue - public XMRInstance m_PrevInst; - public XMRInstState m_IState; + 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; + 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 Yengine m_Engine = null; private string m_ScriptBasePath; private string m_StateFileName; - public string m_SourceCode; - public bool m_PostOnRez; + 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; + 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; + 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) - public double m_SliceStart = 0; // when did current exec start + 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) + public double m_SliceStart = 0; // when did current exec start // If code needs to have both m_QueueLock and m_RunLock, // be sure to lock m_RunLock first then m_QueueLock, as @@ -156,7 +158,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public bool m_Running = true; // queue of events that haven't been acted upon yet - public LinkedList m_EventQueue = new LinkedList (); + public LinkedList m_EventQueue = new LinkedList(); // number of events of each code currently in m_EventQueue. private int[] m_EventCounts = new int[(int)ScriptEventCode.Size]; @@ -175,20 +177,6 @@ namespace OpenSim.Region.ScriptEngine.XMREngine 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. */ diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstMisc.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstMisc.cs similarity index 63% rename from OpenSim/Region/ScriptEngine/XMREngine/XMRInstMisc.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMRInstMisc.cs index 6ff486af49..8f020ce3bd 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstMisc.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstMisc.cs @@ -42,7 +42,7 @@ 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.ScriptEngine.Yengine; using OpenSim.Region.Framework.Scenes; using log4net; @@ -56,7 +56,7 @@ using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; // This class exists in the main app domain // -namespace OpenSim.Region.ScriptEngine.XMREngine +namespace OpenSim.Region.ScriptEngine.Yengine { public partial class XMRInstance { @@ -79,58 +79,49 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ 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) + lock(m_RunLock) { - xmrTrapRegionCrossing (0); - m_Part.RemoveScriptEvents(m_ItemID); - AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID); - m_Part = null; + if(m_Part != null) + { + 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 (); + DecObjCodeRefCount(); } - private void DecObjCodeRefCount () + private void DecObjCodeRefCount() { - if (m_ObjCode != null) { - lock (m_CompileLock) { + if(m_ObjCode != null) + { + lock(m_CompileLock) + { ScriptObjCode objCode; - if (m_CompiledScriptObjCode.TryGetValue (m_ScriptObjCodeKey, out objCode) && + if(m_CompiledScriptObjCode.TryGetValue(m_ScriptObjCodeKey, out objCode) && (objCode == m_ObjCode) && - (-- objCode.refCount == 0)) { - m_CompiledScriptObjCode.Remove (m_ScriptObjCodeKey); + (--objCode.refCount == 0)) + { + m_CompiledScriptObjCode.Remove(m_ScriptObjCodeKey); } } m_ObjCode = null; } } - public void Verbose (string format, params object[] args) + public void Verbose(string format, params object[] args) { - if (m_Engine.m_Verbose) m_log.DebugFormat (format, args); + if(m_Engine.m_Verbose) + m_log.DebugFormat(format, args); } // Called by 'xmr top' console command @@ -138,37 +129,39 @@ namespace OpenSim.Region.ScriptEngine.XMREngine // 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()); - } + 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) { + 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_ItemID = " + m_ItemID + " (.state file)"); sb.AppendLine(" m_Item.AssetID = " + m_Item.AssetID); - sb.AppendLine(" m_Part.WorldPosition = " + m_Part.GetWorldPosition ()); + 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); @@ -185,28 +178,28 @@ namespace OpenSim.Region.ScriptEngine.XMREngine sb.AppendLine(" suspOnCkRunHold = " + suspendOnCheckRunHold); sb.AppendLine(" suspOnCkRunTemp = " + suspendOnCheckRunTemp); sb.AppendLine(" m_CheckRunPhase = " + m_CheckRunPhase); - sb.AppendLine(" heapUsed/Limit = " + xmrHeapUsed () + "/" + heapLimit); + 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) + lock(m_QueueLock) { sb.AppendLine(" m_Running = " + m_Running); - foreach (EventParams evt in m_EventQueue) + 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), + } + 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); } @@ -218,16 +211,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ public int GetStateEventFlags(int stateCode) { - if ((stateCode < 0) || + if((stateCode < 0) || (stateCode >= m_ObjCode.scriptEventHandlerTable.GetLength(0))) { return 0; } int code = 0; - for (int i = 0 ; i < 32; i ++) + for(int i = 0; i < 32; i++) { - if (m_ObjCode.scriptEventHandlerTable[stateCode, i] != null) + if(m_ObjCode.scriptEventHandlerTable[stateCode, i] != null) { code |= 1 << i; } @@ -239,41 +232,47 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Get the .state file name. */ - public static string GetStateFileName (string scriptBasePath, UUID itemID) + public static string GetStateFileName(string scriptBasePath, UUID itemID) { - return GetScriptFileName (scriptBasePath, itemID.ToString() + ".state"); + return GetScriptFileName(scriptBasePath, itemID.ToString() + ".state"); } - public string GetScriptFileName (string filename) + public string GetScriptFileName(string filename) { - return GetScriptFileName (m_ScriptBasePath, filename); + return GetScriptFileName(m_ScriptBasePath, filename); } - public static string GetScriptFileName (string scriptBasePath, string 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); + 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); + // string subdir = filename.Substring (0, 2); + // filename = filename.Substring (2); + string subdir = filename.Substring(0, 1); + filename = filename.Substring(1); + 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); + if(File.Exists(oldPath)) + { + if(File.Exists(newPath)) + { + File.Delete(oldPath); + } + else + { + File.Move(oldPath, newPath); } } @@ -286,12 +285,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Decode state code (int) to state name (string). */ - public string GetStateName (int stateCode) + public string GetStateName(int stateCode) { - try { + try + { return m_ObjCode.stateNames[stateCode]; - } catch { - return stateCode.ToString (); + } + catch + { + return stateCode.ToString(); } } @@ -300,42 +302,66 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ public int StartParam { - get { return m_StartParam; } - set { m_StartParam = value; } + get + { + return m_StartParam; + } + set + { + m_StartParam = value; + } } public SceneObjectPart SceneObject { - get { return m_Part; } + get + { + return m_Part; + } } public DetectParams[] DetectParams { - get { return m_DetectParams; } - set { m_DetectParams = value; } + get + { + return m_DetectParams; + } + set + { + m_DetectParams = value; + } } public UUID ItemID { - get { return m_ItemID; } + get + { + return m_ItemID; + } } public UUID AssetID { - get { return m_Item.AssetID; } + get + { + return m_Item.AssetID; + } } public bool Running { - get { return m_Running; } + get + { + return m_Running; + } set { - lock (m_QueueLock) + lock(m_QueueLock) { m_Running = value; - if (!value) + if(!value) { - EmptyEventQueues (); + EmptyEventQueues(); } } } @@ -345,26 +371,28 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @brief Empty out the event queues. * Assumes caller has the m_QueueLock locked. */ - public void EmptyEventQueues () + public void EmptyEventQueues() { m_EventQueue.Clear(); - for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0; + 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) + public static OpenMetaverse.Vector3 LSLVec2OMVec(LSL_Vector lslVec) { - return new OpenMetaverse.Vector3 ((float)lslVec.x, (float)lslVec.y, (float)lslVec.z); + 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) + public static int ListInt(object element) { - if (element is LSL_Integer) { + if(element is LSL_Integer) + { return (int)(LSL_Integer)element; } return (int)element; @@ -373,9 +401,10 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Extract a string from an element of an LSL_List. */ - public static string ListStr (object element) + public static string ListStr(object element) { - if (element is LSL_String) { + if(element is LSL_String) + { return (string)(LSL_String)element; } return (string)element; diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstQueue.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstQueue.cs similarity index 82% rename from OpenSim/Region/ScriptEngine/XMREngine/XMRInstQueue.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMRInstQueue.cs index 41acac86fb..549fab5065 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstQueue.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstQueue.cs @@ -27,7 +27,7 @@ using System; -namespace OpenSim.Region.ScriptEngine.XMREngine +namespace OpenSim.Region.ScriptEngine.Yengine { /** * @brief Implements a queue of XMRInstance's. @@ -47,15 +47,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ public void InsertHead(XMRInstance inst) { - if ((inst.m_PrevInst != inst) || (inst.m_NextInst != 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) { + if((inst.m_NextInst = m_Head) == null) m_Tail = inst; - } else { + else m_Head.m_PrevInst = inst; - } + m_Head = inst; } @@ -65,15 +65,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ public void InsertTail(XMRInstance inst) { - if ((inst.m_PrevInst != inst) || (inst.m_NextInst != 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) { + if((inst.m_PrevInst = m_Tail) == null) m_Head = inst; - } else { + else m_Tail.m_NextInst = inst; - } + m_Tail = inst; } @@ -84,19 +84,19 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ public void InsertBefore(XMRInstance inst, XMRInstance after) { - if ((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) { + if((inst.m_PrevInst != inst) || (inst.m_NextInst != inst)) throw new Exception("already in list"); - } - if (after == null) { + + if(after == null) InsertTail(inst); - } else { + else + { inst.m_NextInst = after; inst.m_PrevInst = after.m_PrevInst; - if (inst.m_PrevInst == null) { + if(inst.m_PrevInst == null) m_Head = inst; - } else { + else inst.m_PrevInst.m_NextInst = inst; - } after.m_PrevInst = inst; } } @@ -119,12 +119,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public XMRInstance RemoveHead() { XMRInstance inst = m_Head; - if (inst != null) { - if ((m_Head = inst.m_NextInst) == null) { + if(inst != null) + { + if((m_Head = inst.m_NextInst) == null) m_Tail = null; - } else { + else m_Head.m_PrevInst = null; - } + inst.m_NextInst = inst; inst.m_PrevInst = inst; } @@ -139,12 +140,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine public XMRInstance RemoveTail() { XMRInstance inst = m_Tail; - if (inst != null) { - if ((m_Tail = inst.m_PrevInst) == null) { + if(inst != null) + { + if((m_Tail = inst.m_PrevInst) == null) m_Head = null; - } else { + else m_Tail.m_NextInst = null; - } + inst.m_NextInst = inst; inst.m_PrevInst = inst; } @@ -160,25 +162,29 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { XMRInstance next = inst.m_NextInst; XMRInstance prev = inst.m_PrevInst; - if ((prev == inst) || (next == inst)) { + if((prev == inst) || (next == inst)) throw new Exception("not in a list"); - } - if (next == null) { - if (m_Tail != inst) { + + if(next == null) + { + if(m_Tail != inst) throw new Exception("not in this list"); - } + m_Tail = prev; - } else { + } + else next.m_PrevInst = prev; - } - if (prev == null) { - if (m_Head != inst) { + + if(prev == null) + { + if(m_Head != inst) throw new Exception("not in this list"); - } + m_Head = next; - } else { - prev.m_NextInst = next; } + else + prev.m_NextInst = next; + inst.m_NextInst = inst; inst.m_PrevInst = inst; } diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs similarity index 70% rename from OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs index 9654b01941..8603fbffef 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs @@ -32,11 +32,9 @@ 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; @@ -48,7 +46,7 @@ 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 +namespace OpenSim.Region.ScriptEngine.Yengine { public partial class XMRInstance { @@ -65,15 +63,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ public void PostEvent(EventParams evt) { - ScriptEventCode evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), + 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 wakeIt = false; + lock(m_QueueLock) { bool construct = (m_IState == XMRInstState.CONSTRUCT); @@ -82,49 +80,50 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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 + 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) { + 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) + if((uint)evc < (uint)m_EventCounts.Length) + { + if(evc == ScriptEventCode.timer) { - return; + if(m_EventCounts[(int)evc] >= 1) + return; } - m_EventCounts[(int)evc] ++; + else if(m_EventCounts[(int)evc] >= MAXEVENTQUEUE) + return; + + m_EventCounts[(int)evc]++; } /* * Put event on end of instance's event queue. */ LinkedListNode lln = new LinkedListNode(evt); - switch (evc) { - + 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: { + case ScriptEventCode.state_entry: m_EventQueue.AddFirst(lln); break; - } /* * The attach event sneaks to the front of the queue. @@ -134,22 +133,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * before attach(NULL_KEY) is executed. */ case ScriptEventCode.attach: - { - if (evt.Params[0].ToString() == UUID.Zero.ToString()) + if(evt.Params[0].ToString() == UUID.Zero.ToString()) { LinkedListNode lln2 = null; - for (lln2 = m_EventQueue.First; lln2 != null; lln2 = lln2.Next) + for(lln2 = m_EventQueue.First; lln2 != null; lln2 = lln2.Next) { EventParams evt2 = lln2.Value; - ScriptEventCode evc2 = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), + ScriptEventCode evc2 = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt2.EventName); - if ((evc2 != ScriptEventCode.state_entry) && - (evc2 != ScriptEventCode.attach)) break; + if((evc2 != ScriptEventCode.state_entry) && + (evc2 != ScriptEventCode.attach)) + break; } - if (lln2 == null) + 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 @@ -160,17 +160,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine } else m_EventQueue.AddLast(lln); + break; - } /* * All others just go on end in the order queued. */ default: - { m_EventQueue.AddLast(lln); break; - } } /* @@ -180,7 +178,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * to do the same thing right now. * Dont' flag it if it's still suspended! */ - if ((m_IState == XMRInstState.IDLE) && !m_Suspended) + if((m_IState == XMRInstState.IDLE) && !m_Suspended) { m_IState = XMRInstState.ONSTARTQ; startIt = true; @@ -190,15 +188,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * If instance is sleeping (ie, possibly in xmrEventDequeue), * wake it up if event is in the mask. */ - if ((m_SleepUntil > DateTime.UtcNow) && !m_Suspended) + 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))) - { + if((((uint)evc1 < (uint)32) && (((m_SleepEventMask1 >> evc1) & 1) != 0)) || + (((uint)evc2 < (uint)32) && (((m_SleepEventMask2 >> evc2) & 1) != 0))) wakeIt = true; - } } } @@ -206,13 +202,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * If transitioned from IDLE->ONSTARTQ, actually go insert it * on m_StartQueue and give the RunScriptThread() a wake-up. */ - if (startIt) + if(startIt) m_Engine.QueueToStart(this); /* * Likewise, if the event mask triggered a wake, wake it up. */ - if (wakeIt) + if(wakeIt) { m_SleepUntil = DateTime.MinValue; m_Engine.WakeFromSleep(this); @@ -234,7 +230,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * up. */ m_RunOnePhase = "check m_SleepUntil"; - if (m_SleepUntil > now) + if(m_SleepUntil > now) { m_RunOnePhase = "return is sleeping"; return XMRInstState.ONSLEEPQ; @@ -244,7 +240,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * Also, someone may have called Suspend(). */ m_RunOnePhase = "check m_SuspendCount"; - if (m_SuspendCount > 0) + if(m_SuspendCount > 0) { m_RunOnePhase = "return is suspended"; return XMRInstState.SUSPENDED; @@ -256,7 +252,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * back right away, delay a bit so we don't get in infinite loop. */ m_RunOnePhase = "lock m_RunLock"; - if (!Monitor.TryEnter (m_RunLock)) + if(!Monitor.TryEnter(m_RunLock)) { m_SleepUntil = now.AddMilliseconds(3); m_RunOnePhase = "return was locked"; @@ -269,23 +265,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine Exception e = null; /* - * Maybe we have been disposed. + * Maybe it has been Disposed() */ - m_RunOnePhase = "check disposed"; - if (microthread == null) + if(m_Part == null) { - m_RunOnePhase = "return disposed"; + m_RunOnePhase = "runone saw it disposed"; return XMRInstState.DISPOSED; } /* * Do some more of the last event if it didn't finish. */ - else if (eventCode != ScriptEventCode.None) + if(this.eventCode != ScriptEventCode.None) { - lock (m_QueueLock) + lock(m_QueueLock) { - if (m_DetachQuantum > 0 && --m_DetachQuantum == 0) + if(m_DetachQuantum > 0 && --m_DetachQuantum == 0) { m_Suspended = true; m_DetachReady.Set(); @@ -296,10 +291,10 @@ namespace OpenSim.Region.ScriptEngine.XMREngine } m_RunOnePhase = "resume old event handler"; - m_LastRanAt = now; - m_InstEHSlice ++; + m_LastRanAt = now; + m_InstEHSlice++; callMode = CallMode_NORMAL; - e = microthread.ResumeEx (); + e = ResumeEx(); } /* @@ -312,7 +307,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine EventParams evt = null; ScriptEventCode evc = ScriptEventCode.None; - lock (m_QueueLock) + lock(m_QueueLock) { /* We can't get here unless the script has been resumed @@ -324,7 +319,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * out and may be improved in the future. */ - if (m_Suspended) + if(m_Suspended) { m_RunOnePhase = "m_Suspended is set"; CheckRunLockInvariants(true); @@ -332,14 +327,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine } m_RunOnePhase = "dequeue event"; - if (m_EventQueue.First != null) + if(m_EventQueue.First != null) { evt = m_EventQueue.First.Value; - evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), - evt.EventName); - if (m_DetachQuantum > 0) + if(m_DetachQuantum > 0) { - if (evc != ScriptEventCode.attach) + evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), + evt.EventName); + if(evc != ScriptEventCode.attach) { /* * This is the case where the attach event @@ -355,17 +350,19 @@ namespace OpenSim.Region.ScriptEngine.XMREngine } } m_EventQueue.RemoveFirst(); - if (evc >= 0) - m_EventCounts[(int)evc] --; + 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(evt == null) { - if (m_DetachQuantum > 0) + if(m_DetachQuantum > 0) { /* * This will happen if the attach event has run @@ -385,19 +382,19 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * Dequeued an event, so start it going until it either * finishes or it calls CheckRun(). */ - m_RunOnePhase = "start event handler"; + m_RunOnePhase = "start event handler"; m_DetectParams = evt.DetectParams; - m_LastRanAt = now; - m_InstEHEvent ++; - e = StartEventHandler (evc, evt.Params); + m_LastRanAt = now; + m_InstEHEvent++; + e = StartEventHandler(evc, evt.Params); } m_RunOnePhase = "done running"; - m_CPUTime += DateTime.UtcNow.Subtract(now).TotalMilliseconds; + m_CPUTime += DateTime.UtcNow.Subtract(now).TotalMilliseconds; /* * Maybe it puqued. */ - if (e != null) + if(e != null) { m_RunOnePhase = "handling exception " + e.Message; HandleScriptException(e); @@ -409,10 +406,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /* * If event handler completed, get rid of detect params. */ - if (this.eventCode == ScriptEventCode.None) - { + if(this.eventCode == ScriptEventCode.None) m_DetectParams = null; - } + } finally { @@ -432,33 +428,30 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @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 = ""; + private bool lastActive = false; + 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. + * If not executing any event handler, there shouldn't be any saved stack frames. + * If executing an event handler, there should be some saved stack frames. */ - 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; + bool active = (stackFrames != null); + ScriptEventCode ec = this.eventCode; + if(((ec == ScriptEventCode.None) && active) || + ((ec != ScriptEventCode.None) && !active)) + { + 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; } /* @@ -475,38 +468,34 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * from ehArgs[] and will throw an array bounds or cast exception * if it can't. */ - private Exception StartEventHandler (ScriptEventCode eventCode, object[] ehArgs) + 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"); + 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 ((eventCode >= 0) && (m_ObjCode.scriptEventHandlerTable[this.stateCode,(int)eventCode] == null)) + 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 ()); + if(this.eventCode != ScriptEventCode.None) + throw new Exception("still processing event " + this.eventCode.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.ehArgs = ehArgs; /* * This calls ScriptUThread.Main() directly, and returns when Main() [indirectly] @@ -515,9 +504,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * without doing any stack frame restores first. */ this.stackFrames = null; - Exception e; - e = microthread.StartEx (); - return e; + return StartEx(); } @@ -533,17 +520,17 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ eventCode = ScriptEventCode.None; - if (e is ScriptDeleteException) + 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); + Verbose("[YEngine]: script self-delete {0}", m_ItemID); m_Part.Inventory.RemoveInventoryItem(m_ItemID); } - else if (e is ScriptDieException) + else if(e is ScriptDieException) { /* * Script did an llDie() @@ -552,7 +539,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine m_SleepUntil = DateTime.MaxValue; m_Engine.World.DeleteSceneObject(m_Part.ParentGroup, false); } - else if (e is ScriptResetException) + else if(e is ScriptResetException) { /* * Script did an llResetScript(). @@ -579,63 +566,68 @@ namespace OpenSim.Region.ScriptEngine.XMREngine { StringBuilder msg = new StringBuilder(); - msg.Append ("[XMREngine]: Exception while running "); - msg.Append (m_ItemID); - msg.Append ('\n'); + msg.Append("[YEngine]: 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"); + 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 (" <"); + 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"); + 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); + m_log.Info(msg.ToString() + XMRExceptionStackString(e), e); /* * Give script owner the stack dump. */ - msg.Append (XMRExceptionStackString (e)); + 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'; + 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; + 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'); + this.llOwnerSay(line); + imstr.Append(line); + imstr.Append('\n'); } /* @@ -643,14 +635,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * Code modelled from llInstantMessage(). */ IMessageTransferModule transferModule = m_Engine.World.RequestModuleInterface(); - if (transferModule != null) { + 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.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.message = imstr.ToString(); gim.dialog = (byte)19; // messgage from script gim.fromGroup = false; gim.offline = (byte)0; @@ -663,7 +656,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine (int)Math.Floor(pos.X), (int)Math.Floor(pos.Y), (int)Math.Floor(pos.Z)); - transferModule.SendInstantMessage(gim, delegate(bool success) {}); + transferModule.SendInstantMessage(gim, delegate (bool success) + { + }); } /* @@ -679,16 +674,15 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ public void Reset() { - checkstate: + checkstate: XMRInstState iState = m_IState; - switch (iState) { - + switch(iState) + { /* * If it's really being constructed now, that's about as reset as we get. */ - case XMRInstState.CONSTRUCT: { + case XMRInstState.CONSTRUCT: return; - } /* * If it's idle, that means it is ready to receive a new event. @@ -696,15 +690,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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) { + 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 @@ -712,112 +707,108 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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) { + 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: { + case XMRInstState.RUNNING: suspendOnCheckRunHold = true; - lock (m_QueueLock) { } + 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) { + 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); + 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) { + 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); + case XMRInstState.FINISHED: + Sleep(10); goto checkstate; - } /* * If it's disposed, that's about as reset as it gets. */ - case XMRInstState.DISPOSED: { + case XMRInstState.DISPOSED: return; - } /* * Some other thread is already resetting it, let it finish. */ - case XMRInstState.RESETTING: { + case XMRInstState.RESETTING: return; - } - default: throw new Exception("bad state"); + + default: + throw new Exception("bad state"); } /* * This thread transitioned the instance to RESETTING so reset it. */ - lock (m_RunLock) { + 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 }); - } + if(m_IState != XMRInstState.RESETTING) + throw new Exception("bad state"); /* * Mark it idle now so it can get queued to process new stuff. @@ -836,21 +827,22 @@ namespace OpenSim.Region.ScriptEngine.XMREngine private void ClearQueueExceptLinkMessages() { - lock (m_QueueLock) { + lock(m_QueueLock) + { EventParams[] linkMessages = new EventParams[m_EventQueue.Count]; int n = 0; - foreach (EventParams evt2 in m_EventQueue) { - if (evt2.EventName == "link_message") { + 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 = m_EventCounts.Length; --i >= 0;) + m_EventCounts[i] = 0; - for (int i = 0; i < n; i ++) { + for(int i = 0; i < n; i++) m_EventQueue.AddLast(linkMessages[i]); - } m_EventCounts[(int)ScriptEventCode.link_message] = n; } @@ -858,10 +850,11 @@ namespace OpenSim.Region.ScriptEngine.XMREngine private void ClearQueue() { - lock (m_QueueLock) + lock(m_QueueLock) { m_EventQueue.Clear(); // no events queued - for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0; + for(int i = m_EventCounts.Length; --i >= 0;) + m_EventCounts[i] = 0; } } @@ -882,7 +875,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine m_Part.Inventory.GetInventoryItem(m_ItemID).PermsMask = 0; m_Part.Inventory.GetInventoryItem(m_ItemID).PermsGranter = UUID.Zero; IUrlModule urlModule = m_Engine.World.RequestModuleInterface(); - if (urlModule != null) + if(urlModule != null) urlModule.ScriptRemoved(m_ItemID); AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID); @@ -890,8 +883,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine 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 + 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 @@ -906,8 +899,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine m_RunOnePhase = "ResetLocked: posting default:state_entry() event"; stateCode = 0; m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(0)); - PostEvent(new EventParams("state_entry", - zeroObjectArray, + PostEvent(new EventParams("state_entry", + zeroObjectArray, zeroDetectParams)); /* @@ -920,27 +913,30 @@ namespace OpenSim.Region.ScriptEngine.XMREngine private void ReleaseControls() { - if (m_Part != null) + if(m_Part != null) { bool found; int permsMask; UUID permsGranter; - try { + try + { permsGranter = m_Part.TaskInventory[m_ItemID].PermsGranter; permsMask = m_Part.TaskInventory[m_ItemID].PermsMask; found = true; - } catch { + } + catch + { permsGranter = UUID.Zero; permsMask = 0; found = false; } - if (found && ((permsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)) { + if(found && ((permsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)) + { ScenePresence presence = m_Engine.World.GetScenePresence(permsGranter); - if (presence != null) { + if(presence != null) presence.UnRegisterControlEventsToScript(m_LocalID, m_ItemID); - } } } } @@ -949,61 +945,53 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @brief The script code should call this routine whenever it is * convenient to perform a migation or switch microthreads. */ - public override void CheckRunWork () + public override void CheckRunWork() { - if(!suspendOnCheckRunHold && ! suspendOnCheckRunTemp) + if(!suspendOnCheckRunHold && !suspendOnCheckRunTemp) { if(Util.GetTimeStampMS() - m_SliceStart < 60.0) return; suspendOnCheckRunTemp = true; } - m_CheckRunPhase = "entered"; /* * Stay stuck in this loop as long as something wants us suspended. */ - while (suspendOnCheckRunHold || suspendOnCheckRunTemp) + 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 (); + + switch(this.callMode) + { + // 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. + + // the script event handler wants to hibernate + // capture stack frames and unwind to Start() or Resume() + case CallMode_NORMAL: + m_CheckRunPhase = "suspending"; + callMode = XMRInstance.CallMode_SAVE; + stackFrames = null; + throw new StackHibernateException(); + + // 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. + + // the stack has been restored as a result of calling ResumeEx() + // tell script code to process calls normally + case CallMode_RESTORE: + this.callMode = CallMode_NORMAL; + break; + + default: + throw new Exception("callMode=" + callMode); + } + m_CheckRunPhase = "resumed"; } @@ -1013,7 +1001,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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); + if(callMode != CallMode_NORMAL) + throw new Exception("bad callMode " + callMode); } /** @@ -1021,12 +1010,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ public void ResumeIt() { - lock (m_QueueLock) + lock(m_QueueLock) { m_Suspended = false; - if ((m_EventQueue != null) && + if((m_EventQueue != null) && (m_EventQueue.First != null) && - (m_IState == XMRInstState.IDLE)) { + (m_IState == XMRInstState.IDLE)) + { m_IState = XMRInstState.ONSTARTQ; m_Engine.QueueToStart(this); } @@ -1039,7 +1029,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine */ public void SuspendIt() { - lock (m_QueueLock) + lock(m_QueueLock) { m_Suspended = true; } @@ -1052,5 +1042,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * to intercept this exception as it would block the stack capture * functionality. */ - public class StackCaptureException : Exception, IXMRUncatchable { } + public class StackCaptureException: Exception, IXMRUncatchable + { + } } diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRObjectTokens.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRObjectTokens.cs new file mode 100644 index 0000000000..d3ae1654e8 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRObjectTokens.cs @@ -0,0 +1,6296 @@ +/* + * 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.Yengine +{ + + /* + * 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 labelNames; + private Dictionary 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(); + localNames = new Dictionary(); + + 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 typeTranslator = InitTypeTranslator(); + private static Dictionary InitTypeTranslator() + { + Dictionary d = new Dictionary(); + 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 eharglist; + private Dictionary labels; + private Dictionary locals; + private Dictionary methargnames; + private LinkedList cilinstrs; + private OTStmtBlock topBlock; + private Stack opstack; + private Stack trystack; + private Stack 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(); + } + + 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(); + labels = new Dictionary(); + locals = new Dictionary(); + cilinstrs = new LinkedList(); + opstack = new Stack(); + trystack = new Stack(); + blockstack = new Stack(); + + 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 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_; 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 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 namecounts = new Dictionary(); + foreach(Dictionary 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 inittypes = new Dictionary(); + 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 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.YEngine.")) + { + 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 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 link) + { + OTStmtLabel.AddLast(decompile, this); + } + } + + /* + * 'try {' + */ + private class OTCilBegExcBlk: OTCilInstr + { + public LinkedList catches = new LinkedList(); + + public OTCilBegExcBlk(int offset) : base(offset) + { + } + + public override string DumpString() + { + return "try {"; + } + + public override void BuildStatements(OTDecompile decompile, LinkedListNode 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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[] is a reference to a global variable + * likewise so is __xmrinst.glblVars.iar[] + */ + if((array is OTOpndField) && (index is OTOpndInt)) + { + + /* + * arrayfield = (arg$0.glblVars).iar + * arrayfieldobj = arg$0.glblVars + * iartypename = iar + */ + 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 xor1ops = InitXor1Ops(); + + private static Dictionary InitXor1Ops() + { + Dictionary d = new Dictionary(); + 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 precedence = InitPrecedence(); + private static Dictionary InitPrecedence() + { + Dictionary d = new Dictionary(); + 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 mathmeths = InitMathMeths(); + private static Dictionary InitMathMeths() + { + Dictionary d = new Dictionary(); + 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 + * 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 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$ = newarr object[] << link points here + * dup$[0] = bla + * dup$[1] = bla + * ... + * ... newobj list (dup$) ... + */ + public static bool Detect(LinkedListNode link) + { + if(link == null) + return false; + + /* + * Check for 'dup$ = newarr object[]' and get listsize from . + */ + 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$[] = bla + * If so, save the bla values in the values[] array. + */ + LinkedListNode 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$)' 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 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) => + 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) => + 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 binops = InitBinops(); + private static Dictionary InitBinops() + { + Dictionary d = new Dictionary(); + 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 brfops = InitBrfOps(); + private static Dictionary InitBrfOps() + { + Dictionary d = new Dictionary(); + 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 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 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 link) + { + // strip all statements following jump until seeing some label + bool rc = false; + if(link != null) + { + LinkedListNode 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 link) + { + return catchblock.StripStuff(null); + } + + public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) + { + return catchblock.ReplaceOperand(oldopnd, newopnd); + } + + public override bool DetectDoForIfWhile(LinkedListNode 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 catches = new LinkedList(); + + // 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 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 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 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 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 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 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 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 ScanForLabel(LinkedListNode 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 link, LinkedListNode donelink) + { + LinkedListNode 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 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 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 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 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 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 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 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 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 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 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 blkstmts = new LinkedList(); + + 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 link) + { + // loop through all sub-statements to strip out behind-the-scenes references + bool rc = false; + loop: + for(LinkedListNode 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 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 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 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_; << link points here + * ... ... + * [ if (dotest) ] jump doloop_; + */ + public static bool Detect(LinkedListNode 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 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 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 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 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... + * + * @forloop_; << link points here + * [ if () jump forbreak_; ] + * ... ... + * jump forloop_; + * [ @forbreak_; ] + */ + public static bool Detect(LinkedListNode 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_;' statement + // also gather up the statements for the for body block + it.forbody = new OTStmtBlock(); + LinkedListNode 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 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 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 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_; << link points here + * ... then body ... + * @ifdone_; + * + * if (condition) jump ifelse_; + * ... then body ... + * jump ifdone_; << optional if true body doesn't fall through + * @ifelse_; + * ... else body ... + * @ifdone_; + */ + public static bool Detect(LinkedListNode 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_ label + // also save the intervening statements for the then body + OTStmtBlock thenbody; + LinkedListNode donelink = ScanForLabel(link, jumpstmt.label, out thenbody); + + // make sure we found matching label + if(donelink == null) + return false; + + // replace the jump ifdone_ with the + 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_ 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_ label + // also save the intervening statements for the true body + OTStmtBlock thenbody; + LinkedListNode 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_ + LinkedListNode 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_ label + // also save the intervening statements for the else body + OTStmtBlock elsebody; + LinkedListNode donelink = ScanForLabel(elselink, jumpifdone.label, out elsebody); + if(donelink != null) + { + + // replace the jump ifdone_ with the + 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_ 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_, so make it a simple if/then + // if (condition) jump ifelse_; << link + // ... then body ... << encapsulated in block thenbody + // @ifelse_; << elselink + // ... else body ... << still inline and leave it there + // @ifdone_; << strip this out + + // replace the jump ifelse_ with the + 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_ label + StripInterveningStatements(link, elselink); + + // there's a dangling unused ifdone_ label ahead that has to be stripped + for(LinkedListNode 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 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 ScanForLabel(LinkedListNode 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 link, LinkedListNode donelink) + { + LinkedListNode 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 myopsbyname = new Dictionary(); + 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", "!="); + } + } +} diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRSDTypeClObj.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRSDTypeClObj.cs similarity index 77% rename from OpenSim/Region/ScriptEngine/XMREngine/XMRSDTypeClObj.cs rename to OpenSim/Region/ScriptEngine/YEngine/XMRSDTypeClObj.cs index cbb8f963da..1bdd28e851 100644 --- a/OpenSim/Region/ScriptEngine/XMREngine/XMRSDTypeClObj.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRSDTypeClObj.cs @@ -36,7 +36,7 @@ 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 +namespace OpenSim.Region.ScriptEngine.Yengine { public class XMRSDTypeClObj { @@ -56,7 +56,7 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * Our VTable array, used for calling virtual functions. * And ITable array, used for calling our implementation of interface functions. */ - public Delegate[] sdtcVTable; + public Delegate[] sdtcVTable; public Delegate[][] sdtcITable; /* @@ -69,10 +69,10 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Called by script's $new() to initialize a new object. */ - public XMRSDTypeClObj (XMRInstAbstract inst, int classindex) + public XMRSDTypeClObj(XMRInstAbstract inst, int classindex) { - Construct (inst, classindex); - instVars.AllocVarArrays (sdtcClass.instSizes); + Construct(inst, classindex); + instVars.AllocVarArrays(sdtcClass.instSizes); } /** @@ -81,26 +81,28 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @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) + private void Construct(XMRInstAbstract inst, int classindex) { Delegate[] thisMid = null; TokenDeclSDTypeClass clas = (TokenDeclSDTypeClass)inst.m_ObjCode.sdObjTypesIndx[classindex]; - xmrInst = inst; + xmrInst = inst; sdtcClass = clas; - instVars = new XMRInstArrays (inst); + 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) { + 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); + for(int i = 0; i < n; i++) + { + sdtcVTable[i] = vDynMeths[i].CreateDelegate(vMethTypes[i], this); } } @@ -114,33 +116,40 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * So we end up with this: * sdtcITable[interfacenumber][methodofintfnumber] = delegate of this.ourimplementationofinterfacesmethod */ - if (clas.iDynMeths != null) { + if(clas.iDynMeths != null) + { int nIFaces = clas.iDynMeths.Length; sdtcITable = new Delegate[nIFaces][]; - for (int i = 0; i < nIFaces; i ++) { + 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]; + 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) { + 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); + for(int j = 0; j < nMeths; j++) + { + ivec[j] = iDynMeths[j].CreateDelegate(iMethTypes[j], this); } - } else { + } + 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) { + if(thisMid == null) + { thisMid = new Delegate[1]; - thisMid[0] = markerInterfaceDummy.CreateDelegate (typeof (MarkerInterfaceDummy), this); + thisMid[0] = markerInterfaceDummy.CreateDelegate(typeof(MarkerInterfaceDummy), this); } ivec = thisMid; } @@ -151,13 +160,13 @@ namespace OpenSim.Region.ScriptEngine.XMREngine } } - private delegate void MarkerInterfaceDummy (); - private static DynamicMethod markerInterfaceDummy = MakeMarkerInterfaceDummy (); - private static DynamicMethod MakeMarkerInterfaceDummy () + 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); + DynamicMethod dm = new DynamicMethod("XMRSDTypeClObj.MarkerInterfaceDummy", null, new Type[] { typeof(XMRSDTypeClObj) }); + ILGenerator ilGen = dm.GetILGenerator(); + ilGen.Emit(OpCodes.Ret); return dm; } @@ -168,9 +177,9 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @param classindex = what class those implementations are supposedly part of * @returns original script-defined class object */ - public static XMRSDTypeClObj CastIFace2Class (Delegate[] da, int classindex) + public static XMRSDTypeClObj CastIFace2Class(Delegate[] da, int classindex) { - return CastClass2Class (da[0].Target, classindex); + return CastClass2Class(da[0].Target, classindex); } /** @@ -179,13 +188,14 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @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) + 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) { + if(ci != null) + { /* * This is the target class, ie, what we are hoping the object can cast to. @@ -197,9 +207,11 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * 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); + 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); } /* @@ -215,14 +227,16 @@ namespace OpenSim.Region.ScriptEngine.XMREngine * @param ob = object to be cast of unknown type * @returns ob cast to the interface type */ - public static Delegate[] CastObj2IFace (object ob, string ifacename) + public static Delegate[] CastObj2IFace(object ob, string ifacename) { - if (ob == null) return null; + 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[]) { + if(ob is Delegate[]) + { Delegate[] da = (Delegate[])ob; ob = da[0].Target; } @@ -239,21 +253,23 @@ namespace OpenSim.Region.ScriptEngine.XMREngine /** * @brief Write the whole thing out to a stream. */ - public void Capture (XMRInstArrays.Sender sendValue) + public void Capture(XMRInstArrays.Sender sendValue) { - sendValue (this.sdtcClass.sdTypeIndex); - this.instVars.SendArrays (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) + public XMRSDTypeClObj() { - int classindex = (int)recvValue (); - Construct (inst, classindex); - this.instVars.RecvArrays (recvValue); + } + public void Restore(XMRInstAbstract inst, XMRInstArrays.Recver recvValue) + { + int classindex = (int)recvValue(); + Construct(inst, classindex); + this.instVars.RecvArrays(recvValue); } } } diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRScriptThread.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRScriptThread.cs new file mode 100644 index 0000000000..08c7e80c92 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRScriptThread.cs @@ -0,0 +1,240 @@ +/* + * 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.Framework.Monitoring; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace OpenSim.Region.ScriptEngine.Yengine +{ + + public partial class Yengine + { + private int m_WakeUpOne = 0; + public object m_WakeUpLock = new object(); + + private Dictionary m_RunningInstances = new Dictionary(); + + private bool m_SuspendScriptThreadFlag = false; + private bool m_WakeUpThis = false; + public DateTime m_LastRanAt = DateTime.MinValue; + public long m_ScriptExecTime = 0; + + [ThreadStatic] + private static int m_ScriptThreadTID; + + public static bool IsScriptThread + { + get + { + return m_ScriptThreadTID != 0; + } + } + + public void StartThreadWorker(int i) + { + Thread thd; + if(i >= 0) + thd = Yengine.StartMyThread(RunScriptThread, "YScript" + i.ToString(), ThreadPriority.BelowNormal); + else + thd = Yengine.StartMyThread(RunScriptThread, "YScript", ThreadPriority.BelowNormal); + lock(m_WakeUpLock) + m_RunningInstances.Add(thd.ManagedThreadId, null); + } + + public void StopThreadWorkers() + { + lock(m_WakeUpLock) + { + while(m_RunningInstances.Count != 0) + { + Monitor.PulseAll(m_WakeUpLock); + Monitor.Wait(m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2); + } + } + } + + /** + * @brief Something was just added to the Start or Yield queue so + * wake one of the RunScriptThread() instances to run it. + */ + public void WakeUpOne() + { + lock(m_WakeUpLock) + { + m_WakeUpOne++; + Monitor.Pulse(m_WakeUpLock); + } + } + + public void SuspendThreads() + { + lock(m_WakeUpLock) + { + m_SuspendScriptThreadFlag = true; + Monitor.PulseAll(m_WakeUpLock); + } + } + + public void ResumeThreads() + { + lock(m_WakeUpLock) + { + m_SuspendScriptThreadFlag = false; + Monitor.PulseAll(m_WakeUpLock); + } + } + + /** + * @brief Thread that runs the scripts. + * + * 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. + */ + private void RunScriptThread() + { + int tid = System.Threading.Thread.CurrentThread.ManagedThreadId; + ThreadStart thunk; + XMRInstance inst; + bool didevent; + m_ScriptThreadTID = tid; + + while(!m_Exiting) + { + Yengine.UpdateMyThread(); + + lock(m_WakeUpLock) + { + // Maybe there are some scripts waiting to be migrated in or out. + thunk = null; + if(m_ThunkQueue.Count > 0) + thunk = m_ThunkQueue.Dequeue(); + + // Handle 'xmr resume/suspend' commands. + else if(m_SuspendScriptThreadFlag && !m_Exiting) + { + Monitor.Wait(m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2); + Yengine.UpdateMyThread(); + continue; + } + } + + if(thunk != null) + { + thunk(); + continue; + } + + if(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; + didevent = false; + for(numStarts = 5; numStarts >= 0; --numStarts) + { + lock(m_StartQueue) + inst = m_StartQueue.RemoveHead(); + + if(inst == null) + break; + if(inst.m_IState != XMRInstState.ONSTARTQ) + throw new Exception("bad state"); + RunInstance(inst, tid); + if(m_SuspendScriptThreadFlag || m_Exiting) + continue; + didevent = true; + } + + // 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(m_YieldQueue) + inst = m_YieldQueue.RemoveHead(); + + if(inst != null) + { + if(inst.m_IState != XMRInstState.ONYIELDQ) + throw new Exception("bad state"); + RunInstance(inst, tid); + continue; + } + + // If we left something dangling in the m_StartQueue or m_YieldQueue, go back to check it. + if(didevent) + 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); + } + } + lock(m_WakeUpLock) + m_RunningInstances.Remove(tid); + + Yengine.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, int tid) + { + m_LastRanAt = DateTime.UtcNow; + m_ScriptExecTime -= (long)(m_LastRanAt - DateTime.MinValue).TotalMilliseconds; + inst.m_IState = XMRInstState.RUNNING; + + lock(m_WakeUpLock) + m_RunningInstances[tid] = inst; + + XMRInstState newIState = inst.RunOne(); + + lock(m_WakeUpLock) + m_RunningInstances[tid] = null; + + HandleNewIState(inst, newIState); + m_ScriptExecTime += (long)(DateTime.UtcNow - DateTime.MinValue).TotalMilliseconds; + } + } +} diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRScriptUThread.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRScriptUThread.cs new file mode 100644 index 0000000000..5806d36ba2 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRScriptUThread.cs @@ -0,0 +1,97 @@ +/* + * 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.Threading; + +namespace OpenSim.Region.ScriptEngine.Yengine +{ + + public partial class XMRInstance + { + /** + * @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() + { + // Start script event handler from very beginning. + callMode = XMRInstance.CallMode_NORMAL; + try + { + CallSEH(); // run script event handler + } + catch(StackHibernateException) + { + if(callMode != XMRInstance.CallMode_SAVE) + throw new Exception("callMode=" + callMode); + } + catch(Exception e) + { + return e; + } + + return null; + } + + /** + * @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() + { + // Resume script from captured stack. + callMode = XMRInstance.CallMode_RESTORE; + suspendOnCheckRunTemp = true; + try + { + CallSEH(); // run script event handler + } + catch(StackHibernateException) + { + if(callMode != XMRInstance.CallMode_SAVE) + throw new Exception("callMode=" + callMode); + } + catch(Exception e) + { + return e; + } + + return null; + } + + public class StackHibernateException: Exception, IXMRUncatchable + { + } + } +} diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index fc29304a8a..57dd6fd1ee 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -265,8 +265,8 @@ ; DefaultScriptEngine = "XEngine" ;; ***DANGER***DANGER*** ;; experimental engine - ;; see section [XMREngine} below - ; DefaultScriptEngine = "XMREngine" + ;; see section [YEngine} below + ; DefaultScriptEngine = "YEngine" ;# {HttpProxy} {} {Proxy URL for llHTTPRequest and dynamic texture loading} {} http://proxy.com:8080 ;; Http proxy setting for llHTTPRequest and dynamic texture loading, if @@ -922,20 +922,19 @@ [XMREngine] ;; experimental engine ;; ONLY SUPORTS ONE REGION PER INSTANCE at this point - ;; implements microthreading, so fixing problems like llSleep or long events handlers + ;; implements non preemptive microthreading, so fixing problems like llSleep or long events handlers ;; but those will suffer from timeslicing, so will be slower. ;; compiles LSL directly to IL, so only suports LSL scripting (no C# etc) ;; shares the Xengine APIs like LSL, OSSL, etc. ;; DANGER, do not use with HG, don't leave regions running alone with it. - ;; TPs or crossings to/from Xengine will recompile scripts losing state. + ;; TPs or crossings to/from Xengine will full recompile scripts losing state. ;; attachment scripts may misbehave, cars will stop on crossings, etc. Enabled = false - UThreadModel = sys ScriptStackSize = 256 ScriptHeapSize = 256 UseSourceHashCode = true MinTimerInterval = 0.1 - ;ScriptBasePath="ScriptData" + ;ScriptBasePath="ScriptEngines" [XEngine] ;# {Enabled} {} {Enable the XEngine scripting engine?} {true false} true diff --git a/prebuild.xml b/prebuild.xml index f6c1e46940..f0721c4df2 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2418,8 +2418,8 @@ - - + + ../../../../bin/