/* * 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 readonly Dictionary opCodes = PopulateOpCodes(); private static readonly Dictionary string2Type = PopulateS2T(); private static readonly Dictionary type2String = PopulateT2S(); 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 readonly Dictionary sdTypesRev = new (); public int labelNumber = 0; public int localNumber = 0; public string methName { get; private set;} 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 () { type = type, name = name, number = localNumber++, 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() { name = name, 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(nameof(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 (); DynamicMethod method = null; ILGenerator ilGen = null; Dictionary labels = new (); Dictionary locals = new (); Dictionary labelNames = new (); Dictionary localNames = new (); object[] ilGenArg = new object[1]; int offset; Dictionary srcLocs = null; string srcFile = ""; int srcLine = 0; int srcPosn = 0; while(true) { // Get IL instruction offset at beginning of instruction. offset = ilGen is null ? 0 : ilGen.ILOffset; // 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(); 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; 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()); 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()); 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)); 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]); objectTokens?.MarkLabel(offset, number); break; } // Try/Catch blocks. case ScriptObjWriterCode.BegExcBlk: { ilGen.BeginExceptionBlock(); objectTokens?.BegExcBlk(offset); break; } case ScriptObjWriterCode.BegCatBlk: { Type excType = GetTypeFromStr(sdTypes, objReader.ReadString()); ilGen.BeginCatchBlock(excType); objectTokens?.BegCatBlk(offset, excType); break; } case ScriptObjWriterCode.BegFinBlk: { ilGen.BeginFinallyBlock(); objectTokens?.BegFinBlk(offset); break; } case ScriptObjWriterCode.EndExcBlk: { ilGen.EndExceptionBlock(); 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); 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); 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]); 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); 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]); 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); 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); 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); 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); 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); 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); 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); 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: 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); 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 (); 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) { srcLocs[offset] = new ScriptSrcLoc() { file = srcFile, line = srcLine, posn = srcPosn }; } /** * @brief Create type<->string conversions. * Using Type.AssemblyQualifiedName is horribly inefficient * and all our types should be known. */ private static Dictionary PopulateS2T() { return new Dictionary { { "badcallx", typeof(ScriptBadCallNoException) }, { "binopstr", typeof(BinOpStr) }, { "bool", typeof(bool) }, { "char", typeof(char) }, { "delegate", typeof(Delegate) }, { "delarr[]", typeof(Delegate[]) }, { "double", typeof(double) }, { "exceptn", typeof(Exception) }, { "float", typeof(float) }, { "htlist", typeof(HeapTrackerList) }, { "htobject", typeof(HeapTrackerObject) }, { "htstring", typeof(HeapTrackerString) }, { "inlfunc", typeof(CompValuInline) }, { "int", typeof(int) }, { "int*", typeof(int).MakeByRefType() }, { "intrlokd", typeof(System.Threading.Interlocked) }, { "lslfloat", typeof(LSL_Float) }, { "lslint", typeof(LSL_Integer) }, { "lsllist", typeof(LSL_List) }, { "lslrot", typeof(LSL_Rotation) }, { "lslstr", typeof(LSL_String) }, { "lslvec", typeof(LSL_Vector) }, { "math", typeof(Math) }, { "midround", typeof(MidpointRounding) }, { "object", typeof(object) }, { "object*", typeof(object).MakeByRefType() }, { "object[]", typeof(object[]) }, { "scrbase", typeof(ScriptBaseClass) }, { "scrcode", typeof(ScriptCodeGen) }, { "sdtclobj", typeof(XMRSDTypeClObj) }, { "string", typeof(string) }, { "typecast", typeof(TypeCast) }, { "undstatx", typeof(ScriptUndefinedStateException) }, { "void", typeof(void) }, { "xmrarray", typeof(XMR_Array) }, { "xmrinst", typeof(XMRInstAbstract) } }; } private static Dictionary PopulateT2S() { Dictionary s2t = PopulateS2T(); Dictionary t2s = new (); 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) { // internal fixed types like int and xmrarray etc if (type2String.TryGetValue(t, out string 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) { // internal fixed types like int and xmrarray etc if (string2Type.TryGetValue(s, out Type t)) return t; // script-defined types if (s.StartsWith("sdt$")) return sdTypes[s[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; } }