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