// Try to create object code from source code // If error, just throw exception private ScriptObjCode TryToCompile() { m_CompilerErrors.Clear(); // If object file exists, create ScriptObjCode directly from that. // Otherwise, compile the source to create object file then create // ScriptObjCode from that. string assetID = m_Item.AssetID.ToString(); m_CameFrom = "asset://" + assetID; ScriptObjCode objCode = Compile(); if (m_CompilerErrors.Count != 0) { throw new Exception("compilation errors"); } if (objCode == null) { throw new Exception("compilation failed"); } return(objCode); }
/** * @brief Take an object file created by ScriptObjWriter() and convert it to a series of dynamic methods. * @param sdTypes = script-defined types * @param objReader = where to read object file from (as written by ScriptObjWriter above). * @param scriptObjCode.EndMethod = called for each method defined at the end of the methods definition * @param objectTokens = write disassemble/decompile data (or null if not wanted) */ public static void CreateObjCode(Dictionary <string, TokenDeclSDType> sdTypes, BinaryReader objReader, ScriptObjCode scriptObjCode, ObjectTokens objectTokens) { Dictionary <string, DynamicMethod> methods = new Dictionary <string, DynamicMethod>(); DynamicMethod method = null; ILGenerator ilGen = null; Dictionary <int, Label> labels = new Dictionary <int, Label>(); Dictionary <int, LocalBuilder> locals = new Dictionary <int, LocalBuilder>(); Dictionary <int, string> labelNames = new Dictionary <int, string>(); Dictionary <int, string> localNames = new Dictionary <int, string>(); object[] ilGenArg = new object[1]; int offset = 0; Dictionary <int, ScriptSrcLoc> srcLocs = null; string srcFile = ""; int srcLine = 0; int srcPosn = 0; while (true) { // Get IL instruction offset at beginning of instruction. offset = 0; if ((ilGen != null) && (monoGetCurrentOffset != null)) { offset = (int)monoGetCurrentOffset.Invoke(null, ilGenArg); } // Read and decode next internal format code from input file (.xmrobj file). ScriptObjWriterCode code = (ScriptObjWriterCode)objReader.ReadByte(); switch (code) { // Reached end-of-file so we are all done. case ScriptObjWriterCode.TheEnd: return; // Beginning of method's contents. // Method must have already been declared via DclMethod // so all we need is its name to retrieve from methods[]. case ScriptObjWriterCode.BegMethod: { string methName = objReader.ReadString(); method = methods[methName]; ilGen = method.GetILGenerator(); ilGenArg[0] = ilGen; labels.Clear(); locals.Clear(); labelNames.Clear(); localNames.Clear(); srcLocs = new Dictionary <int, ScriptSrcLoc>(); if (objectTokens != null) { objectTokens.BegMethod(method); } break; } // End of method's contents (ie, an OpCodes.Ret was probably just output). // Call the callback to tell it the method is complete, and it can do whatever // it wants with the method. case ScriptObjWriterCode.EndMethod: { ilGen = null; ilGenArg[0] = null; scriptObjCode.EndMethod(method, srcLocs); srcLocs = null; if (objectTokens != null) { objectTokens.EndMethod(); } break; } // Declare a label for branching to. case ScriptObjWriterCode.DclLabel: { int number = objReader.ReadInt32(); string name = objReader.ReadString(); labels.Add(number, ilGen.DefineLabel()); labelNames.Add(number, name + "_" + number.ToString()); if (objectTokens != null) { objectTokens.DefineLabel(number, name); } break; } // Declare a local variable to store into. case ScriptObjWriterCode.DclLocal: { int number = objReader.ReadInt32(); string name = objReader.ReadString(); string type = objReader.ReadString(); Type syType = GetTypeFromStr(sdTypes, type); locals.Add(number, ilGen.DeclareLocal(syType)); localNames.Add(number, name + "_" + number.ToString()); if (objectTokens != null) { objectTokens.DefineLocal(number, name, type, syType); } break; } // Declare a method that will subsequently be defined. // We create the DynamicMethod object at this point in case there // are forward references from other method bodies. case ScriptObjWriterCode.DclMethod: { string methName = objReader.ReadString(); Type retType = GetTypeFromStr(sdTypes, objReader.ReadString()); int nArgs = objReader.ReadInt32(); Type[] argTypes = new Type[nArgs]; string[] argNames = new string[nArgs]; for (int i = 0; i < nArgs; i++) { argTypes[i] = GetTypeFromStr(sdTypes, objReader.ReadString()); argNames[i] = objReader.ReadString(); } methods.Add(methName, new DynamicMethod(methName, retType, argTypes)); if (objectTokens != null) { objectTokens.DefineMethod(methName, retType, argTypes, argNames); } break; } // Mark a previously declared label at this spot. case ScriptObjWriterCode.MarkLabel: { int number = objReader.ReadInt32(); ilGen.MarkLabel(labels[number]); if (objectTokens != null) { objectTokens.MarkLabel(offset, number); } break; } // Try/Catch blocks. case ScriptObjWriterCode.BegExcBlk: { ilGen.BeginExceptionBlock(); if (objectTokens != null) { objectTokens.BegExcBlk(offset); } break; } case ScriptObjWriterCode.BegCatBlk: { Type excType = GetTypeFromStr(sdTypes, objReader.ReadString()); ilGen.BeginCatchBlock(excType); if (objectTokens != null) { objectTokens.BegCatBlk(offset, excType); } break; } case ScriptObjWriterCode.BegFinBlk: { ilGen.BeginFinallyBlock(); if (objectTokens != null) { objectTokens.BegFinBlk(offset); } break; } case ScriptObjWriterCode.EndExcBlk: { ilGen.EndExceptionBlock(); if (objectTokens != null) { objectTokens.EndExcBlk(offset); } break; } // Emit an opcode with no operand. case ScriptObjWriterCode.EmitNull: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode); if (objectTokens != null) { objectTokens.EmitNull(offset, opCode); } break; } // Emit an opcode with a FieldInfo operand. case ScriptObjWriterCode.EmitField: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); Type reflectedType = GetTypeFromStr(sdTypes, objReader.ReadString()); string fieldName = objReader.ReadString(); FieldInfo field = reflectedType.GetField(fieldName); SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, field); if (objectTokens != null) { objectTokens.EmitField(offset, opCode, field); } break; } // Emit an opcode with a LocalBuilder operand. case ScriptObjWriterCode.EmitLocal: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); int number = objReader.ReadInt32(); SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, locals[number]); if (objectTokens != null) { objectTokens.EmitLocal(offset, opCode, number); } break; } // Emit an opcode with a Type operand. case ScriptObjWriterCode.EmitType: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); string name = objReader.ReadString(); Type type = GetTypeFromStr(sdTypes, name); SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, type); if (objectTokens != null) { objectTokens.EmitType(offset, opCode, type); } break; } // Emit an opcode with a Label operand. case ScriptObjWriterCode.EmitLabel: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); int number = objReader.ReadInt32(); SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, labels[number]); if (objectTokens != null) { objectTokens.EmitLabel(offset, opCode, number); } break; } // Emit an opcode with a Label array operand. case ScriptObjWriterCode.EmitLabels: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); int nLabels = objReader.ReadInt32(); Label[] lbls = new Label[nLabels]; int[] nums = new int[nLabels]; for (int i = 0; i < nLabels; i++) { nums[i] = objReader.ReadInt32(); lbls[i] = labels[nums[i]]; } SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, lbls); if (objectTokens != null) { objectTokens.EmitLabels(offset, opCode, nums); } break; } // Emit an opcode with a MethodInfo operand (such as a call) of an external function. case ScriptObjWriterCode.EmitMethodExt: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); string methName = objReader.ReadString(); Type methType = GetTypeFromStr(sdTypes, objReader.ReadString()); int nArgs = objReader.ReadInt32(); Type[] argTypes = new Type[nArgs]; for (int i = 0; i < nArgs; i++) { argTypes[i] = GetTypeFromStr(sdTypes, objReader.ReadString()); } MethodInfo methInfo = methType.GetMethod(methName, argTypes); SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, methInfo); if (objectTokens != null) { objectTokens.EmitMethod(offset, opCode, methInfo); } break; } // Emit an opcode with a MethodInfo operand of an internal function // (previously declared via DclMethod). case ScriptObjWriterCode.EmitMethodInt: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); string methName = objReader.ReadString(); MethodInfo methInfo = methods[methName]; SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, methInfo); if (objectTokens != null) { objectTokens.EmitMethod(offset, opCode, methInfo); } break; } // Emit an opcode with a ConstructorInfo operand. case ScriptObjWriterCode.EmitCtor: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); Type ctorType = GetTypeFromStr(sdTypes, objReader.ReadString()); int nArgs = objReader.ReadInt32(); Type[] argTypes = new Type[nArgs]; for (int i = 0; i < nArgs; i++) { argTypes[i] = GetTypeFromStr(sdTypes, objReader.ReadString()); } ConstructorInfo ctorInfo = ctorType.GetConstructor(argTypes); SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, ctorInfo); if (objectTokens != null) { objectTokens.EmitCtor(offset, opCode, ctorInfo); } break; } // Emit an opcode with a constant operand of various types. case ScriptObjWriterCode.EmitDouble: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); double value = objReader.ReadDouble(); if (opCode != OpCodes.Ldc_R8) { throw new Exception("bad opcode " + opCode.ToString()); } SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, value); if (objectTokens != null) { objectTokens.EmitDouble(offset, opCode, value); } break; } case ScriptObjWriterCode.EmitFloat: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); float value = objReader.ReadSingle(); if (opCode != OpCodes.Ldc_R4) { throw new Exception("bad opcode " + opCode.ToString()); } SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, value); if (objectTokens != null) { objectTokens.EmitFloat(offset, opCode, value); } break; } case ScriptObjWriterCode.EmitInteger: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); int value = objReader.ReadInt32(); SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); if (opCode == OpCodes.Ldc_I4) { if ((value >= -1) && (value <= 8)) { opCode = opCodesLdcI4M1P8[value + 1]; ilGen.Emit(opCode); if (objectTokens != null) { objectTokens.EmitNull(offset, opCode); } break; } if ((value >= 0) && (value <= 127)) { opCode = OpCodes.Ldc_I4_S; ilGen.Emit(OpCodes.Ldc_I4_S, (sbyte)value); goto pemitint; } } ilGen.Emit(opCode, value); pemitint: if (objectTokens != null) { objectTokens.EmitInteger(offset, opCode, value); } break; } case ScriptObjWriterCode.EmitString: { OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn); string value = objReader.ReadString(); SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn); ilGen.Emit(opCode, value); if (objectTokens != null) { objectTokens.EmitString(offset, opCode, value); } break; } // Who knows what? default: throw new Exception("bad ScriptObjWriterCode " + ((byte)code).ToString()); } } }
/** * @brief Compile a script to produce a ScriptObjCode object * @returns object code pointer or null if compile error * also can throw compile error exception */ public ScriptObjCode Compile() { Stream objFileStream = null; StreamWriter asmFileWriter = null; string sourceHash = null; TextWriter saveSource = null; string objFileName = GetScriptFileName(m_ScriptObjCodeKey + ".yobj"); string tmpFileName = GetScriptFileName(m_ScriptObjCodeKey + ".ytmp"); // If we already have an object file, don't bother compiling. if (!m_ForceRecomp && File.Exists(objFileName)) { objFileStream = File.OpenRead(objFileName); } 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. 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. TokenBegin tokenBegin; try { tokenBegin = TokenBegin.Construct(m_CameFrom, saveSource, ErrorHandler, m_SourceCode, out sourceHash); } finally { if (saveSource != null) { saveSource.Close(); } } if (tokenBegin == null) { m_log.Debug("[YEngine]: parsing errors on " + m_ScriptObjCodeKey); return(null); } // Create object file one way or another. try { // Create abstract syntax tree from raw tokens. TokenScript tokenScript = ScriptReduce.Reduce(tokenBegin); if (tokenScript == null) { m_log.Warn("[YEngine]: reduction errors on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")"); PrintCompilerErrors(); return(null); } // Compile abstract syntax tree to write object file. using (BinaryWriter objFileWriter = new BinaryWriter(File.Create(tmpFileName))) { bool ok = ScriptCodeGen.CodeGen(tokenScript, objFileWriter, sourceHash); if (!ok) { m_log.Warn("[YEngine]: compile error on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")"); PrintCompilerErrors(); return(null); } } // File has been completely written. // If there is an old one laying around, delete it now. // Then re-open the new file for reading from the beginning. if (File.Exists(objFileName)) { File.Replace(tmpFileName, objFileName, null); } else { File.Move(tmpFileName, objFileName); } objFileStream = File.OpenRead(objFileName); } finally { // In case something went wrong writing temp file, delete it. File.Delete(tmpFileName); } // Since we just wrote the .xmrobj file, maybe save disassembly. if (m_Engine.m_ScriptDebugSaveIL) { string asmFileName = GetScriptFileName(m_ScriptObjCodeKey + ".yasm"); // 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. BinaryReader objFileReader = new BinaryReader(objFileStream); ScriptObjCode scriptObjCode = null; try { scriptObjCode = new ScriptObjCode(objFileReader, asmFileWriter, null); } finally { objFileReader.Close(); if (asmFileWriter != null) { asmFileWriter.Flush(); asmFileWriter.Close(); } } return(scriptObjCode); }