// 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> (); 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 = (ilGen != null) ? ilGen.ILOffset : 0; /* * 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(); 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; 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() { bool oldObjFile = false; Stream objFileStream = null; StreamWriter asmFileWriter = null; string envar = null; string sourceHash = null; TextWriter saveSource = null; string asmFileName = GetScriptFileName(m_ScriptObjCodeKey + ".xmrasm"); string lslFileName = GetScriptFileName(m_ScriptObjCodeKey + ".lsl"); string objFileName = GetScriptFileName(m_ScriptObjCodeKey + ".xmrobj"); string tmpFileName = GetScriptFileName(m_ScriptObjCodeKey + ".xmrtmp"); /* * If we already have an object file, don't bother compiling. */ if (!m_ForceRecomp && File.Exists(objFileName)) { objFileStream = File.OpenRead(objFileName); oldObjFile = true; } else { /* * If source file empty, try to read from asset server. */ if (EmptySource(m_SourceCode)) { m_SourceCode = FetchSource(m_CameFrom); } /* * Maybe write script source to a file for debugging. */ envar = Environment.GetEnvironmentVariable("MMRScriptCompileSaveSource"); if ((envar != null) && ((envar[0] & 1) != 0)) { m_log.Debug("[XMREngine]: MMRScriptCompileSaveSource: saving to " + lslFileName); saveSource = File.CreateText(lslFileName); } /* * Parse source string into tokens. */ TokenBegin tokenBegin; try { tokenBegin = TokenBegin.Construct(m_CameFrom, saveSource, ErrorHandler, m_SourceCode, out sourceHash); } finally { if (saveSource != null) { saveSource.Close(); } } if (tokenBegin == null) { m_log.Debug("[XMREngine]: parsing errors on " + m_ScriptObjCodeKey); return(null); } /* * Create object file one way or another. */ try { objFileStream = File.Create(tmpFileName); /* * Create abstract syntax tree from raw tokens. */ TokenScript tokenScript = ScriptReduce.Reduce(tokenBegin); if (tokenScript == null) { m_log.Warn("[XMREngine]: reduction errors on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")"); PrintCompilerErrors(); return(null); } /* * Compile abstract syntax tree to write object file. */ BinaryWriter objFileWriter = new BinaryWriter(objFileStream); bool ok = ScriptCodeGen.CodeGen(tokenScript, objFileWriter, sourceHash); if (!ok) { m_log.Warn("[XMREngine]: compile error on " + m_ScriptObjCodeKey + " (" + m_CameFrom + ")"); PrintCompilerErrors(); objFileStream.Close(); return(null); } objFileStream.Close(); /* * File has been completely written. * If there is an old one laying around, delete it now. * Then re-open the new file for reading from the beginning. */ if (File.Exists(objFileName)) { File.Replace(tmpFileName, objFileName, null); } else { File.Move(tmpFileName, objFileName); } objFileStream = File.OpenRead(objFileName); } finally { /* * In case something went wrong writing temp file, delete it. */ try { File.Delete(tmpFileName); } catch { } } /* * Since we just wrote the .xmrobj file, maybe save disassembly. */ envar = Environment.GetEnvironmentVariable("MMRScriptCompileSaveILGen"); if ((envar != null) && ((envar[0] & 1) != 0)) { m_log.Debug("[XMREngine]: MMRScriptCompileSaveILGen: saving to " + asmFileName); asmFileWriter = File.CreateText(asmFileName); } } /* * Read object file to create ScriptObjCode object. * Maybe also write disassembly to a file for debugging. */ BinaryReader objFileReader = new BinaryReader(objFileStream); ScriptObjCode scriptObjCode = null; try { scriptObjCode = new ScriptObjCode(objFileReader, asmFileWriter, null); if (scriptObjCode != null) { scriptObjCode.fileDateUtc = File.GetLastWriteTimeUtc(objFileName); } } finally { objFileReader.Close(); if (asmFileWriter != null) { asmFileWriter.Flush(); asmFileWriter.Close(); } } /* * Maybe an old object file has reached its expiration date. */ if (oldObjFile && (scriptObjCode != null) && scriptObjCode.IsExpired()) { m_log.Debug("[XMREngine]: expiration reached on " + m_ScriptObjCodeKey + ", reloading"); m_ForceRecomp = true; scriptObjCode = Compile(); } return(scriptObjCode); }