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