MMRScriptObjWriter.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
  28. using System;
  29. using System.Collections.Generic;
  30. using System.IO;
  31. using System.Reflection;
  32. using System.Reflection.Emit;
  33. using System.Text;
  34. using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
  35. using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
  36. using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
  37. using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
  38. using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
  39. using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
  40. using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
  41. /**
  42. * @brief Wrapper class for ILGenerator.
  43. * It writes the object code to a file and can then make real ILGenerator calls
  44. * based on the file's contents.
  45. */
  46. namespace OpenSim.Region.ScriptEngine.Yengine
  47. {
  48. public enum ScriptObjWriterCode: byte
  49. {
  50. BegMethod, EndMethod, TheEnd,
  51. DclLabel, DclLocal, DclMethod, MarkLabel,
  52. EmitNull, EmitField, EmitLocal, EmitType, EmitLabel, EmitMethodExt,
  53. EmitMethodInt, EmitCtor, EmitDouble, EmitFloat, EmitInteger, EmitString,
  54. EmitLabels,
  55. BegExcBlk, BegCatBlk, BegFinBlk, EndExcBlk
  56. }
  57. public class ScriptObjWriter: ScriptMyILGen
  58. {
  59. private static readonly Dictionary<short, OpCode> opCodes = PopulateOpCodes();
  60. private static readonly Dictionary<string, Type> string2Type = PopulateS2T();
  61. private static readonly Dictionary<Type, string> type2String = PopulateT2S();
  62. private static readonly OpCode[] opCodesLdcI4M1P8 = new OpCode[]
  63. {
  64. OpCodes.Ldc_I4_M1, OpCodes.Ldc_I4_0, OpCodes.Ldc_I4_1, OpCodes.Ldc_I4_2, OpCodes.Ldc_I4_3,
  65. OpCodes.Ldc_I4_4, OpCodes.Ldc_I4_5, OpCodes.Ldc_I4_6, OpCodes.Ldc_I4_7, OpCodes.Ldc_I4_8
  66. };
  67. private BinaryWriter objFileWriter;
  68. private string lastErrorAtFile = "";
  69. private int lastErrorAtLine = 0;
  70. private int lastErrorAtPosn = 0;
  71. private readonly Dictionary<Type, string> sdTypesRev = new ();
  72. public int labelNumber = 0;
  73. public int localNumber = 0;
  74. public string methName { get; private set;}
  75. public Type retType;
  76. public Type[] argTypes;
  77. /**
  78. * @brief Begin function declaration
  79. * @param sdTypes = script-defined types
  80. * @param methName = name of the method being declared, eg, "Verify(array,list,string)"
  81. * @param retType = its return value type
  82. * @param argTypes[] = its argument types
  83. * @param objFileWriter = file to write its object code to
  84. *
  85. * After calling this function, the following functions should be called:
  86. * this.BegMethod ();
  87. * this.<as required> ();
  88. * this.EndMethod ();
  89. *
  90. * The design of this object is such that many constructors may be called,
  91. * but once a BegMethod() is called for one of the objects, no method may
  92. * called for any of the other objects until EndMethod() is called (or it
  93. * would break up the object stream for that method). But we need to have
  94. * many constructors possible so we get function headers at the beginning
  95. * of the object file in case there are forward references to the functions.
  96. */
  97. public ScriptObjWriter(TokenScript tokenScript, string methName, Type retType, Type[] argTypes, string[] argNames, BinaryWriter objFileWriter)
  98. {
  99. this.methName = methName;
  100. this.retType = retType;
  101. this.argTypes = argTypes;
  102. this.objFileWriter = objFileWriter;
  103. // Build list that translates system-defined types to script defined types.
  104. foreach(TokenDeclSDType sdt in tokenScript.sdSrcTypesValues)
  105. {
  106. Type sys = sdt.GetSysType();
  107. if(sys != null)
  108. sdTypesRev[sys] = sdt.longName.val;
  109. }
  110. // This tells the reader to call 'new DynamicMethod()' to create
  111. // the function header. Then any forward reference calls to this
  112. // method will have a MethodInfo struct to call.
  113. objFileWriter.Write((byte)ScriptObjWriterCode.DclMethod);
  114. objFileWriter.Write(methName);
  115. objFileWriter.Write(GetStrFromType(retType));
  116. int nArgs = argTypes.Length;
  117. objFileWriter.Write(nArgs);
  118. for(int i = 0; i < nArgs; i++)
  119. {
  120. objFileWriter.Write(GetStrFromType(argTypes[i]));
  121. objFileWriter.Write(argNames[i]);
  122. }
  123. }
  124. /**
  125. * @brief Begin outputting object code for the function
  126. */
  127. public void BegMethod()
  128. {
  129. // This tells the reader to call methodInfo.GetILGenerator()
  130. // so it can start writing CIL code for the method.
  131. objFileWriter.Write((byte)ScriptObjWriterCode.BegMethod);
  132. objFileWriter.Write(methName);
  133. }
  134. /**
  135. * @brief End of object code for the function
  136. */
  137. public void EndMethod()
  138. {
  139. // This tells the reader that all code for the method has
  140. // been written and so it will typically call CreateDelegate()
  141. // to finalize the method and create an entrypoint.
  142. objFileWriter.Write((byte)ScriptObjWriterCode.EndMethod);
  143. objFileWriter = null;
  144. }
  145. /**
  146. * @brief Declare a local variable for use by the function
  147. */
  148. public ScriptMyLocal DeclareLocal(Type type, string name)
  149. {
  150. ScriptMyLocal myLocal = new ()
  151. {
  152. type = type,
  153. name = name,
  154. number = localNumber++,
  155. isReferenced = true // so ScriptCollector won't optimize references away
  156. };
  157. return DeclareLocal(myLocal);
  158. }
  159. public ScriptMyLocal DeclareLocal(ScriptMyLocal myLocal)
  160. {
  161. objFileWriter.Write((byte)ScriptObjWriterCode.DclLocal);
  162. objFileWriter.Write(myLocal.number);
  163. objFileWriter.Write(myLocal.name);
  164. objFileWriter.Write(GetStrFromType(myLocal.type));
  165. return myLocal;
  166. }
  167. /**
  168. * @brief Define a label for use by the function
  169. */
  170. public ScriptMyLabel DefineLabel(string name)
  171. {
  172. ScriptMyLabel myLabel = new()
  173. {
  174. name = name,
  175. number = labelNumber++
  176. };
  177. return DefineLabel(myLabel);
  178. }
  179. public ScriptMyLabel DefineLabel(ScriptMyLabel myLabel)
  180. {
  181. objFileWriter.Write((byte)ScriptObjWriterCode.DclLabel);
  182. objFileWriter.Write(myLabel.number);
  183. objFileWriter.Write(myLabel.name);
  184. return myLabel;
  185. }
  186. /**
  187. * @brief try/catch blocks.
  188. */
  189. public void BeginExceptionBlock()
  190. {
  191. objFileWriter.Write((byte)ScriptObjWriterCode.BegExcBlk);
  192. }
  193. public void BeginCatchBlock(Type excType)
  194. {
  195. objFileWriter.Write((byte)ScriptObjWriterCode.BegCatBlk);
  196. objFileWriter.Write(GetStrFromType(excType));
  197. }
  198. public void BeginFinallyBlock()
  199. {
  200. objFileWriter.Write((byte)ScriptObjWriterCode.BegFinBlk);
  201. }
  202. public void EndExceptionBlock()
  203. {
  204. objFileWriter.Write((byte)ScriptObjWriterCode.EndExcBlk);
  205. }
  206. public void Emit(Token errorAt, OpCode opcode)
  207. {
  208. objFileWriter.Write((byte)ScriptObjWriterCode.EmitNull);
  209. WriteOpCode(errorAt, opcode);
  210. }
  211. public void Emit(Token errorAt, OpCode opcode, FieldInfo field)
  212. {
  213. objFileWriter.Write((byte)ScriptObjWriterCode.EmitField);
  214. WriteOpCode(errorAt, opcode);
  215. objFileWriter.Write(GetStrFromType(field.ReflectedType));
  216. objFileWriter.Write(field.Name);
  217. }
  218. public void Emit(Token errorAt, OpCode opcode, ScriptMyLocal myLocal)
  219. {
  220. objFileWriter.Write((byte)ScriptObjWriterCode.EmitLocal);
  221. WriteOpCode(errorAt, opcode);
  222. objFileWriter.Write(myLocal.number);
  223. }
  224. public void Emit(Token errorAt, OpCode opcode, Type type)
  225. {
  226. objFileWriter.Write((byte)ScriptObjWriterCode.EmitType);
  227. WriteOpCode(errorAt, opcode);
  228. objFileWriter.Write(GetStrFromType(type));
  229. }
  230. public void Emit(Token errorAt, OpCode opcode, ScriptMyLabel myLabel)
  231. {
  232. objFileWriter.Write((byte)ScriptObjWriterCode.EmitLabel);
  233. WriteOpCode(errorAt, opcode);
  234. objFileWriter.Write(myLabel.number);
  235. }
  236. public void Emit(Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels)
  237. {
  238. objFileWriter.Write((byte)ScriptObjWriterCode.EmitLabels);
  239. WriteOpCode(errorAt, opcode);
  240. int nLabels = myLabels.Length;
  241. objFileWriter.Write(nLabels);
  242. for(int i = 0; i < nLabels; i++)
  243. {
  244. objFileWriter.Write(myLabels[i].number);
  245. }
  246. }
  247. public void Emit(Token errorAt, OpCode opcode, ScriptObjWriter method)
  248. {
  249. if(method == null)
  250. throw new ArgumentNullException(nameof(method));
  251. objFileWriter.Write((byte)ScriptObjWriterCode.EmitMethodInt);
  252. WriteOpCode(errorAt, opcode);
  253. objFileWriter.Write(method.methName);
  254. }
  255. public void Emit(Token errorAt, OpCode opcode, MethodInfo method)
  256. {
  257. objFileWriter.Write((byte)ScriptObjWriterCode.EmitMethodExt);
  258. WriteOpCode(errorAt, opcode);
  259. objFileWriter.Write(method.Name);
  260. objFileWriter.Write(GetStrFromType(method.ReflectedType));
  261. ParameterInfo[] parms = method.GetParameters();
  262. int nArgs = parms.Length;
  263. objFileWriter.Write(nArgs);
  264. for(int i = 0; i < nArgs; i++)
  265. {
  266. objFileWriter.Write(GetStrFromType(parms[i].ParameterType));
  267. }
  268. }
  269. public void Emit(Token errorAt, OpCode opcode, ConstructorInfo ctor)
  270. {
  271. objFileWriter.Write((byte)ScriptObjWriterCode.EmitCtor);
  272. WriteOpCode(errorAt, opcode);
  273. objFileWriter.Write(GetStrFromType(ctor.ReflectedType));
  274. ParameterInfo[] parms = ctor.GetParameters();
  275. int nArgs = parms.Length;
  276. objFileWriter.Write(nArgs);
  277. for(int i = 0; i < nArgs; i++)
  278. {
  279. objFileWriter.Write(GetStrFromType(parms[i].ParameterType));
  280. }
  281. }
  282. public void Emit(Token errorAt, OpCode opcode, double value)
  283. {
  284. if(opcode != OpCodes.Ldc_R8)
  285. {
  286. throw new Exception("bad opcode " + opcode.ToString());
  287. }
  288. objFileWriter.Write((byte)ScriptObjWriterCode.EmitDouble);
  289. WriteOpCode(errorAt, opcode);
  290. objFileWriter.Write(value);
  291. }
  292. public void Emit(Token errorAt, OpCode opcode, float value)
  293. {
  294. if(opcode != OpCodes.Ldc_R4)
  295. {
  296. throw new Exception("bad opcode " + opcode.ToString());
  297. }
  298. objFileWriter.Write((byte)ScriptObjWriterCode.EmitFloat);
  299. WriteOpCode(errorAt, opcode);
  300. objFileWriter.Write(value);
  301. }
  302. public void Emit(Token errorAt, OpCode opcode, int value)
  303. {
  304. objFileWriter.Write((byte)ScriptObjWriterCode.EmitInteger);
  305. WriteOpCode(errorAt, opcode);
  306. objFileWriter.Write(value);
  307. }
  308. public void Emit(Token errorAt, OpCode opcode, string value)
  309. {
  310. objFileWriter.Write((byte)ScriptObjWriterCode.EmitString);
  311. WriteOpCode(errorAt, opcode);
  312. objFileWriter.Write(value);
  313. }
  314. /**
  315. * @brief Declare that the target of a label is the next instruction.
  316. */
  317. public void MarkLabel(ScriptMyLabel myLabel)
  318. {
  319. objFileWriter.Write((byte)ScriptObjWriterCode.MarkLabel);
  320. objFileWriter.Write(myLabel.number);
  321. }
  322. /**
  323. * @brief Write end-of-file marker to binary file.
  324. */
  325. public static void TheEnd(BinaryWriter objFileWriter)
  326. {
  327. objFileWriter.Write((byte)ScriptObjWriterCode.TheEnd);
  328. }
  329. /**
  330. * @brief Take an object file created by ScriptObjWriter() and convert it to a series of dynamic methods.
  331. * @param sdTypes = script-defined types
  332. * @param objReader = where to read object file from (as written by ScriptObjWriter above).
  333. * @param scriptObjCode.EndMethod = called for each method defined at the end of the methods definition
  334. * @param objectTokens = write disassemble/decompile data (or null if not wanted)
  335. */
  336. public static void CreateObjCode(Dictionary<string, TokenDeclSDType> sdTypes, BinaryReader objReader,
  337. ScriptObjCode scriptObjCode, ObjectTokens objectTokens)
  338. {
  339. Dictionary<string, DynamicMethod> methods = new ();
  340. DynamicMethod method = null;
  341. ILGenerator ilGen = null;
  342. Dictionary<int, Label> labels = new ();
  343. Dictionary<int, LocalBuilder> locals = new ();
  344. Dictionary<int, string> labelNames = new ();
  345. Dictionary<int, string> localNames = new ();
  346. object[] ilGenArg = new object[1];
  347. int offset;
  348. Dictionary<int, ScriptSrcLoc> srcLocs = null;
  349. string srcFile = "";
  350. int srcLine = 0;
  351. int srcPosn = 0;
  352. while(true)
  353. {
  354. // Get IL instruction offset at beginning of instruction.
  355. offset = ilGen is null ? 0 : ilGen.ILOffset;
  356. // Read and decode next internal format code from input file (.xmrobj file).
  357. ScriptObjWriterCode code = (ScriptObjWriterCode)objReader.ReadByte();
  358. switch(code)
  359. {
  360. // Reached end-of-file so we are all done.
  361. case ScriptObjWriterCode.TheEnd:
  362. return;
  363. // Beginning of method's contents.
  364. // Method must have already been declared via DclMethod
  365. // so all we need is its name to retrieve from methods[].
  366. case ScriptObjWriterCode.BegMethod:
  367. {
  368. string methName = objReader.ReadString();
  369. method = methods[methName];
  370. ilGen = method.GetILGenerator();
  371. ilGenArg[0] = ilGen;
  372. labels.Clear();
  373. locals.Clear();
  374. labelNames.Clear();
  375. localNames.Clear();
  376. srcLocs = new Dictionary<int, ScriptSrcLoc>();
  377. objectTokens?.BegMethod(method);
  378. break;
  379. }
  380. // End of method's contents (ie, an OpCodes.Ret was probably just output).
  381. // Call the callback to tell it the method is complete, and it can do whatever
  382. // it wants with the method.
  383. case ScriptObjWriterCode.EndMethod:
  384. {
  385. ilGen = null;
  386. ilGenArg[0] = null;
  387. scriptObjCode.EndMethod(method, srcLocs);
  388. srcLocs = null;
  389. objectTokens?.EndMethod();
  390. break;
  391. }
  392. // Declare a label for branching to.
  393. case ScriptObjWriterCode.DclLabel:
  394. {
  395. int number = objReader.ReadInt32();
  396. string name = objReader.ReadString();
  397. labels.Add(number, ilGen.DefineLabel());
  398. labelNames.Add(number, name + "_" + number.ToString());
  399. objectTokens?.DefineLabel(number, name);
  400. break;
  401. }
  402. // Declare a local variable to store into.
  403. case ScriptObjWriterCode.DclLocal:
  404. {
  405. int number = objReader.ReadInt32();
  406. string name = objReader.ReadString();
  407. string type = objReader.ReadString();
  408. Type syType = GetTypeFromStr(sdTypes, type);
  409. locals.Add(number, ilGen.DeclareLocal(syType));
  410. localNames.Add(number, name + "_" + number.ToString());
  411. objectTokens?.DefineLocal(number, name, type, syType);
  412. break;
  413. }
  414. // Declare a method that will subsequently be defined.
  415. // We create the DynamicMethod object at this point in case there
  416. // are forward references from other method bodies.
  417. case ScriptObjWriterCode.DclMethod:
  418. {
  419. string methName = objReader.ReadString();
  420. Type retType = GetTypeFromStr(sdTypes, objReader.ReadString());
  421. int nArgs = objReader.ReadInt32();
  422. Type[] argTypes = new Type[nArgs];
  423. string[] argNames = new string[nArgs];
  424. for(int i = 0; i < nArgs; i++)
  425. {
  426. argTypes[i] = GetTypeFromStr(sdTypes, objReader.ReadString());
  427. argNames[i] = objReader.ReadString();
  428. }
  429. methods.Add(methName, new DynamicMethod(methName, retType, argTypes));
  430. objectTokens?.DefineMethod(methName, retType, argTypes, argNames);
  431. break;
  432. }
  433. // Mark a previously declared label at this spot.
  434. case ScriptObjWriterCode.MarkLabel:
  435. {
  436. int number = objReader.ReadInt32();
  437. ilGen.MarkLabel(labels[number]);
  438. objectTokens?.MarkLabel(offset, number);
  439. break;
  440. }
  441. // Try/Catch blocks.
  442. case ScriptObjWriterCode.BegExcBlk:
  443. {
  444. ilGen.BeginExceptionBlock();
  445. objectTokens?.BegExcBlk(offset);
  446. break;
  447. }
  448. case ScriptObjWriterCode.BegCatBlk:
  449. {
  450. Type excType = GetTypeFromStr(sdTypes, objReader.ReadString());
  451. ilGen.BeginCatchBlock(excType);
  452. objectTokens?.BegCatBlk(offset, excType);
  453. break;
  454. }
  455. case ScriptObjWriterCode.BegFinBlk:
  456. {
  457. ilGen.BeginFinallyBlock();
  458. objectTokens?.BegFinBlk(offset);
  459. break;
  460. }
  461. case ScriptObjWriterCode.EndExcBlk:
  462. {
  463. ilGen.EndExceptionBlock();
  464. objectTokens?.EndExcBlk(offset);
  465. break;
  466. }
  467. // Emit an opcode with no operand.
  468. case ScriptObjWriterCode.EmitNull:
  469. {
  470. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  471. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  472. ilGen.Emit(opCode);
  473. objectTokens?.EmitNull(offset, opCode);
  474. break;
  475. }
  476. // Emit an opcode with a FieldInfo operand.
  477. case ScriptObjWriterCode.EmitField:
  478. {
  479. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  480. Type reflectedType = GetTypeFromStr(sdTypes, objReader.ReadString());
  481. string fieldName = objReader.ReadString();
  482. FieldInfo field = reflectedType.GetField(fieldName);
  483. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  484. ilGen.Emit(opCode, field);
  485. objectTokens?.EmitField(offset, opCode, field);
  486. break;
  487. }
  488. // Emit an opcode with a LocalBuilder operand.
  489. case ScriptObjWriterCode.EmitLocal:
  490. {
  491. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  492. int number = objReader.ReadInt32();
  493. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  494. ilGen.Emit(opCode, locals[number]);
  495. objectTokens?.EmitLocal(offset, opCode, number);
  496. break;
  497. }
  498. // Emit an opcode with a Type operand.
  499. case ScriptObjWriterCode.EmitType:
  500. {
  501. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  502. string name = objReader.ReadString();
  503. Type type = GetTypeFromStr(sdTypes, name);
  504. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  505. ilGen.Emit(opCode, type);
  506. objectTokens?.EmitType(offset, opCode, type);
  507. break;
  508. }
  509. // Emit an opcode with a Label operand.
  510. case ScriptObjWriterCode.EmitLabel:
  511. {
  512. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  513. int number = objReader.ReadInt32();
  514. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  515. ilGen.Emit(opCode, labels[number]);
  516. objectTokens?.EmitLabel(offset, opCode, number);
  517. break;
  518. }
  519. // Emit an opcode with a Label array operand.
  520. case ScriptObjWriterCode.EmitLabels:
  521. {
  522. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  523. int nLabels = objReader.ReadInt32();
  524. Label[] lbls = new Label[nLabels];
  525. int[] nums = new int[nLabels];
  526. for(int i = 0; i < nLabels; i++)
  527. {
  528. nums[i] = objReader.ReadInt32();
  529. lbls[i] = labels[nums[i]];
  530. }
  531. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  532. ilGen.Emit(opCode, lbls);
  533. objectTokens?.EmitLabels(offset, opCode, nums);
  534. break;
  535. }
  536. // Emit an opcode with a MethodInfo operand (such as a call) of an external function.
  537. case ScriptObjWriterCode.EmitMethodExt:
  538. {
  539. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  540. string methName = objReader.ReadString();
  541. Type methType = GetTypeFromStr(sdTypes, objReader.ReadString());
  542. int nArgs = objReader.ReadInt32();
  543. Type[] argTypes = new Type[nArgs];
  544. for(int i = 0; i < nArgs; i++)
  545. {
  546. argTypes[i] = GetTypeFromStr(sdTypes, objReader.ReadString());
  547. }
  548. MethodInfo methInfo = methType.GetMethod(methName, argTypes);
  549. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  550. ilGen.Emit(opCode, methInfo);
  551. objectTokens?.EmitMethod(offset, opCode, methInfo);
  552. break;
  553. }
  554. // Emit an opcode with a MethodInfo operand of an internal function
  555. // (previously declared via DclMethod).
  556. case ScriptObjWriterCode.EmitMethodInt:
  557. {
  558. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  559. string methName = objReader.ReadString();
  560. MethodInfo methInfo = methods[methName];
  561. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  562. ilGen.Emit(opCode, methInfo);
  563. objectTokens?.EmitMethod(offset, opCode, methInfo);
  564. break;
  565. }
  566. // Emit an opcode with a ConstructorInfo operand.
  567. case ScriptObjWriterCode.EmitCtor:
  568. {
  569. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  570. Type ctorType = GetTypeFromStr(sdTypes, objReader.ReadString());
  571. int nArgs = objReader.ReadInt32();
  572. Type[] argTypes = new Type[nArgs];
  573. for(int i = 0; i < nArgs; i++)
  574. {
  575. argTypes[i] = GetTypeFromStr(sdTypes, objReader.ReadString());
  576. }
  577. ConstructorInfo ctorInfo = ctorType.GetConstructor(argTypes);
  578. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  579. ilGen.Emit(opCode, ctorInfo);
  580. objectTokens?.EmitCtor(offset, opCode, ctorInfo);
  581. break;
  582. }
  583. // Emit an opcode with a constant operand of various types.
  584. case ScriptObjWriterCode.EmitDouble:
  585. {
  586. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  587. double value = objReader.ReadDouble();
  588. if(opCode != OpCodes.Ldc_R8)
  589. {
  590. throw new Exception("bad opcode " + opCode.ToString());
  591. }
  592. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  593. ilGen.Emit(opCode, value);
  594. objectTokens?.EmitDouble(offset, opCode, value);
  595. break;
  596. }
  597. case ScriptObjWriterCode.EmitFloat:
  598. {
  599. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  600. float value = objReader.ReadSingle();
  601. if(opCode != OpCodes.Ldc_R4)
  602. {
  603. throw new Exception("bad opcode " + opCode.ToString());
  604. }
  605. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  606. ilGen.Emit(opCode, value);
  607. objectTokens?.EmitFloat(offset, opCode, value);
  608. break;
  609. }
  610. case ScriptObjWriterCode.EmitInteger:
  611. {
  612. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  613. int value = objReader.ReadInt32();
  614. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  615. if(opCode == OpCodes.Ldc_I4)
  616. {
  617. if((value >= -1) && (value <= 8))
  618. {
  619. opCode = opCodesLdcI4M1P8[value + 1];
  620. ilGen.Emit(opCode);
  621. objectTokens?.EmitNull(offset, opCode);
  622. break;
  623. }
  624. if((value >= 0) && (value <= 127))
  625. {
  626. opCode = OpCodes.Ldc_I4_S;
  627. ilGen.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
  628. goto pemitint;
  629. }
  630. }
  631. ilGen.Emit(opCode, value);
  632. pemitint:
  633. objectTokens?.EmitInteger(offset, opCode, value);
  634. break;
  635. }
  636. case ScriptObjWriterCode.EmitString:
  637. {
  638. OpCode opCode = ReadOpCode(objReader, ref srcFile, ref srcLine, ref srcPosn);
  639. string value = objReader.ReadString();
  640. SaveSrcLoc(srcLocs, offset, srcFile, srcLine, srcPosn);
  641. ilGen.Emit(opCode, value);
  642. objectTokens?.EmitString(offset, opCode, value);
  643. break;
  644. }
  645. // Who knows what?
  646. default:
  647. throw new Exception("bad ScriptObjWriterCode " + ((byte)code).ToString());
  648. }
  649. }
  650. }
  651. /**
  652. * @brief Generate array to quickly translate OpCode.Value to full OpCode struct.
  653. */
  654. private static Dictionary<short, OpCode> PopulateOpCodes()
  655. {
  656. Dictionary<short, OpCode> opCodeDict = new ();
  657. FieldInfo[] fields = typeof(OpCodes).GetFields();
  658. for(int i = 0; i < fields.Length; i++)
  659. {
  660. OpCode opcode = (OpCode)fields[i].GetValue(null);
  661. opCodeDict.Add(opcode.Value, opcode);
  662. }
  663. return opCodeDict;
  664. }
  665. /**
  666. * @brief Write opcode out to file.
  667. */
  668. private void WriteOpCode(Token errorAt, OpCode opcode)
  669. {
  670. if(errorAt == null)
  671. {
  672. objFileWriter.Write("");
  673. objFileWriter.Write(lastErrorAtLine);
  674. objFileWriter.Write(lastErrorAtPosn);
  675. }
  676. else
  677. {
  678. if(errorAt.file != lastErrorAtFile)
  679. {
  680. objFileWriter.Write(errorAt.file);
  681. lastErrorAtFile = errorAt.file;
  682. }
  683. else
  684. {
  685. objFileWriter.Write("");
  686. }
  687. objFileWriter.Write(errorAt.line);
  688. objFileWriter.Write(errorAt.posn);
  689. lastErrorAtLine = errorAt.line;
  690. lastErrorAtPosn = errorAt.posn;
  691. }
  692. objFileWriter.Write(opcode.Value);
  693. }
  694. /**
  695. * @brief Read opcode in from file.
  696. */
  697. private static OpCode ReadOpCode(BinaryReader objReader, ref string srcFile, ref int srcLine, ref int srcPosn)
  698. {
  699. string f = objReader.ReadString();
  700. if(f != "")
  701. srcFile = f;
  702. srcLine = objReader.ReadInt32();
  703. srcPosn = objReader.ReadInt32();
  704. short value = objReader.ReadInt16();
  705. return opCodes[value];
  706. }
  707. /**
  708. * @brief Save an IL_offset -> source location translation entry
  709. * @param srcLocs = saved entries for the current function
  710. * @param offset = offset in IL object code for next instruction
  711. * @param src{File,Line,Posn} = location in source file corresponding to opcode
  712. * @returns with entry added to srcLocs
  713. */
  714. private static void SaveSrcLoc(Dictionary<int, ScriptSrcLoc> srcLocs, int offset, string srcFile, int srcLine, int srcPosn)
  715. {
  716. srcLocs[offset] = new ScriptSrcLoc()
  717. {
  718. file = srcFile,
  719. line = srcLine,
  720. posn = srcPosn
  721. };
  722. }
  723. /**
  724. * @brief Create type<->string conversions.
  725. * Using Type.AssemblyQualifiedName is horribly inefficient
  726. * and all our types should be known.
  727. */
  728. private static Dictionary<string, Type> PopulateS2T()
  729. {
  730. return new Dictionary<string, Type>
  731. {
  732. { "badcallx", typeof(ScriptBadCallNoException) },
  733. { "binopstr", typeof(BinOpStr) },
  734. { "bool", typeof(bool) },
  735. { "char", typeof(char) },
  736. { "delegate", typeof(Delegate) },
  737. { "delarr[]", typeof(Delegate[]) },
  738. { "double", typeof(double) },
  739. { "exceptn", typeof(Exception) },
  740. { "float", typeof(float) },
  741. { "htlist", typeof(HeapTrackerList) },
  742. { "htobject", typeof(HeapTrackerObject) },
  743. { "htstring", typeof(HeapTrackerString) },
  744. { "inlfunc", typeof(CompValuInline) },
  745. { "int", typeof(int) },
  746. { "int*", typeof(int).MakeByRefType() },
  747. { "intrlokd", typeof(System.Threading.Interlocked) },
  748. { "lslfloat", typeof(LSL_Float) },
  749. { "lslint", typeof(LSL_Integer) },
  750. { "lsllist", typeof(LSL_List) },
  751. { "lslrot", typeof(LSL_Rotation) },
  752. { "lslstr", typeof(LSL_String) },
  753. { "lslvec", typeof(LSL_Vector) },
  754. { "math", typeof(Math) },
  755. { "midround", typeof(MidpointRounding) },
  756. { "object", typeof(object) },
  757. { "object*", typeof(object).MakeByRefType() },
  758. { "object[]", typeof(object[]) },
  759. { "scrbase", typeof(ScriptBaseClass) },
  760. { "scrcode", typeof(ScriptCodeGen) },
  761. { "sdtclobj", typeof(XMRSDTypeClObj) },
  762. { "string", typeof(string) },
  763. { "typecast", typeof(TypeCast) },
  764. { "undstatx", typeof(ScriptUndefinedStateException) },
  765. { "void", typeof(void) },
  766. { "xmrarray", typeof(XMR_Array) },
  767. { "xmrinst", typeof(XMRInstAbstract) }
  768. };
  769. }
  770. private static Dictionary<Type, string> PopulateT2S()
  771. {
  772. Dictionary<string, Type> s2t = PopulateS2T();
  773. Dictionary<Type, string> t2s = new ();
  774. foreach(KeyValuePair<string, Type> kvp in s2t)
  775. {
  776. t2s.Add(kvp.Value, kvp.Key);
  777. }
  778. return t2s;
  779. }
  780. /**
  781. * @brief Add to list of internally recognized types.
  782. */
  783. public static void DefineInternalType(string name, Type type)
  784. {
  785. if(!string2Type.ContainsKey(name))
  786. {
  787. string2Type.Add(name, type);
  788. type2String.Add(type, name);
  789. }
  790. }
  791. private string GetStrFromType(Type t)
  792. {
  793. string s = GetStrFromTypeWork(t);
  794. return s;
  795. }
  796. private string GetStrFromTypeWork(Type t)
  797. {
  798. // internal fixed types like int and xmrarray etc
  799. if (type2String.TryGetValue(t, out string s))
  800. return s;
  801. // script-defined types
  802. if (sdTypesRev.TryGetValue(t, out s))
  803. return "sdt$" + s;
  804. // inline function types
  805. s = TokenDeclSDTypeDelegate.TryGetInlineName(t);
  806. if(s != null)
  807. return s;
  808. // last resort
  809. return t.AssemblyQualifiedName;
  810. }
  811. private static Type GetTypeFromStr(Dictionary<string, TokenDeclSDType> sdTypes, string s)
  812. {
  813. // internal fixed types like int and xmrarray etc
  814. if (string2Type.TryGetValue(s, out Type t))
  815. return t;
  816. // script-defined types
  817. if (s.StartsWith("sdt$"))
  818. return sdTypes[s[4..]].GetSysType();
  819. // inline function types
  820. t = TokenDeclSDTypeDelegate.TryGetInlineSysType(s);
  821. if(t != null)
  822. return t;
  823. // last resort
  824. return Type.GetType(s, true);
  825. }
  826. }
  827. public class ScriptSrcLoc
  828. {
  829. public string file;
  830. public int line;
  831. public int posn;
  832. }
  833. }