diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/ScriptEngine/YEngine/XMRObjectTokens.cs | 6225 |
1 files changed, 6225 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRObjectTokens.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRObjectTokens.cs new file mode 100644 index 0000000..b54e14c --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRObjectTokens.cs | |||
@@ -0,0 +1,6225 @@ | |||
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 | * Contains classes that disassemble or decompile an yobj file. | ||
46 | * See xmrengcomp.cx utility program. | ||
47 | */ | ||
48 | |||
49 | namespace OpenSim.Region.ScriptEngine.Yengine | ||
50 | { | ||
51 | /* | ||
52 | * Encapsulate object code for a method. | ||
53 | */ | ||
54 | public abstract class ObjectTokens | ||
55 | { | ||
56 | public ScriptObjCode scriptObjCode; | ||
57 | |||
58 | public ObjectTokens(ScriptObjCode scriptObjCode) | ||
59 | { | ||
60 | this.scriptObjCode = scriptObjCode; | ||
61 | } | ||
62 | |||
63 | public abstract void Close(); | ||
64 | public abstract void BegMethod(DynamicMethod method); | ||
65 | public abstract void EndMethod(); | ||
66 | public abstract void DefineLabel(int number, string name); | ||
67 | public abstract void DefineLocal(int number, string name, string type, Type syType); | ||
68 | public abstract void DefineMethod(string methName, Type retType, Type[] argTypes, string[] argNames); | ||
69 | public abstract void MarkLabel(int offset, int number); | ||
70 | public abstract void BegExcBlk(int offset); | ||
71 | public abstract void BegCatBlk(int offset, Type excType); | ||
72 | public abstract void BegFinBlk(int offset); | ||
73 | public abstract void EndExcBlk(int offset); | ||
74 | public abstract void EmitNull(int offset, OpCode opCode); | ||
75 | public abstract void EmitField(int offset, OpCode opCode, FieldInfo field); | ||
76 | public abstract void EmitLocal(int offset, OpCode opCode, int number); | ||
77 | public abstract void EmitType(int offset, OpCode opCode, Type type); | ||
78 | public abstract void EmitLabel(int offset, OpCode opCode, int number); | ||
79 | public abstract void EmitLabels(int offset, OpCode opCode, int[] numbers); | ||
80 | public abstract void EmitMethod(int offset, OpCode opCode, MethodInfo method); | ||
81 | public abstract void EmitCtor(int offset, OpCode opCode, ConstructorInfo ctor); | ||
82 | public abstract void EmitDouble(int offset, OpCode opCode, double value); | ||
83 | public abstract void EmitFloat(int offset, OpCode opCode, float value); | ||
84 | public abstract void EmitInteger(int offset, OpCode opCode, int value); | ||
85 | public abstract void EmitString(int offset, OpCode opCode, string value); | ||
86 | } | ||
87 | |||
88 | /******************\ | ||
89 | * DISASSEMBLER * | ||
90 | \******************/ | ||
91 | |||
92 | public class OTDisassemble: ObjectTokens | ||
93 | { | ||
94 | private static readonly int OPCSTRWIDTH = 12; | ||
95 | |||
96 | private Dictionary<int, string> labelNames; | ||
97 | private Dictionary<int, string> localNames; | ||
98 | private StringBuilder lbuf = new StringBuilder(); | ||
99 | private TextWriter twout; | ||
100 | |||
101 | public OTDisassemble(ScriptObjCode scriptObjCode, TextWriter twout) : base(scriptObjCode) | ||
102 | { | ||
103 | this.twout = twout; | ||
104 | } | ||
105 | |||
106 | public override void Close() | ||
107 | { | ||
108 | twout.WriteLine("TheEnd."); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * About to generate object code for this method. | ||
113 | */ | ||
114 | public override void BegMethod(DynamicMethod method) | ||
115 | { | ||
116 | labelNames = new Dictionary<int, string>(); | ||
117 | localNames = new Dictionary<int, string>(); | ||
118 | |||
119 | twout.WriteLine(""); | ||
120 | |||
121 | lbuf.Append(method.ReturnType.Name); | ||
122 | lbuf.Append(' '); | ||
123 | lbuf.Append(method.Name); | ||
124 | |||
125 | ParameterInfo[] parms = method.GetParameters(); | ||
126 | int nArgs = parms.Length; | ||
127 | lbuf.Append(" ("); | ||
128 | for(int i = 0; i < nArgs; i++) | ||
129 | { | ||
130 | if(i > 0) | ||
131 | lbuf.Append(", "); | ||
132 | lbuf.Append(parms[i].ParameterType.Name); | ||
133 | } | ||
134 | lbuf.Append(')'); | ||
135 | FlushLine(); | ||
136 | |||
137 | lbuf.Append('{'); | ||
138 | FlushLine(); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * Dump out reconstructed source for this method. | ||
143 | */ | ||
144 | public override void EndMethod() | ||
145 | { | ||
146 | lbuf.Append('}'); | ||
147 | FlushLine(); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * Add instructions to stream. | ||
152 | */ | ||
153 | public override void DefineLabel(int number, string name) | ||
154 | { | ||
155 | labelNames[number] = name + "$" + number; | ||
156 | } | ||
157 | |||
158 | public override void DefineLocal(int number, string name, string type, Type syType) | ||
159 | { | ||
160 | localNames[number] = name + "$" + number; | ||
161 | |||
162 | lbuf.Append(" "); | ||
163 | lbuf.Append(type.PadRight(OPCSTRWIDTH - 1)); | ||
164 | lbuf.Append(' '); | ||
165 | lbuf.Append(localNames[number]); | ||
166 | FlushLine(); | ||
167 | } | ||
168 | |||
169 | public override void DefineMethod(string methName, Type retType, Type[] argTypes, string[] argNames) | ||
170 | { | ||
171 | } | ||
172 | |||
173 | public override void MarkLabel(int offset, int number) | ||
174 | { | ||
175 | LinePrefix(offset); | ||
176 | lbuf.Append(labelNames[number]); | ||
177 | lbuf.Append(":"); | ||
178 | FlushLine(); | ||
179 | } | ||
180 | |||
181 | public override void BegExcBlk(int offset) | ||
182 | { | ||
183 | LinePrefix(offset); | ||
184 | lbuf.Append(" BeginExceptionBlock"); | ||
185 | FlushLine(); | ||
186 | } | ||
187 | |||
188 | public override void BegCatBlk(int offset, Type excType) | ||
189 | { | ||
190 | LinePrefix(offset); | ||
191 | lbuf.Append(" BeginCatchBlock "); | ||
192 | lbuf.Append(excType.Name); | ||
193 | FlushLine(); | ||
194 | } | ||
195 | |||
196 | public override void BegFinBlk(int offset) | ||
197 | { | ||
198 | LinePrefix(offset); | ||
199 | lbuf.Append(" BeginFinallyBlock"); | ||
200 | FlushLine(); | ||
201 | } | ||
202 | |||
203 | public override void EndExcBlk(int offset) | ||
204 | { | ||
205 | LinePrefix(offset); | ||
206 | lbuf.Append(" EndExceptionBlock"); | ||
207 | FlushLine(); | ||
208 | } | ||
209 | |||
210 | public override void EmitNull(int offset, OpCode opCode) | ||
211 | { | ||
212 | LinePrefix(offset, opCode); | ||
213 | FlushLine(); | ||
214 | } | ||
215 | |||
216 | public override void EmitField(int offset, OpCode opCode, FieldInfo field) | ||
217 | { | ||
218 | LinePrefix(offset, opCode); | ||
219 | lbuf.Append(field.DeclaringType.Name); | ||
220 | lbuf.Append(':'); | ||
221 | lbuf.Append(field.Name); | ||
222 | lbuf.Append(" -> "); | ||
223 | lbuf.Append(field.FieldType.Name); | ||
224 | lbuf.Append(" (field)"); | ||
225 | FlushLine(); | ||
226 | } | ||
227 | |||
228 | public override void EmitLocal(int offset, OpCode opCode, int number) | ||
229 | { | ||
230 | LinePrefix(offset, opCode); | ||
231 | lbuf.Append(localNames[number]); | ||
232 | lbuf.Append(" (local)"); | ||
233 | FlushLine(); | ||
234 | } | ||
235 | |||
236 | public override void EmitType(int offset, OpCode opCode, Type type) | ||
237 | { | ||
238 | LinePrefix(offset, opCode); | ||
239 | lbuf.Append(type.Name); | ||
240 | lbuf.Append(" (type)"); | ||
241 | FlushLine(); | ||
242 | } | ||
243 | |||
244 | public override void EmitLabel(int offset, OpCode opCode, int number) | ||
245 | { | ||
246 | LinePrefix(offset, opCode); | ||
247 | lbuf.Append(labelNames[number]); | ||
248 | lbuf.Append(" (label)"); | ||
249 | FlushLine(); | ||
250 | } | ||
251 | |||
252 | public override void EmitLabels(int offset, OpCode opCode, int[] numbers) | ||
253 | { | ||
254 | LinePrefix(offset, opCode); | ||
255 | |||
256 | int lineLen = lbuf.Length; | ||
257 | int nLabels = numbers.Length; | ||
258 | for(int i = 0; i < nLabels; i++) | ||
259 | { | ||
260 | if(i > 0) | ||
261 | { | ||
262 | lbuf.AppendLine(); | ||
263 | lbuf.Append(",".PadLeft(lineLen)); | ||
264 | } | ||
265 | lbuf.Append(labelNames[numbers[i]]); | ||
266 | } | ||
267 | |||
268 | FlushLine(); | ||
269 | } | ||
270 | |||
271 | public override void EmitMethod(int offset, OpCode opCode, MethodInfo method) | ||
272 | { | ||
273 | LinePrefix(offset, opCode); | ||
274 | |||
275 | ParameterInfo[] parms = method.GetParameters(); | ||
276 | int nArgs = parms.Length; | ||
277 | if(method.DeclaringType != null) | ||
278 | { | ||
279 | lbuf.Append(method.DeclaringType.Name); | ||
280 | lbuf.Append(':'); | ||
281 | } | ||
282 | lbuf.Append(method.Name); | ||
283 | lbuf.Append('('); | ||
284 | for(int i = 0; i < nArgs; i++) | ||
285 | { | ||
286 | if(i > 0) | ||
287 | lbuf.Append(","); | ||
288 | lbuf.Append(parms[i].ParameterType.Name); | ||
289 | } | ||
290 | lbuf.Append(") -> "); | ||
291 | lbuf.Append(method.ReturnType.Name); | ||
292 | |||
293 | FlushLine(); | ||
294 | } | ||
295 | |||
296 | public override void EmitCtor(int offset, OpCode opCode, ConstructorInfo ctor) | ||
297 | { | ||
298 | LinePrefix(offset, opCode); | ||
299 | |||
300 | ParameterInfo[] parms = ctor.GetParameters(); | ||
301 | int nArgs = parms.Length; | ||
302 | lbuf.Append(ctor.DeclaringType.Name); | ||
303 | lbuf.Append(":("); | ||
304 | for(int i = 0; i < nArgs; i++) | ||
305 | { | ||
306 | if(i > 0) | ||
307 | lbuf.Append(","); | ||
308 | lbuf.Append(parms[i].ParameterType.Name); | ||
309 | } | ||
310 | lbuf.Append(")"); | ||
311 | |||
312 | FlushLine(); | ||
313 | } | ||
314 | |||
315 | public override void EmitDouble(int offset, OpCode opCode, double value) | ||
316 | { | ||
317 | LinePrefix(offset, opCode); | ||
318 | lbuf.Append(value.ToString()); | ||
319 | lbuf.Append(" (double)"); | ||
320 | FlushLine(); | ||
321 | } | ||
322 | |||
323 | public override void EmitFloat(int offset, OpCode opCode, float value) | ||
324 | { | ||
325 | LinePrefix(offset, opCode); | ||
326 | lbuf.Append(value.ToString()); | ||
327 | lbuf.Append(" (float)"); | ||
328 | FlushLine(); | ||
329 | } | ||
330 | |||
331 | public override void EmitInteger(int offset, OpCode opCode, int value) | ||
332 | { | ||
333 | LinePrefix(offset, opCode); | ||
334 | lbuf.Append(value.ToString()); | ||
335 | lbuf.Append(" (int)"); | ||
336 | FlushLine(); | ||
337 | } | ||
338 | |||
339 | public override void EmitString(int offset, OpCode opCode, string value) | ||
340 | { | ||
341 | LinePrefix(offset, opCode); | ||
342 | lbuf.Append("\""); | ||
343 | lbuf.Append(value); | ||
344 | lbuf.Append("\" (string)"); | ||
345 | FlushLine(); | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * Put offset and opcode at beginning of line. | ||
350 | */ | ||
351 | private void LinePrefix(int offset, OpCode opCode) | ||
352 | { | ||
353 | LinePrefix(offset); | ||
354 | lbuf.Append(" "); | ||
355 | lbuf.Append(opCode.ToString().PadRight(OPCSTRWIDTH - 1)); | ||
356 | lbuf.Append(' '); | ||
357 | } | ||
358 | |||
359 | private void LinePrefix(int offset) | ||
360 | { | ||
361 | lbuf.Append(" "); | ||
362 | lbuf.Append(offset.ToString("X4")); | ||
363 | lbuf.Append(" "); | ||
364 | } | ||
365 | |||
366 | /** | ||
367 | * Flush line buffer to output file. | ||
368 | */ | ||
369 | private void FlushLine() | ||
370 | { | ||
371 | if(lbuf.Length > 0) | ||
372 | { | ||
373 | twout.WriteLine(lbuf.ToString()); | ||
374 | lbuf.Remove(0, lbuf.Length); | ||
375 | } | ||
376 | } | ||
377 | } | ||
378 | |||
379 | /****************\ | ||
380 | * DECOMPILER * | ||
381 | \****************/ | ||
382 | |||
383 | /** | ||
384 | * Note: The decompiler does not handle any xmroption extensions | ||
385 | * such as &&&, |||, ? operators and switch statements, as | ||
386 | * they do branches with a non-empty stack, which is way | ||
387 | * beyond this code's ability to analyze. | ||
388 | */ | ||
389 | |||
390 | public class OTDecompile: ObjectTokens | ||
391 | { | ||
392 | public const string _mainCallNo = "__mainCallNo$"; | ||
393 | public const string _callLabel = "__call_"; | ||
394 | public const string _callMode = "callMode"; | ||
395 | public const string _checkRunQuick = "CheckRunQuick"; | ||
396 | public const string _checkRunStack = "CheckRunStack"; | ||
397 | public const string _cmRestore = "__cmRestore"; | ||
398 | public const string _doBreak = "dobreak_"; | ||
399 | public const string _doCont = "docont_"; | ||
400 | public const string _doGblInit = "doGblInit"; | ||
401 | public const string _doLoop = "doloop_"; | ||
402 | public const string _ehArgs = "ehArgs"; | ||
403 | public const string _forBreak = "forbreak_"; | ||
404 | public const string _forCont = "forcont_"; | ||
405 | public const string _forLoop = "forloop_"; | ||
406 | public const string _globalvarinit = "$globalvarinit()"; | ||
407 | public const string _heapTrackerPop = "Pop"; | ||
408 | public const string _heapTrackerPush = "Push"; | ||
409 | public const string _ifDone = "ifdone_"; | ||
410 | public const string _ifElse = "ifelse_"; | ||
411 | public const string _llAbstemp = "llAbstemp"; | ||
412 | public const string _retlbl = "__retlbl"; | ||
413 | public const string _retval = "__retval$"; | ||
414 | public const string _whileBreak = "whilebreak_"; | ||
415 | public const string _whileCont = "whilecont_"; | ||
416 | public const string _whileLoop = "whileloop_"; | ||
417 | public const string _xmrinst = "__xmrinst"; | ||
418 | public const string _xmrinstlocal = "__xmrinst$"; | ||
419 | |||
420 | private const string INDENT = " "; | ||
421 | private const string LABELINDENT = " "; | ||
422 | |||
423 | private static Dictionary<string, string> typeTranslator = InitTypeTranslator(); | ||
424 | private static Dictionary<string, string> InitTypeTranslator() | ||
425 | { | ||
426 | Dictionary<string, string> d = new Dictionary<string, string>(); | ||
427 | d["Boolean"] = "integer"; | ||
428 | d["bool"] = "integer"; | ||
429 | d["Double"] = "float"; | ||
430 | d["double"] = "float"; | ||
431 | d["Int32"] = "integer"; | ||
432 | d["int"] = "integer"; | ||
433 | d["htlist"] = "list"; | ||
434 | d["htobject"] = "object"; | ||
435 | d["htstring"] = "string"; | ||
436 | d["lslfloat"] = "float"; | ||
437 | d["lslint"] = "integer"; | ||
438 | d["lsllist"] = "list"; | ||
439 | d["lslrot"] = "rotation"; | ||
440 | d["lslstr"] = "string"; | ||
441 | d["lslvec"] = "vector"; | ||
442 | d["Quaternion"] = "rotation"; | ||
443 | d["String"] = "string"; | ||
444 | d["Vector3"] = "vector"; | ||
445 | return d; | ||
446 | } | ||
447 | |||
448 | private Dictionary<int, OTLocal> eharglist; | ||
449 | private Dictionary<int, OTLabel> labels; | ||
450 | private Dictionary<int, OTLocal> locals; | ||
451 | private Dictionary<string, string[]> methargnames; | ||
452 | private LinkedList<OTCilInstr> cilinstrs; | ||
453 | private OTStmtBlock topBlock; | ||
454 | private Stack<OTOpnd> opstack; | ||
455 | private Stack<OTStmtBegExcBlk> trystack; | ||
456 | private Stack<OTStmtBlock> blockstack; | ||
457 | |||
458 | private int dupNo; | ||
459 | private DynamicMethod method; | ||
460 | private string laststate; | ||
461 | private TextWriter twout; | ||
462 | |||
463 | public OTDecompile(ScriptObjCode scriptObjCode, TextWriter twout) : base(scriptObjCode) | ||
464 | { | ||
465 | this.twout = twout; | ||
466 | twout.Write("xmroption dollarsigns;"); | ||
467 | methargnames = new Dictionary<string, string[]>(); | ||
468 | } | ||
469 | |||
470 | public override void Close() | ||
471 | { | ||
472 | if(laststate != null) | ||
473 | { | ||
474 | twout.Write("\n}"); | ||
475 | laststate = null; | ||
476 | } | ||
477 | twout.Write('\n'); | ||
478 | } | ||
479 | |||
480 | /** | ||
481 | * About to generate object code for this method. | ||
482 | */ | ||
483 | public override void BegMethod(DynamicMethod method) | ||
484 | { | ||
485 | this.method = method; | ||
486 | |||
487 | eharglist = new Dictionary<int, OTLocal>(); | ||
488 | labels = new Dictionary<int, OTLabel>(); | ||
489 | locals = new Dictionary<int, OTLocal>(); | ||
490 | cilinstrs = new LinkedList<OTCilInstr>(); | ||
491 | opstack = new Stack<OTOpnd>(); | ||
492 | trystack = new Stack<OTStmtBegExcBlk>(); | ||
493 | blockstack = new Stack<OTStmtBlock>(); | ||
494 | |||
495 | dupNo = 0; | ||
496 | } | ||
497 | |||
498 | /** | ||
499 | * Dump out reconstructed source for this method. | ||
500 | */ | ||
501 | public override void EndMethod() | ||
502 | { | ||
503 | // Convert CIL code to primitive statements. | ||
504 | // There are a bunch of labels and internal code such as call stack save restore. | ||
505 | topBlock = new OTStmtBlock(); | ||
506 | blockstack.Push(topBlock); | ||
507 | for(LinkedListNode<OTCilInstr> link = cilinstrs.First; link != null; link = link.Next) | ||
508 | { | ||
509 | link.Value.BuildStatements(this, link); | ||
510 | } | ||
511 | |||
512 | // Strip out stuff we don't want, such as references to callMode. | ||
513 | // This strips out stack frame capture and restore code. | ||
514 | topBlock.StripStuff(null); | ||
515 | |||
516 | // including a possible final return statement | ||
517 | // - delete if void return value | ||
518 | // - delete if returning __retval cuz we converted all __retval assignments to return statements | ||
519 | if((topBlock.blkstmts.Last != null) && (topBlock.blkstmts.Last.Value is OTStmtRet)) | ||
520 | { | ||
521 | OTStmtRet finalret = (OTStmtRet)topBlock.blkstmts.Last.Value; | ||
522 | if((finalret.value == null) || | ||
523 | ((finalret.value is OTOpndLocal) && | ||
524 | ((OTOpndLocal)finalret.value).local.name.StartsWith(_retval))) | ||
525 | { | ||
526 | topBlock.blkstmts.RemoveLast(); | ||
527 | } | ||
528 | } | ||
529 | |||
530 | // At this point, all behind-the-scenes references are removed except | ||
531 | // that the do/for/if/while blocks are represented by OTStmtCont-style | ||
532 | // if/jumps. So try to convert them to the higher-level structures. | ||
533 | topBlock.DetectDoForIfWhile(null); | ||
534 | |||
535 | // Final strip to get rid of unneeded @forbreak_<suffix>; labels and the like. | ||
536 | topBlock.StripStuff(null); | ||
537 | |||
538 | // Build reference counts so we don't output unneeded declarations, | ||
539 | // especially temps and internal variables. | ||
540 | foreach(OTLocal local in locals.Values) | ||
541 | { | ||
542 | local.nlclreads = 0; | ||
543 | local.nlclwrites = 0; | ||
544 | } | ||
545 | topBlock.CountRefs(); | ||
546 | for(IEnumerator<int> localenum = locals.Keys.GetEnumerator(); localenum.MoveNext();) | ||
547 | { | ||
548 | OTLocal local = locals[localenum.Current]; | ||
549 | if(((local.nlclreads | local.nlclwrites) == 0) || local.name.StartsWith(_xmrinstlocal)) | ||
550 | { | ||
551 | locals.Remove(localenum.Current); | ||
552 | localenum = locals.Keys.GetEnumerator(); | ||
553 | } | ||
554 | } | ||
555 | |||
556 | // Strip the $n off of local vars that are not ambiguous. | ||
557 | // Make sure they don't mask globals and arguments as well. | ||
558 | Dictionary<string, int> namecounts = new Dictionary<string, int>(); | ||
559 | foreach(Dictionary<int, string> varnames in scriptObjCode.globalVarNames.Values) | ||
560 | { | ||
561 | foreach(string varname in varnames.Values) | ||
562 | { | ||
563 | int count; | ||
564 | if(!namecounts.TryGetValue(varname, out count)) | ||
565 | count = 0; | ||
566 | namecounts[varname] = count + 1; | ||
567 | } | ||
568 | } | ||
569 | if(methargnames.ContainsKey(method.Name)) | ||
570 | { | ||
571 | foreach(string argname in methargnames[method.Name]) | ||
572 | { | ||
573 | int count; | ||
574 | if(!namecounts.TryGetValue(argname, out count)) | ||
575 | count = 0; | ||
576 | namecounts[argname] = count + 1; | ||
577 | } | ||
578 | } | ||
579 | foreach(OTLocal local in locals.Values) | ||
580 | { | ||
581 | int i = local.name.LastIndexOf('$'); | ||
582 | string name = local.name.Substring(0, i); | ||
583 | int count; | ||
584 | if(!namecounts.TryGetValue(name, out count)) | ||
585 | count = 0; | ||
586 | namecounts[name] = count + 1; | ||
587 | } | ||
588 | foreach(OTLocal local in locals.Values) | ||
589 | { | ||
590 | int i = local.name.LastIndexOf('$'); | ||
591 | string name = local.name.Substring(0, i); | ||
592 | int count = namecounts[name]; | ||
593 | if(count == 1) | ||
594 | local.name = name; | ||
595 | } | ||
596 | |||
597 | // Print out result. | ||
598 | if(method.Name == _globalvarinit) | ||
599 | { | ||
600 | GlobalsDump(); | ||
601 | } | ||
602 | else | ||
603 | { | ||
604 | MethodDump(); | ||
605 | } | ||
606 | } | ||
607 | |||
608 | /** | ||
609 | * Add instructions to stream. | ||
610 | */ | ||
611 | public override void DefineLabel(int number, string name) | ||
612 | { | ||
613 | labels.Add(number, new OTLabel(number, name)); | ||
614 | } | ||
615 | public override void DefineLocal(int number, string name, string type, Type syType) | ||
616 | { | ||
617 | locals.Add(number, new OTLocal(number, name, type)); | ||
618 | } | ||
619 | public override void DefineMethod(string methName, Type retType, Type[] argTypes, string[] argNames) | ||
620 | { | ||
621 | methargnames[methName] = argNames; | ||
622 | } | ||
623 | public override void MarkLabel(int offset, int number) | ||
624 | { | ||
625 | OTCilInstr label = labels[number]; | ||
626 | label.offset = offset; | ||
627 | cilinstrs.AddLast(label); | ||
628 | } | ||
629 | public override void BegExcBlk(int offset) | ||
630 | { | ||
631 | cilinstrs.AddLast(new OTCilBegExcBlk(offset)); | ||
632 | } | ||
633 | public override void BegCatBlk(int offset, Type excType) | ||
634 | { | ||
635 | cilinstrs.AddLast(new OTCilBegCatBlk(offset, excType)); | ||
636 | } | ||
637 | public override void BegFinBlk(int offset) | ||
638 | { | ||
639 | cilinstrs.AddLast(new OTCilBegFinBlk(offset)); | ||
640 | } | ||
641 | public override void EndExcBlk(int offset) | ||
642 | { | ||
643 | cilinstrs.AddLast(new OTCilEndExcBlk(offset)); | ||
644 | } | ||
645 | public override void EmitNull(int offset, OpCode opCode) | ||
646 | { | ||
647 | cilinstrs.AddLast(new OTCilNull(offset, opCode)); | ||
648 | } | ||
649 | public override void EmitField(int offset, OpCode opCode, FieldInfo field) | ||
650 | { | ||
651 | cilinstrs.AddLast(new OTCilField(offset, opCode, field)); | ||
652 | } | ||
653 | public override void EmitLocal(int offset, OpCode opCode, int number) | ||
654 | { | ||
655 | cilinstrs.AddLast(new OTCilLocal(offset, opCode, locals[number])); | ||
656 | } | ||
657 | public override void EmitType(int offset, OpCode opCode, Type type) | ||
658 | { | ||
659 | cilinstrs.AddLast(new OTCilType(offset, opCode, type)); | ||
660 | } | ||
661 | public override void EmitLabel(int offset, OpCode opCode, int number) | ||
662 | { | ||
663 | cilinstrs.AddLast(new OTCilLabel(offset, opCode, labels[number])); | ||
664 | } | ||
665 | public override void EmitLabels(int offset, OpCode opCode, int[] numbers) | ||
666 | { | ||
667 | OTLabel[] labelarray = new OTLabel[numbers.Length]; | ||
668 | for(int i = 0; i < numbers.Length; i++) | ||
669 | { | ||
670 | labelarray[i] = labels[numbers[i]]; | ||
671 | } | ||
672 | cilinstrs.AddLast(new OTCilLabels(offset, opCode, labelarray)); | ||
673 | } | ||
674 | public override void EmitMethod(int offset, OpCode opCode, MethodInfo method) | ||
675 | { | ||
676 | cilinstrs.AddLast(new OTCilMethod(offset, opCode, method)); | ||
677 | } | ||
678 | public override void EmitCtor(int offset, OpCode opCode, ConstructorInfo ctor) | ||
679 | { | ||
680 | cilinstrs.AddLast(new OTCilCtor(offset, opCode, ctor)); | ||
681 | } | ||
682 | public override void EmitDouble(int offset, OpCode opCode, double value) | ||
683 | { | ||
684 | cilinstrs.AddLast(new OTCilDouble(offset, opCode, value)); | ||
685 | } | ||
686 | public override void EmitFloat(int offset, OpCode opCode, float value) | ||
687 | { | ||
688 | cilinstrs.AddLast(new OTCilFloat(offset, opCode, value)); | ||
689 | } | ||
690 | public override void EmitInteger(int offset, OpCode opCode, int value) | ||
691 | { | ||
692 | cilinstrs.AddLast(new OTCilInteger(offset, opCode, value)); | ||
693 | } | ||
694 | public override void EmitString(int offset, OpCode opCode, string value) | ||
695 | { | ||
696 | cilinstrs.AddLast(new OTCilString(offset, opCode, value)); | ||
697 | } | ||
698 | |||
699 | /** | ||
700 | * Add the given statement to the end of the currently open block. | ||
701 | */ | ||
702 | public void AddLastStmt(OTStmt stmt) | ||
703 | { | ||
704 | blockstack.Peek().blkstmts.AddLast(stmt); | ||
705 | } | ||
706 | |||
707 | /** | ||
708 | * Generate output for $globalvarinit() function. | ||
709 | * Also outputs declarations for global variables. | ||
710 | */ | ||
711 | private void GlobalsDump() | ||
712 | { | ||
713 | // Scan $globalvarinit(). It should only have global var assignments in it. | ||
714 | // Also gather up list of variables it initializes. | ||
715 | bool badinit = false; | ||
716 | Dictionary<string, string> inittypes = new Dictionary<string, string>(); | ||
717 | foreach(OTStmt stmt in topBlock.blkstmts) | ||
718 | { | ||
719 | if(!(stmt is OTStmtStore)) | ||
720 | { | ||
721 | badinit = true; | ||
722 | break; | ||
723 | } | ||
724 | OTStmtStore store = (OTStmtStore)stmt; | ||
725 | if(!(store.varwr is OTOpndGlobal)) | ||
726 | { | ||
727 | badinit = true; | ||
728 | break; | ||
729 | } | ||
730 | OTOpndGlobal globalop = (OTOpndGlobal)store.varwr; | ||
731 | inittypes[globalop.PrintableString] = ""; | ||
732 | } | ||
733 | |||
734 | // Scan through list of all global variables in the script. | ||
735 | // Output declarations for those what don't have any init statement for them. | ||
736 | // Save the type for those that do have init statements. | ||
737 | bool first = true; | ||
738 | foreach(string iartypename in scriptObjCode.globalVarNames.Keys) | ||
739 | { | ||
740 | Dictionary<int, string> varnames = scriptObjCode.globalVarNames[iartypename]; | ||
741 | string typename = iartypename.ToLowerInvariant(); | ||
742 | if(typename.StartsWith("iar")) | ||
743 | typename = typename.Substring(3); | ||
744 | if(typename.EndsWith("s")) | ||
745 | typename = typename.Substring(0, typename.Length - 1); | ||
746 | foreach(string varname in varnames.Values) | ||
747 | { | ||
748 | if(!badinit && inittypes.ContainsKey(varname)) | ||
749 | { | ||
750 | inittypes[varname] = typename; | ||
751 | } | ||
752 | else | ||
753 | { | ||
754 | if(first) | ||
755 | twout.Write('\n'); | ||
756 | twout.Write('\n' + typename + ' ' + varname + ';'); | ||
757 | first = false; | ||
758 | } | ||
759 | } | ||
760 | } | ||
761 | |||
762 | // If $globalvarinit() has anything bad in it, output it as a function. | ||
763 | // Otherwise, output it as a series of global declarations with init values. | ||
764 | if(badinit) | ||
765 | { | ||
766 | MethodDump(); | ||
767 | } | ||
768 | else | ||
769 | { | ||
770 | foreach(OTStmt stmt in topBlock.blkstmts) | ||
771 | { | ||
772 | OTStmtStore store = (OTStmtStore)stmt; | ||
773 | OTOpndGlobal globalop = (OTOpndGlobal)store.varwr; | ||
774 | string name = globalop.PrintableString; | ||
775 | if(first) | ||
776 | twout.Write('\n'); | ||
777 | twout.Write('\n' + inittypes[name] + ' '); | ||
778 | store.PrintStmt(twout, ""); | ||
779 | first = false; | ||
780 | } | ||
781 | } | ||
782 | } | ||
783 | |||
784 | /** | ||
785 | * Generate output for other functions. | ||
786 | */ | ||
787 | private void MethodDump() | ||
788 | { | ||
789 | string indent; | ||
790 | |||
791 | // Event handlers don't have an argument list as such in the original | ||
792 | // code. Instead they have a series of assignments from ehargs[] to | ||
793 | // local variables. So make those local variables look like they are | ||
794 | // an argument list. | ||
795 | int i = method.Name.IndexOf(' '); | ||
796 | if(i >= 0) | ||
797 | { | ||
798 | // Maybe we have to output the state name. | ||
799 | string statename = method.Name.Substring(0, i); | ||
800 | string eventname = method.Name.Substring(++i); | ||
801 | |||
802 | if(laststate != statename) | ||
803 | { | ||
804 | if(laststate != null) | ||
805 | twout.Write("\n}"); | ||
806 | if(statename == "default") | ||
807 | { | ||
808 | twout.Write("\n\ndefault {"); | ||
809 | } | ||
810 | else | ||
811 | { | ||
812 | twout.Write("\n\nstate " + statename + " {"); | ||
813 | } | ||
814 | laststate = statename; | ||
815 | } | ||
816 | else | ||
817 | { | ||
818 | twout.Write('\n'); | ||
819 | } | ||
820 | |||
821 | // Output event name and argument list. | ||
822 | // Remove from locals list so they don't print below. | ||
823 | twout.Write('\n' + INDENT + eventname + " ("); | ||
824 | MethodInfo meth = typeof(IEventHandlers).GetMethod(eventname); | ||
825 | i = 0; | ||
826 | foreach(ParameterInfo pi in meth.GetParameters()) | ||
827 | { | ||
828 | // skip the first param cuz it's the XMRInstance arg | ||
829 | if(i > 0) | ||
830 | twout.Write(", "); | ||
831 | OTLocal local; | ||
832 | if(eharglist.TryGetValue(i, out local) && locals.ContainsKey(local.number)) | ||
833 | { | ||
834 | twout.Write(local.DumpString()); | ||
835 | locals.Remove(local.number); | ||
836 | } | ||
837 | else | ||
838 | { | ||
839 | // maybe the assignment was removed | ||
840 | // eg, because the local was write-only (not referenced) | ||
841 | // so substitute in placeholder that won't be referenced | ||
842 | twout.Write(AbbrType(pi.ParameterType) + " arg$" + (i + 1)); | ||
843 | } | ||
844 | i++; | ||
845 | } | ||
846 | twout.Write(')'); | ||
847 | |||
848 | // Indent method body by 4 spaces. | ||
849 | indent = INDENT; | ||
850 | } | ||
851 | else | ||
852 | { | ||
853 | // Maybe need to close out previous state. | ||
854 | if(laststate != null) | ||
855 | { | ||
856 | twout.Write("\n}"); | ||
857 | laststate = null; | ||
858 | } | ||
859 | |||
860 | // Output blank line and return type (if any). | ||
861 | twout.Write("\n\n"); | ||
862 | if(method.ReturnType != typeof(void)) | ||
863 | { | ||
864 | twout.Write(AbbrType(method.ReturnType) + ' '); | ||
865 | } | ||
866 | |||
867 | // Output method name and argument list. | ||
868 | int j = method.Name.IndexOf('('); | ||
869 | if(j < 0) | ||
870 | { | ||
871 | twout.Write(method.Name); | ||
872 | } | ||
873 | else | ||
874 | { | ||
875 | twout.Write(method.Name.Substring(0, j) + " ("); | ||
876 | bool first = true; | ||
877 | j = 0; | ||
878 | foreach(ParameterInfo pi in method.GetParameters()) | ||
879 | { | ||
880 | if(j > 0) | ||
881 | { // skip the XMRInstance arg$0 parameter | ||
882 | if(!first) | ||
883 | twout.Write(", "); | ||
884 | twout.Write(AbbrType(pi.ParameterType) + ' ' + MethArgName(j)); | ||
885 | first = false; | ||
886 | } | ||
887 | j++; | ||
888 | } | ||
889 | twout.Write(')'); | ||
890 | } | ||
891 | |||
892 | // Don't indent method body at all. | ||
893 | indent = ""; | ||
894 | } | ||
895 | |||
896 | // Output local variable declarations. | ||
897 | twout.Write('\n' + indent + '{'); | ||
898 | bool didOne = false; | ||
899 | foreach(OTLocal local in locals.Values) | ||
900 | { | ||
901 | twout.Write('\n' + indent + INDENT + local.DumpString() + "; // r:" + local.nlclreads + " w:" + local.nlclwrites); | ||
902 | didOne = true; | ||
903 | } | ||
904 | if(didOne) | ||
905 | twout.Write('\n'); | ||
906 | |||
907 | // Output statements. | ||
908 | if(topBlock.blkstmts.Count == 0) | ||
909 | { | ||
910 | twout.Write(" }"); | ||
911 | } | ||
912 | else | ||
913 | { | ||
914 | topBlock.PrintBodyAndEnd(twout, indent); | ||
915 | } | ||
916 | } | ||
917 | |||
918 | /** | ||
919 | * Get abbreviated type string. | ||
920 | */ | ||
921 | public static string AbbrType(Type type) | ||
922 | { | ||
923 | if(type == null) | ||
924 | return "null"; | ||
925 | return AbbrType(type.Name); | ||
926 | } | ||
927 | public static string AbbrType(string type) | ||
928 | { | ||
929 | if(type.StartsWith("OpenSim.Region.ScriptEngine.YEngine.")) | ||
930 | { | ||
931 | type = type.Substring(38); | ||
932 | int i = type.IndexOf(','); | ||
933 | if(i > 0) | ||
934 | type = type.Substring(0, i); | ||
935 | } | ||
936 | if(typeTranslator.ContainsKey(type)) | ||
937 | { | ||
938 | type = typeTranslator[type]; | ||
939 | } | ||
940 | return type; | ||
941 | } | ||
942 | |||
943 | /** | ||
944 | * Get current method's argument name. | ||
945 | */ | ||
946 | public string MethArgName(int index) | ||
947 | { | ||
948 | string[] argnames; | ||
949 | if(methargnames.TryGetValue(method.Name, out argnames) && (index < argnames.Length)) | ||
950 | { | ||
951 | return argnames[index]; | ||
952 | } | ||
953 | return "arg$" + index; | ||
954 | } | ||
955 | |||
956 | /** | ||
957 | * Strip svperflvovs (float) cast from rotation/vector values. | ||
958 | */ | ||
959 | public static OTOpnd StripFloatCast(OTOpnd op) | ||
960 | { | ||
961 | if(op is OTOpndCast) | ||
962 | { | ||
963 | OTOpndCast opcast = (OTOpndCast)op; | ||
964 | if((opcast.type == typeof(double)) && (opcast.value is OTOpndInt)) | ||
965 | { | ||
966 | return opcast.value; | ||
967 | } | ||
968 | } | ||
969 | return op; | ||
970 | } | ||
971 | |||
972 | /** | ||
973 | * Strip svperflvovs Brtrues so we don't end up with stuff like 'if (!! someint) ...'. | ||
974 | */ | ||
975 | public static OTOpnd StripBrtrue(OTOpnd op) | ||
976 | { | ||
977 | if(op is OTOpndUnOp) | ||
978 | { | ||
979 | OTOpndUnOp opunop = (OTOpndUnOp)op; | ||
980 | if(opunop.opCode == MyOp.Brtrue) | ||
981 | return opunop.value; | ||
982 | } | ||
983 | return op; | ||
984 | } | ||
985 | |||
986 | /* | ||
987 | * Local variable declaration. | ||
988 | */ | ||
989 | private class OTLocal | ||
990 | { | ||
991 | public int number; | ||
992 | public string name; | ||
993 | public string type; | ||
994 | |||
995 | public int nlclreads; | ||
996 | public int nlclwrites; | ||
997 | |||
998 | public OTLocal(int number, string name, string type) | ||
999 | { | ||
1000 | this.number = number; | ||
1001 | this.name = name.StartsWith("tmp$") ? name : name + "$" + number; | ||
1002 | this.type = type; | ||
1003 | } | ||
1004 | |||
1005 | public string DumpString() | ||
1006 | { | ||
1007 | return AbbrType(type) + ' ' + name; | ||
1008 | } | ||
1009 | } | ||
1010 | |||
1011 | /***********************************************\ | ||
1012 | * Tokens that are one-for-one with CIL code * | ||
1013 | \***********************************************/ | ||
1014 | |||
1015 | /* | ||
1016 | * Part of instruction stream. | ||
1017 | */ | ||
1018 | public abstract class OTCilInstr | ||
1019 | { | ||
1020 | public int offset; // cil offset | ||
1021 | |||
1022 | public OTCilInstr(int offset) | ||
1023 | { | ||
1024 | this.offset = offset; | ||
1025 | } | ||
1026 | |||
1027 | public abstract string DumpString(); | ||
1028 | public abstract void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link); | ||
1029 | |||
1030 | protected void CheckEmptyStack(OTDecompile decompile, string opMnemonic) | ||
1031 | { | ||
1032 | if(decompile.opstack.Count > 0) | ||
1033 | { | ||
1034 | Console.Error.WriteLine("CheckEmptyStack: " + decompile.method.Name + " 0x" + offset.ToString("X") + ": " + | ||
1035 | opMnemonic + " stack depth " + decompile.opstack.Count); | ||
1036 | } | ||
1037 | } | ||
1038 | } | ||
1039 | |||
1040 | /* | ||
1041 | * Label mark point. | ||
1042 | */ | ||
1043 | private class OTLabel: OTCilInstr | ||
1044 | { | ||
1045 | public int number; | ||
1046 | public string name; | ||
1047 | |||
1048 | public int lbljumps; | ||
1049 | |||
1050 | public OTLabel(int number, string name) : base(-1) | ||
1051 | { | ||
1052 | this.number = number; | ||
1053 | this.name = name; | ||
1054 | } | ||
1055 | |||
1056 | public string PrintableName | ||
1057 | { | ||
1058 | get | ||
1059 | { | ||
1060 | if(name.StartsWith(_doBreak)) | ||
1061 | return _doBreak + "$" + number; | ||
1062 | if(name.StartsWith(_doCont)) | ||
1063 | return _doCont + "$" + number; | ||
1064 | if(name.StartsWith(_forBreak)) | ||
1065 | return _forBreak + "$" + number; | ||
1066 | if(name.StartsWith(_forCont)) | ||
1067 | return _forCont + "$" + number; | ||
1068 | if(name.StartsWith(_whileBreak)) | ||
1069 | return _whileBreak + "$" + number; | ||
1070 | if(name.StartsWith(_whileCont)) | ||
1071 | return _whileCont + "$" + number; | ||
1072 | return name; | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | public override string DumpString() | ||
1077 | { | ||
1078 | return name + ":"; | ||
1079 | } | ||
1080 | |||
1081 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1082 | { | ||
1083 | OTStmtLabel.AddLast(decompile, this); | ||
1084 | } | ||
1085 | } | ||
1086 | |||
1087 | /* | ||
1088 | * 'try {' | ||
1089 | */ | ||
1090 | private class OTCilBegExcBlk: OTCilInstr | ||
1091 | { | ||
1092 | public LinkedList<OTCilBegCatBlk> catches = new LinkedList<OTCilBegCatBlk>(); | ||
1093 | |||
1094 | public OTCilBegExcBlk(int offset) : base(offset) | ||
1095 | { | ||
1096 | } | ||
1097 | |||
1098 | public override string DumpString() | ||
1099 | { | ||
1100 | return "try {"; | ||
1101 | } | ||
1102 | |||
1103 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1104 | { | ||
1105 | CheckEmptyStack(decompile, "try"); | ||
1106 | |||
1107 | // link the try itself onto outer block | ||
1108 | OTStmtBegExcBlk trystmt = new OTStmtBegExcBlk(); | ||
1109 | decompile.AddLastStmt(trystmt); | ||
1110 | |||
1111 | // subsequent statements go to the try block | ||
1112 | trystmt.tryblock = new OTStmtBlock(); | ||
1113 | decompile.trystack.Push(trystmt); | ||
1114 | decompile.blockstack.Push(trystmt.tryblock); | ||
1115 | } | ||
1116 | } | ||
1117 | |||
1118 | /* | ||
1119 | * '} catch (...) {' | ||
1120 | */ | ||
1121 | private class OTCilBegCatBlk: OTCilInstr | ||
1122 | { | ||
1123 | public Type excType; | ||
1124 | |||
1125 | public OTCilBegCatBlk(int offset, Type excType) : base(offset) | ||
1126 | { | ||
1127 | this.excType = excType; | ||
1128 | } | ||
1129 | |||
1130 | public override string DumpString() | ||
1131 | { | ||
1132 | return "} catch (" + AbbrType(excType) + ") {"; | ||
1133 | } | ||
1134 | |||
1135 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1136 | { | ||
1137 | CheckEmptyStack(decompile, "catch"); | ||
1138 | |||
1139 | // link the catch itself onto the try statement | ||
1140 | OTStmtBegExcBlk trystmt = decompile.trystack.Peek(); | ||
1141 | OTStmtBegCatBlk catstmt = new OTStmtBegCatBlk(excType); | ||
1142 | trystmt.catches.AddLast(catstmt); | ||
1143 | |||
1144 | // start capturing statements into the catch block | ||
1145 | catstmt.tryblock = trystmt; | ||
1146 | catstmt.catchblock = new OTStmtBlock(); | ||
1147 | decompile.blockstack.Pop(); | ||
1148 | decompile.blockstack.Push(catstmt.catchblock); | ||
1149 | |||
1150 | // fill the stack slot with something for the exception argument | ||
1151 | OTOpndDup dup = new OTOpndDup(++decompile.dupNo); | ||
1152 | decompile.opstack.Push(dup); | ||
1153 | } | ||
1154 | } | ||
1155 | |||
1156 | /* | ||
1157 | * '} finally {' | ||
1158 | */ | ||
1159 | private class OTCilBegFinBlk: OTCilInstr | ||
1160 | { | ||
1161 | public OTCilBegFinBlk(int offset) : base(offset) | ||
1162 | { | ||
1163 | } | ||
1164 | |||
1165 | public override string DumpString() | ||
1166 | { | ||
1167 | return "} finally {"; | ||
1168 | } | ||
1169 | |||
1170 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1171 | { | ||
1172 | CheckEmptyStack(decompile, "finally"); | ||
1173 | |||
1174 | // link the finally itself to the try statement | ||
1175 | OTStmtBegExcBlk trystmt = decompile.trystack.Peek(); | ||
1176 | OTStmtBegFinBlk finstmt = new OTStmtBegFinBlk(); | ||
1177 | trystmt.finblock = finstmt; | ||
1178 | |||
1179 | // start capturing statements into the finally block | ||
1180 | finstmt.tryblock = trystmt; | ||
1181 | finstmt.finblock = new OTStmtBlock(); | ||
1182 | decompile.blockstack.Pop(); | ||
1183 | decompile.blockstack.Push(finstmt.finblock); | ||
1184 | } | ||
1185 | } | ||
1186 | |||
1187 | /* | ||
1188 | * '}' end of try | ||
1189 | */ | ||
1190 | private class OTCilEndExcBlk: OTCilInstr | ||
1191 | { | ||
1192 | public OTCilEndExcBlk(int offset) : base(offset) | ||
1193 | { | ||
1194 | } | ||
1195 | |||
1196 | public override string DumpString() | ||
1197 | { | ||
1198 | return "} // end try"; | ||
1199 | } | ||
1200 | |||
1201 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1202 | { | ||
1203 | CheckEmptyStack(decompile, "endtry"); | ||
1204 | |||
1205 | // pop the try/catch/finally blocks from stacks | ||
1206 | decompile.blockstack.Pop(); | ||
1207 | decompile.trystack.Pop(); | ||
1208 | |||
1209 | // subsequent statements collect following the try | ||
1210 | } | ||
1211 | } | ||
1212 | |||
1213 | /* | ||
1214 | * Actual opcodes (instructions). | ||
1215 | */ | ||
1216 | private class OTCilNull: OTCilInstr | ||
1217 | { | ||
1218 | public MyOp opCode; | ||
1219 | |||
1220 | public OTCilNull(int offset, OpCode opCode) : base(offset) | ||
1221 | { | ||
1222 | this.opCode = MyOp.GetByName(opCode.Name); | ||
1223 | } | ||
1224 | |||
1225 | public override string DumpString() | ||
1226 | { | ||
1227 | return opCode.ToString(); | ||
1228 | } | ||
1229 | |||
1230 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1231 | { | ||
1232 | switch(opCode.ToString()) | ||
1233 | { | ||
1234 | case "conv.i1": | ||
1235 | case "conv.i2": | ||
1236 | case "conv.i4": | ||
1237 | case "conv.i8": | ||
1238 | { | ||
1239 | OTOpnd value = decompile.opstack.Pop(); | ||
1240 | decompile.opstack.Push(new OTOpndCast(typeof(int), value)); | ||
1241 | break; | ||
1242 | } | ||
1243 | case "conv.r4": | ||
1244 | case "conv.r8": | ||
1245 | { | ||
1246 | OTOpnd value = decompile.opstack.Pop(); | ||
1247 | decompile.opstack.Push(new OTOpndCast(typeof(double), value)); | ||
1248 | break; | ||
1249 | } | ||
1250 | case "dup": | ||
1251 | { | ||
1252 | OTOpnd value = decompile.opstack.Pop(); | ||
1253 | if(!(value is OTOpndDup)) | ||
1254 | { | ||
1255 | OTOpndDup dup = new OTOpndDup(++decompile.dupNo); | ||
1256 | OTStmtStore.AddLast(decompile, dup, value); | ||
1257 | value = dup; | ||
1258 | } | ||
1259 | decompile.opstack.Push(value); | ||
1260 | decompile.opstack.Push(value); | ||
1261 | break; | ||
1262 | } | ||
1263 | case "endfinally": | ||
1264 | break; | ||
1265 | case "ldarg.0": | ||
1266 | { | ||
1267 | decompile.opstack.Push(new OTOpndArg(0, false, decompile)); | ||
1268 | break; | ||
1269 | } | ||
1270 | case "ldarg.1": | ||
1271 | { | ||
1272 | decompile.opstack.Push(new OTOpndArg(1, false, decompile)); | ||
1273 | break; | ||
1274 | } | ||
1275 | case "ldarg.2": | ||
1276 | { | ||
1277 | decompile.opstack.Push(new OTOpndArg(2, false, decompile)); | ||
1278 | break; | ||
1279 | } | ||
1280 | case "ldarg.3": | ||
1281 | { | ||
1282 | decompile.opstack.Push(new OTOpndArg(3, false, decompile)); | ||
1283 | break; | ||
1284 | } | ||
1285 | case "ldc.i4.0": | ||
1286 | { | ||
1287 | decompile.opstack.Push(new OTOpndInt(0)); | ||
1288 | break; | ||
1289 | } | ||
1290 | case "ldc.i4.1": | ||
1291 | { | ||
1292 | decompile.opstack.Push(new OTOpndInt(1)); | ||
1293 | break; | ||
1294 | } | ||
1295 | case "ldc.i4.2": | ||
1296 | { | ||
1297 | decompile.opstack.Push(new OTOpndInt(2)); | ||
1298 | break; | ||
1299 | } | ||
1300 | case "ldc.i4.3": | ||
1301 | { | ||
1302 | decompile.opstack.Push(new OTOpndInt(3)); | ||
1303 | break; | ||
1304 | } | ||
1305 | case "ldc.i4.4": | ||
1306 | { | ||
1307 | decompile.opstack.Push(new OTOpndInt(4)); | ||
1308 | break; | ||
1309 | } | ||
1310 | case "ldc.i4.5": | ||
1311 | { | ||
1312 | decompile.opstack.Push(new OTOpndInt(5)); | ||
1313 | break; | ||
1314 | } | ||
1315 | case "ldc.i4.6": | ||
1316 | { | ||
1317 | decompile.opstack.Push(new OTOpndInt(6)); | ||
1318 | break; | ||
1319 | } | ||
1320 | case "ldc.i4.7": | ||
1321 | { | ||
1322 | decompile.opstack.Push(new OTOpndInt(7)); | ||
1323 | break; | ||
1324 | } | ||
1325 | case "ldc.i4.8": | ||
1326 | { | ||
1327 | decompile.opstack.Push(new OTOpndInt(8)); | ||
1328 | break; | ||
1329 | } | ||
1330 | case "ldc.i4.m1": | ||
1331 | { | ||
1332 | decompile.opstack.Push(new OTOpndInt(-1)); | ||
1333 | break; | ||
1334 | } | ||
1335 | case "ldelem.i4": | ||
1336 | case "ldelem.r4": | ||
1337 | case "ldelem.r8": | ||
1338 | case "ldelem.ref": | ||
1339 | { | ||
1340 | OTOpnd index = decompile.opstack.Pop(); | ||
1341 | OTOpnd array = decompile.opstack.Pop(); | ||
1342 | decompile.opstack.Push(OTOpndArrayElem.Make(array, index, false, decompile)); | ||
1343 | break; | ||
1344 | } | ||
1345 | case "ldnull": | ||
1346 | { | ||
1347 | decompile.opstack.Push(new OTOpndNull()); | ||
1348 | break; | ||
1349 | } | ||
1350 | case "neg": | ||
1351 | case "not": | ||
1352 | { | ||
1353 | OTOpnd value = decompile.opstack.Pop(); | ||
1354 | decompile.opstack.Push(OTOpndUnOp.Make(opCode, value)); | ||
1355 | break; | ||
1356 | } | ||
1357 | case "pop": | ||
1358 | { | ||
1359 | OTStmtVoid.AddLast(decompile, decompile.opstack.Pop()); | ||
1360 | break; | ||
1361 | } | ||
1362 | case "ret": | ||
1363 | { | ||
1364 | OTOpnd value = null; | ||
1365 | if(decompile.method.ReturnType != typeof(void)) | ||
1366 | { | ||
1367 | value = decompile.opstack.Pop(); | ||
1368 | } | ||
1369 | CheckEmptyStack(decompile); | ||
1370 | decompile.AddLastStmt(new OTStmtRet(value)); | ||
1371 | break; | ||
1372 | } | ||
1373 | case "stelem.i4": | ||
1374 | case "stelem.r8": | ||
1375 | case "stelem.ref": | ||
1376 | { | ||
1377 | OTOpnd value = decompile.opstack.Pop(); | ||
1378 | OTOpnd index = decompile.opstack.Pop(); | ||
1379 | OTOpnd array = decompile.opstack.Pop(); | ||
1380 | OTStmtStore.AddLast(decompile, OTOpndArrayElem.Make(array, index, false, decompile), value); | ||
1381 | break; | ||
1382 | } | ||
1383 | case "throw": | ||
1384 | { | ||
1385 | OTOpnd value = decompile.opstack.Pop(); | ||
1386 | CheckEmptyStack(decompile); | ||
1387 | decompile.AddLastStmt(new OTStmtThrow(value, decompile)); | ||
1388 | break; | ||
1389 | } | ||
1390 | case "add": | ||
1391 | case "and": | ||
1392 | case "ceq": | ||
1393 | case "cgt": | ||
1394 | case "cgt.un": | ||
1395 | case "clt": | ||
1396 | case "clt.un": | ||
1397 | case "div": | ||
1398 | case "div.un": | ||
1399 | case "mul": | ||
1400 | case "or": | ||
1401 | case "rem": | ||
1402 | case "rem.un": | ||
1403 | case "shl": | ||
1404 | case "shr": | ||
1405 | case "shr.un": | ||
1406 | case "sub": | ||
1407 | case "xor": | ||
1408 | { | ||
1409 | OTOpnd rite = decompile.opstack.Pop(); | ||
1410 | OTOpnd left = decompile.opstack.Pop(); | ||
1411 | decompile.opstack.Push(OTOpndBinOp.Make(left, opCode, rite)); | ||
1412 | break; | ||
1413 | } | ||
1414 | default: | ||
1415 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1416 | } | ||
1417 | } | ||
1418 | |||
1419 | protected void CheckEmptyStack(OTDecompile decompile) | ||
1420 | { | ||
1421 | CheckEmptyStack(decompile, opCode.ToString()); | ||
1422 | } | ||
1423 | } | ||
1424 | |||
1425 | private class OTCilField: OTCilNull | ||
1426 | { | ||
1427 | public FieldInfo field; | ||
1428 | |||
1429 | public OTCilField(int offset, OpCode opCode, FieldInfo field) : base(offset, opCode) | ||
1430 | { | ||
1431 | this.field = field; | ||
1432 | } | ||
1433 | |||
1434 | public override string DumpString() | ||
1435 | { | ||
1436 | return opCode.ToString() + ' ' + field.Name; | ||
1437 | } | ||
1438 | |||
1439 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1440 | { | ||
1441 | switch(opCode.ToString()) | ||
1442 | { | ||
1443 | case "ldfld": | ||
1444 | { | ||
1445 | OTOpnd obj = decompile.opstack.Pop(); | ||
1446 | decompile.opstack.Push(OTOpndField.Make(obj, field)); | ||
1447 | break; | ||
1448 | } | ||
1449 | case "ldsfld": | ||
1450 | { | ||
1451 | decompile.opstack.Push(new OTOpndSField(field)); | ||
1452 | break; | ||
1453 | } | ||
1454 | case "stfld": | ||
1455 | { | ||
1456 | OTOpnd val = decompile.opstack.Pop(); | ||
1457 | OTOpnd obj = decompile.opstack.Pop(); | ||
1458 | OTStmtStore.AddLast(decompile, OTOpndField.Make(obj, field), val); | ||
1459 | break; | ||
1460 | } | ||
1461 | case "stsfld": | ||
1462 | { | ||
1463 | OTOpnd val = decompile.opstack.Pop(); | ||
1464 | OTStmtStore.AddLast(decompile, new OTOpndSField(field), val); | ||
1465 | break; | ||
1466 | } | ||
1467 | default: | ||
1468 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1469 | } | ||
1470 | } | ||
1471 | } | ||
1472 | |||
1473 | private class OTCilLocal: OTCilNull | ||
1474 | { | ||
1475 | public OTLocal local; | ||
1476 | |||
1477 | public OTCilLocal(int offset, OpCode opCode, OTLocal local) : base(offset, opCode) | ||
1478 | { | ||
1479 | this.local = local; | ||
1480 | } | ||
1481 | |||
1482 | public override string DumpString() | ||
1483 | { | ||
1484 | return opCode.ToString() + ' ' + local.name; | ||
1485 | } | ||
1486 | |||
1487 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1488 | { | ||
1489 | switch(opCode.ToString()) | ||
1490 | { | ||
1491 | case "ldloc": | ||
1492 | { | ||
1493 | decompile.opstack.Push(new OTOpndLocal(local)); | ||
1494 | break; | ||
1495 | } | ||
1496 | case "ldloca": | ||
1497 | { | ||
1498 | decompile.opstack.Push(new OTOpndLocalRef(local)); | ||
1499 | break; | ||
1500 | } | ||
1501 | case "stloc": | ||
1502 | { | ||
1503 | OTOpnd val = decompile.opstack.Pop(); | ||
1504 | OTStmtStore.AddLast(decompile, new OTOpndLocal(local), val); | ||
1505 | break; | ||
1506 | } | ||
1507 | default: | ||
1508 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1509 | } | ||
1510 | } | ||
1511 | } | ||
1512 | |||
1513 | private class OTCilType: OTCilNull | ||
1514 | { | ||
1515 | public Type type; | ||
1516 | |||
1517 | public OTCilType(int offset, OpCode opCode, Type type) : base(offset, opCode) | ||
1518 | { | ||
1519 | this.type = type; | ||
1520 | } | ||
1521 | |||
1522 | public override string DumpString() | ||
1523 | { | ||
1524 | return opCode.ToString() + ' ' + AbbrType(type); | ||
1525 | } | ||
1526 | |||
1527 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1528 | { | ||
1529 | switch(opCode.ToString()) | ||
1530 | { | ||
1531 | case "box": | ||
1532 | { | ||
1533 | break; | ||
1534 | } | ||
1535 | case "castclass": | ||
1536 | case "unbox.any": | ||
1537 | { | ||
1538 | OTOpnd value = decompile.opstack.Pop(); | ||
1539 | decompile.opstack.Push(new OTOpndCast(type, value)); | ||
1540 | break; | ||
1541 | } | ||
1542 | case "ldelem": | ||
1543 | { | ||
1544 | OTOpnd index = decompile.opstack.Pop(); | ||
1545 | OTOpnd array = decompile.opstack.Pop(); | ||
1546 | decompile.opstack.Push(OTOpndArrayElem.Make(array, index, false, decompile)); | ||
1547 | break; | ||
1548 | } | ||
1549 | case "ldelema": | ||
1550 | { | ||
1551 | OTOpnd index = decompile.opstack.Pop(); | ||
1552 | OTOpnd array = decompile.opstack.Pop(); | ||
1553 | decompile.opstack.Push(OTOpndArrayElem.Make(array, index, true, decompile)); | ||
1554 | break; | ||
1555 | } | ||
1556 | case "newarr": | ||
1557 | { | ||
1558 | OTOpnd index = decompile.opstack.Pop(); | ||
1559 | decompile.opstack.Push(new OTOpndNewarr(type, index)); | ||
1560 | break; | ||
1561 | } | ||
1562 | case "stelem": | ||
1563 | { | ||
1564 | OTOpnd value = decompile.opstack.Pop(); | ||
1565 | OTOpnd index = decompile.opstack.Pop(); | ||
1566 | OTOpnd array = decompile.opstack.Pop(); | ||
1567 | OTStmtStore.AddLast(decompile, OTOpndArrayElem.Make(array, index, false, decompile), value); | ||
1568 | break; | ||
1569 | } | ||
1570 | default: | ||
1571 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1572 | } | ||
1573 | } | ||
1574 | } | ||
1575 | |||
1576 | private class OTCilLabel: OTCilNull | ||
1577 | { | ||
1578 | public OTLabel label; | ||
1579 | |||
1580 | public OTCilLabel(int offset, OpCode opCode, OTLabel label) : base(offset, opCode) | ||
1581 | { | ||
1582 | this.label = label; | ||
1583 | } | ||
1584 | |||
1585 | public override string DumpString() | ||
1586 | { | ||
1587 | return opCode.ToString() + ' ' + label.name; | ||
1588 | } | ||
1589 | |||
1590 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1591 | { | ||
1592 | switch(opCode.ToString()) | ||
1593 | { | ||
1594 | // We don't handle non-empty stack at branch points. | ||
1595 | // | ||
1596 | // So handle this case specially: | ||
1597 | // | ||
1598 | // dup | ||
1599 | // ldc.i4.0 | ||
1600 | // bge.s llAbstemp << we are here | ||
1601 | // neg | ||
1602 | // llAbstemp: | ||
1603 | // | ||
1604 | // becomes: | ||
1605 | // | ||
1606 | // call llAbs | ||
1607 | case "bge.s": | ||
1608 | { | ||
1609 | OTOpnd rite = decompile.opstack.Pop(); // alleged zero | ||
1610 | OTOpnd left = decompile.opstack.Pop(); // alleged dup | ||
1611 | |||
1612 | if((label.name == _llAbstemp) && (decompile.opstack.Count > 0)) | ||
1613 | { | ||
1614 | LinkedListNode<OTCilInstr> linkneg = link.Next; | ||
1615 | if((left is OTOpndDup) && (rite is OTOpndInt) && | ||
1616 | (linkneg != null) && (linkneg.Value is OTCilNull) && | ||
1617 | (((OTCilNull)linkneg.Value).opCode == MyOp.Neg)) | ||
1618 | { | ||
1619 | OTOpndInt riteint = (OTOpndInt)rite; | ||
1620 | LinkedListNode<OTCilInstr> linklbl = linkneg.Next; | ||
1621 | if((riteint.value == 0) && (linklbl != null) && (linklbl.Value is OTLabel) && | ||
1622 | (((OTLabel)linklbl.Value) == label)) | ||
1623 | { | ||
1624 | linkneg.List.Remove(linkneg); | ||
1625 | linklbl.List.Remove(linklbl); | ||
1626 | MethodInfo method = typeof(ScriptBaseClass).GetMethod("llAbs"); | ||
1627 | OTOpnd[] args = new OTOpnd[] { new OTOpndNull(), decompile.opstack.Pop() }; | ||
1628 | OTOpndCall.AddLast(decompile, method, args); | ||
1629 | break; | ||
1630 | } | ||
1631 | } | ||
1632 | } | ||
1633 | |||
1634 | CheckEmptyStack(decompile); | ||
1635 | OTOpnd valu = OTOpndBinOp.Make(left, opCode, rite); | ||
1636 | OTStmt jump = OTStmtJump.Make(label); | ||
1637 | decompile.AddLastStmt(new OTStmtCond(valu, jump)); | ||
1638 | break; | ||
1639 | } | ||
1640 | |||
1641 | case "beq": | ||
1642 | case "bge": | ||
1643 | case "bgt": | ||
1644 | case "ble": | ||
1645 | case "blt": | ||
1646 | case "bne.un": | ||
1647 | case "beq.s": | ||
1648 | case "bgt.s": | ||
1649 | case "ble.s": | ||
1650 | case "blt.s": | ||
1651 | case "bne.un.s": | ||
1652 | { | ||
1653 | OTOpnd rite = decompile.opstack.Pop(); | ||
1654 | OTOpnd left = decompile.opstack.Pop(); | ||
1655 | CheckEmptyStack(decompile); | ||
1656 | OTOpnd valu = OTOpndBinOp.Make(left, opCode, rite); | ||
1657 | OTStmt jump = OTStmtJump.Make(label); | ||
1658 | decompile.AddLastStmt(new OTStmtCond(valu, jump)); | ||
1659 | break; | ||
1660 | } | ||
1661 | case "brfalse": | ||
1662 | case "brfalse.s": | ||
1663 | case "brtrue": | ||
1664 | case "brtrue.s": | ||
1665 | { | ||
1666 | OTOpnd value = decompile.opstack.Pop(); | ||
1667 | CheckEmptyStack(decompile); | ||
1668 | OTOpnd valu = OTOpndUnOp.Make(opCode, value); | ||
1669 | OTStmt jump = OTStmtJump.Make(label); | ||
1670 | decompile.AddLastStmt(new OTStmtCond(valu, jump)); | ||
1671 | break; | ||
1672 | } | ||
1673 | case "br": | ||
1674 | case "br.s": | ||
1675 | case "leave": | ||
1676 | { | ||
1677 | CheckEmptyStack(decompile); | ||
1678 | OTStmt jump = OTStmtJump.Make(label); | ||
1679 | decompile.AddLastStmt(jump); | ||
1680 | break; | ||
1681 | } | ||
1682 | default: | ||
1683 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1684 | } | ||
1685 | } | ||
1686 | } | ||
1687 | |||
1688 | private class OTCilLabels: OTCilNull | ||
1689 | { | ||
1690 | public OTLabel[] labels; | ||
1691 | |||
1692 | public OTCilLabels(int offset, OpCode opCode, OTLabel[] labels) : base(offset, opCode) | ||
1693 | { | ||
1694 | this.labels = labels; | ||
1695 | } | ||
1696 | |||
1697 | public override string DumpString() | ||
1698 | { | ||
1699 | StringBuilder sb = new StringBuilder(); | ||
1700 | sb.Append(opCode.ToString()); | ||
1701 | foreach(OTLabel label in labels) | ||
1702 | { | ||
1703 | sb.Append(' '); | ||
1704 | sb.Append(label.name); | ||
1705 | } | ||
1706 | return sb.ToString(); | ||
1707 | } | ||
1708 | |||
1709 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1710 | { | ||
1711 | switch(opCode.ToString()) | ||
1712 | { | ||
1713 | case "switch": | ||
1714 | { | ||
1715 | OTOpnd value = decompile.opstack.Pop(); | ||
1716 | CheckEmptyStack(decompile); | ||
1717 | decompile.AddLastStmt(new OTStmtSwitch(value, labels)); | ||
1718 | break; | ||
1719 | } | ||
1720 | default: | ||
1721 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1722 | } | ||
1723 | } | ||
1724 | } | ||
1725 | |||
1726 | private class OTCilMethod: OTCilNull | ||
1727 | { | ||
1728 | public MethodInfo method; | ||
1729 | |||
1730 | public OTCilMethod(int offset, OpCode opCode, MethodInfo method) : base(offset, opCode) | ||
1731 | { | ||
1732 | this.method = method; | ||
1733 | } | ||
1734 | |||
1735 | public override string DumpString() | ||
1736 | { | ||
1737 | return opCode.ToString() + ' ' + method.Name; | ||
1738 | } | ||
1739 | |||
1740 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1741 | { | ||
1742 | switch(opCode.ToString()) | ||
1743 | { | ||
1744 | case "call": | ||
1745 | case "callvirt": | ||
1746 | { | ||
1747 | int nargs = method.GetParameters().Length; | ||
1748 | if(!method.IsStatic) | ||
1749 | nargs++; | ||
1750 | OTOpnd[] args = new OTOpnd[nargs]; | ||
1751 | for(int i = nargs; --i >= 0;) | ||
1752 | { | ||
1753 | args[i] = decompile.opstack.Pop(); | ||
1754 | } | ||
1755 | OTOpndCall.AddLast(decompile, method, args); | ||
1756 | break; | ||
1757 | } | ||
1758 | default: | ||
1759 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1760 | } | ||
1761 | } | ||
1762 | } | ||
1763 | |||
1764 | private class OTCilCtor: OTCilNull | ||
1765 | { | ||
1766 | public ConstructorInfo ctor; | ||
1767 | |||
1768 | public OTCilCtor(int offset, OpCode opCode, ConstructorInfo ctor) : base(offset, opCode) | ||
1769 | { | ||
1770 | this.ctor = ctor; | ||
1771 | } | ||
1772 | |||
1773 | public override string DumpString() | ||
1774 | { | ||
1775 | return opCode.ToString() + ' ' + AbbrType(ctor.DeclaringType); | ||
1776 | } | ||
1777 | |||
1778 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1779 | { | ||
1780 | switch(opCode.ToString()) | ||
1781 | { | ||
1782 | case "newobj": | ||
1783 | { | ||
1784 | int nargs = ctor.GetParameters().Length; | ||
1785 | OTOpnd[] args = new OTOpnd[nargs]; | ||
1786 | for(int i = nargs; --i >= 0;) | ||
1787 | { | ||
1788 | args[i] = decompile.opstack.Pop(); | ||
1789 | } | ||
1790 | decompile.opstack.Push(OTOpndNewobj.Make(ctor, args)); | ||
1791 | break; | ||
1792 | } | ||
1793 | default: | ||
1794 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1795 | } | ||
1796 | } | ||
1797 | } | ||
1798 | |||
1799 | private class OTCilDouble: OTCilNull | ||
1800 | { | ||
1801 | public double value; | ||
1802 | |||
1803 | public OTCilDouble(int offset, OpCode opCode, double value) : base(offset, opCode) | ||
1804 | { | ||
1805 | this.value = value; | ||
1806 | } | ||
1807 | |||
1808 | public override string DumpString() | ||
1809 | { | ||
1810 | return opCode.ToString() + ' ' + value; | ||
1811 | } | ||
1812 | |||
1813 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1814 | { | ||
1815 | switch(opCode.ToString()) | ||
1816 | { | ||
1817 | case "ldc.r8": | ||
1818 | { | ||
1819 | decompile.opstack.Push(new OTOpndDouble(value)); | ||
1820 | break; | ||
1821 | } | ||
1822 | default: | ||
1823 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1824 | } | ||
1825 | } | ||
1826 | } | ||
1827 | |||
1828 | private class OTCilFloat: OTCilNull | ||
1829 | { | ||
1830 | public float value; | ||
1831 | |||
1832 | public OTCilFloat(int offset, OpCode opCode, float value) : base(offset, opCode) | ||
1833 | { | ||
1834 | this.value = value; | ||
1835 | } | ||
1836 | |||
1837 | public override string DumpString() | ||
1838 | { | ||
1839 | return opCode.ToString() + ' ' + value; | ||
1840 | } | ||
1841 | |||
1842 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1843 | { | ||
1844 | switch(opCode.ToString()) | ||
1845 | { | ||
1846 | case "ldc.r4": | ||
1847 | { | ||
1848 | decompile.opstack.Push(new OTOpndFloat(value)); | ||
1849 | break; | ||
1850 | } | ||
1851 | default: | ||
1852 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1853 | } | ||
1854 | } | ||
1855 | } | ||
1856 | |||
1857 | private class OTCilInteger: OTCilNull | ||
1858 | { | ||
1859 | public int value; | ||
1860 | |||
1861 | public OTCilInteger(int offset, OpCode opCode, int value) : base(offset, opCode) | ||
1862 | { | ||
1863 | this.value = value; | ||
1864 | } | ||
1865 | |||
1866 | public override string DumpString() | ||
1867 | { | ||
1868 | return opCode.ToString() + ' ' + value; | ||
1869 | } | ||
1870 | |||
1871 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1872 | { | ||
1873 | switch(opCode.ToString()) | ||
1874 | { | ||
1875 | case "ldarg": | ||
1876 | case "ldarg.s": | ||
1877 | { | ||
1878 | decompile.opstack.Push(new OTOpndArg(value, false, decompile)); | ||
1879 | break; | ||
1880 | } | ||
1881 | case "ldarga": | ||
1882 | case "ldarga.s": | ||
1883 | { | ||
1884 | decompile.opstack.Push(new OTOpndArg(value, true, decompile)); | ||
1885 | break; | ||
1886 | } | ||
1887 | case "ldc.i4": | ||
1888 | case "ldc.i4.s": | ||
1889 | { | ||
1890 | decompile.opstack.Push(new OTOpndInt(value)); | ||
1891 | break; | ||
1892 | } | ||
1893 | case "starg": | ||
1894 | { | ||
1895 | OTOpnd val = decompile.opstack.Pop(); | ||
1896 | OTStmtStore.AddLast(decompile, new OTOpndArg(value, false, decompile), val); | ||
1897 | break; | ||
1898 | } | ||
1899 | default: | ||
1900 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1901 | } | ||
1902 | } | ||
1903 | } | ||
1904 | |||
1905 | private class OTCilString: OTCilNull | ||
1906 | { | ||
1907 | public string value; | ||
1908 | |||
1909 | public OTCilString(int offset, OpCode opCode, string value) : base(offset, opCode) | ||
1910 | { | ||
1911 | this.value = value; | ||
1912 | } | ||
1913 | |||
1914 | public override string DumpString() | ||
1915 | { | ||
1916 | StringBuilder sb = new StringBuilder(); | ||
1917 | sb.Append(opCode.ToString()); | ||
1918 | sb.Append(' '); | ||
1919 | TokenDeclInline.PrintParamString(sb, value); | ||
1920 | return sb.ToString(); | ||
1921 | } | ||
1922 | |||
1923 | public override void BuildStatements(OTDecompile decompile, LinkedListNode<OTCilInstr> link) | ||
1924 | { | ||
1925 | switch(opCode.ToString()) | ||
1926 | { | ||
1927 | case "ldstr": | ||
1928 | { | ||
1929 | decompile.opstack.Push(new OTOpndString(value)); | ||
1930 | break; | ||
1931 | } | ||
1932 | default: | ||
1933 | throw new Exception("unknown opcode " + opCode.ToString()); | ||
1934 | } | ||
1935 | } | ||
1936 | } | ||
1937 | |||
1938 | /***************************************\ | ||
1939 | * Tokens what are on operand stack. * | ||
1940 | \***************************************/ | ||
1941 | |||
1942 | public abstract class OTOpnd | ||
1943 | { | ||
1944 | |||
1945 | /** | ||
1946 | * See if it possibly has any side effects. | ||
1947 | */ | ||
1948 | public abstract bool HasSideEffects | ||
1949 | { | ||
1950 | get; | ||
1951 | } | ||
1952 | |||
1953 | /** | ||
1954 | * Increment reference counts. | ||
1955 | */ | ||
1956 | public virtual void CountRefs(bool writing) | ||
1957 | { | ||
1958 | } | ||
1959 | |||
1960 | /** | ||
1961 | * If this operand is a 'by reference' operand, | ||
1962 | * return the corresponding 'by value' operand. | ||
1963 | */ | ||
1964 | public virtual OTOpnd GetNonByRefOpnd() | ||
1965 | { | ||
1966 | return this; | ||
1967 | } | ||
1968 | |||
1969 | /** | ||
1970 | * If this operand is same as oldopnd, replace it with newopnd. | ||
1971 | * | ||
1972 | * This default just does a shallow search which is ok if this operand does not have any sub-operands. | ||
1973 | * But it must be overridden for a deep search if this operand has any sub-operands. | ||
1974 | */ | ||
1975 | public virtual OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
1976 | { | ||
1977 | if(SameAs(oldopnd)) | ||
1978 | { | ||
1979 | rc = true; | ||
1980 | return newopnd; | ||
1981 | } | ||
1982 | return this; | ||
1983 | } | ||
1984 | |||
1985 | /** | ||
1986 | * See if the two operands are the same value. | ||
1987 | * Note that calls might have side-effects so are never the same. | ||
1988 | */ | ||
1989 | public abstract bool SameAs(OTOpnd other); | ||
1990 | |||
1991 | /** | ||
1992 | * Get a printable string representation of the operand. | ||
1993 | */ | ||
1994 | public abstract string PrintableString | ||
1995 | { | ||
1996 | get; | ||
1997 | } | ||
1998 | } | ||
1999 | |||
2000 | /** | ||
2001 | * Argument variable. | ||
2002 | */ | ||
2003 | private class OTOpndArg: OTOpnd | ||
2004 | { | ||
2005 | public int index; | ||
2006 | public bool byref; | ||
2007 | |||
2008 | private OTDecompile decompile; | ||
2009 | |||
2010 | public OTOpndArg(int index, bool byref, OTDecompile decompile) | ||
2011 | { | ||
2012 | this.index = index; | ||
2013 | this.byref = byref; | ||
2014 | this.decompile = decompile; | ||
2015 | } | ||
2016 | |||
2017 | public override bool HasSideEffects | ||
2018 | { | ||
2019 | get | ||
2020 | { | ||
2021 | return false; | ||
2022 | } | ||
2023 | } | ||
2024 | |||
2025 | public override OTOpnd GetNonByRefOpnd() | ||
2026 | { | ||
2027 | if(!byref) | ||
2028 | return this; | ||
2029 | return new OTOpndArg(index, false, decompile); | ||
2030 | } | ||
2031 | |||
2032 | public override bool SameAs(OTOpnd other) | ||
2033 | { | ||
2034 | if(!(other is OTOpndArg)) | ||
2035 | return false; | ||
2036 | return (((OTOpndArg)other).byref == byref) && (((OTOpndArg)other).index == index); | ||
2037 | } | ||
2038 | |||
2039 | public override string PrintableString | ||
2040 | { | ||
2041 | get | ||
2042 | { | ||
2043 | string argname = decompile.MethArgName(index); | ||
2044 | return byref ? ("ref " + argname) : argname; | ||
2045 | } | ||
2046 | } | ||
2047 | } | ||
2048 | |||
2049 | /** | ||
2050 | * Element of an array. | ||
2051 | */ | ||
2052 | private class OTOpndArrayElem: OTOpnd | ||
2053 | { | ||
2054 | public bool byref; | ||
2055 | public OTOpnd array; | ||
2056 | public OTOpnd index; | ||
2057 | |||
2058 | public static OTOpnd Make(OTOpnd array, OTOpnd index, bool byref, OTDecompile decompile) | ||
2059 | { | ||
2060 | // arg$0.glblVars.iar<type>[<intconst>] is a reference to a global variable | ||
2061 | // likewise so is __xmrinst.glblVars.iar<type>[<intconst>] | ||
2062 | if((array is OTOpndField) && (index is OTOpndInt)) | ||
2063 | { | ||
2064 | // arrayfield = (arg$0.glblVars).iar<type> | ||
2065 | // arrayfieldobj = arg$0.glblVars | ||
2066 | // iartypename = iar<type> | ||
2067 | OTOpndField arrayfield = (OTOpndField)array; | ||
2068 | OTOpnd arrayfieldobj = arrayfield.obj; | ||
2069 | string iartypename = arrayfield.field.Name; | ||
2070 | |||
2071 | // See if they are what they are supposed to be. | ||
2072 | if((arrayfieldobj is OTOpndField) && iartypename.StartsWith("iar")) | ||
2073 | { | ||
2074 | // arrayfieldobjfield = arg$0.glblVars | ||
2075 | OTOpndField arrayfieldobjfield = (OTOpndField)arrayfieldobj; | ||
2076 | |||
2077 | // See if the parts are what they are supposed to be. | ||
2078 | if(IsArg0OrXMRInst(arrayfieldobjfield.obj) && (arrayfieldobjfield.field.Name == "glblVars")) | ||
2079 | { | ||
2080 | // Everything matches up, make a global variable instead of an array reference. | ||
2081 | return new OTOpndGlobal(iartypename, ((OTOpndInt)index).value, byref, decompile.scriptObjCode); | ||
2082 | } | ||
2083 | } | ||
2084 | } | ||
2085 | |||
2086 | // Other array reference. | ||
2087 | OTOpndArrayElem it = new OTOpndArrayElem(); | ||
2088 | it.array = array; | ||
2089 | it.index = index; | ||
2090 | it.byref = byref; | ||
2091 | return it; | ||
2092 | } | ||
2093 | |||
2094 | private OTOpndArrayElem() | ||
2095 | { | ||
2096 | } | ||
2097 | |||
2098 | public override bool HasSideEffects | ||
2099 | { | ||
2100 | get | ||
2101 | { | ||
2102 | return array.HasSideEffects || index.HasSideEffects; | ||
2103 | } | ||
2104 | } | ||
2105 | |||
2106 | public override void CountRefs(bool writing) | ||
2107 | { | ||
2108 | array.CountRefs(false); | ||
2109 | index.CountRefs(false); | ||
2110 | } | ||
2111 | |||
2112 | public override OTOpnd GetNonByRefOpnd() | ||
2113 | { | ||
2114 | if(!byref) | ||
2115 | return this; | ||
2116 | OTOpndArrayElem it = new OTOpndArrayElem(); | ||
2117 | it.array = array; | ||
2118 | it.index = index; | ||
2119 | return it; | ||
2120 | } | ||
2121 | |||
2122 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
2123 | { | ||
2124 | if(SameAs(oldopnd)) | ||
2125 | { | ||
2126 | rc = true; | ||
2127 | return newopnd; | ||
2128 | } | ||
2129 | array = array.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
2130 | index = index.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
2131 | return this; | ||
2132 | } | ||
2133 | |||
2134 | public override bool SameAs(OTOpnd other) | ||
2135 | { | ||
2136 | if(!(other is OTOpndArrayElem)) | ||
2137 | return false; | ||
2138 | OTOpndArrayElem otherae = (OTOpndArrayElem)other; | ||
2139 | return array.SameAs(otherae.array) && index.SameAs(otherae.index); | ||
2140 | } | ||
2141 | |||
2142 | public override string PrintableString | ||
2143 | { | ||
2144 | get | ||
2145 | { | ||
2146 | return (byref ? "ref " : "") + array.PrintableString + "[" + index.PrintableString + "]"; | ||
2147 | } | ||
2148 | } | ||
2149 | |||
2150 | /** | ||
2151 | * See if the argument is a reference to arg$0 or __xmrinst | ||
2152 | */ | ||
2153 | public static bool IsArg0OrXMRInst(OTOpnd obj) | ||
2154 | { | ||
2155 | if(obj is OTOpndArg) | ||
2156 | { | ||
2157 | OTOpndArg objarg = (OTOpndArg)obj; | ||
2158 | return objarg.index == 0; | ||
2159 | } | ||
2160 | if(obj is OTOpndLocal) | ||
2161 | { | ||
2162 | OTOpndLocal objlcl = (OTOpndLocal)obj; | ||
2163 | return objlcl.local.name.StartsWith(_xmrinstlocal); | ||
2164 | } | ||
2165 | return false; | ||
2166 | } | ||
2167 | } | ||
2168 | |||
2169 | /** | ||
2170 | * Binary operator. | ||
2171 | */ | ||
2172 | private class OTOpndBinOp: OTOpnd | ||
2173 | { | ||
2174 | public OTOpnd left; | ||
2175 | public MyOp opCode; | ||
2176 | public OTOpnd rite; | ||
2177 | |||
2178 | private static Dictionary<string, string> xor1ops = InitXor1Ops(); | ||
2179 | |||
2180 | private static Dictionary<string, string> InitXor1Ops() | ||
2181 | { | ||
2182 | Dictionary<string, string> d = new Dictionary<string, string>(); | ||
2183 | d["ceq"] = "cne"; | ||
2184 | d["cge"] = "clt"; | ||
2185 | d["cgt"] = "cle"; | ||
2186 | d["cle"] = "cgt"; | ||
2187 | d["clt"] = "cge"; | ||
2188 | d["cne"] = "ceq"; | ||
2189 | return d; | ||
2190 | } | ||
2191 | |||
2192 | public static OTOpnd Make(OTOpnd left, MyOp opCode, OTOpnd rite) | ||
2193 | { | ||
2194 | // ((x clt y) xor 1) => (x cge y) etc | ||
2195 | string xor1op; | ||
2196 | if((left is OTOpndBinOp) && xor1ops.TryGetValue(((OTOpndBinOp)left).opCode.name, out xor1op) && | ||
2197 | (opCode == MyOp.Xor) && | ||
2198 | (rite is OTOpndInt) && (((OTOpndInt)rite).value == 1)) | ||
2199 | { | ||
2200 | opCode = MyOp.GetByName(xor1op); | ||
2201 | } | ||
2202 | |||
2203 | // handle strcmp() cases (see OTOpndStrCmp) | ||
2204 | if(left is OTOpndStrCmp) | ||
2205 | { | ||
2206 | OTOpnd strcmp = ((OTOpndStrCmp)left).MakeBinOp(opCode, rite); | ||
2207 | if(strcmp != null) | ||
2208 | return strcmp; | ||
2209 | } | ||
2210 | |||
2211 | // nothing special, make as is | ||
2212 | OTOpndBinOp it = new OTOpndBinOp(); | ||
2213 | it.left = left; | ||
2214 | it.opCode = opCode; | ||
2215 | it.rite = rite; | ||
2216 | return it; | ||
2217 | } | ||
2218 | |||
2219 | private OTOpndBinOp() | ||
2220 | { | ||
2221 | } | ||
2222 | |||
2223 | public override bool HasSideEffects | ||
2224 | { | ||
2225 | get | ||
2226 | { | ||
2227 | return left.HasSideEffects || rite.HasSideEffects; | ||
2228 | } | ||
2229 | } | ||
2230 | |||
2231 | public override void CountRefs(bool writing) | ||
2232 | { | ||
2233 | left.CountRefs(false); | ||
2234 | rite.CountRefs(false); | ||
2235 | } | ||
2236 | |||
2237 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
2238 | { | ||
2239 | if(SameAs(oldopnd)) | ||
2240 | { | ||
2241 | rc = true; | ||
2242 | return newopnd; | ||
2243 | } | ||
2244 | left = left.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
2245 | rite = rite.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
2246 | return this; | ||
2247 | } | ||
2248 | |||
2249 | public override bool SameAs(OTOpnd other) | ||
2250 | { | ||
2251 | if(!(other is OTOpndBinOp)) | ||
2252 | return false; | ||
2253 | OTOpndBinOp otherbo = (OTOpndBinOp)other; | ||
2254 | return left.SameAs(otherbo.left) && (opCode.ToString() == otherbo.opCode.ToString()) && rite.SameAs(otherbo.rite); | ||
2255 | } | ||
2256 | |||
2257 | public override string PrintableString | ||
2258 | { | ||
2259 | get | ||
2260 | { | ||
2261 | StringBuilder sb = new StringBuilder(); | ||
2262 | |||
2263 | bool leftneedsparen = ItNeedsParentheses(left, true); | ||
2264 | if(leftneedsparen) | ||
2265 | sb.Append('('); | ||
2266 | sb.Append(left.PrintableString); | ||
2267 | if(leftneedsparen) | ||
2268 | sb.Append(')'); | ||
2269 | |||
2270 | sb.Append(' '); | ||
2271 | sb.Append(opCode.source); | ||
2272 | sb.Append(' '); | ||
2273 | |||
2274 | bool riteneedsparen = ItNeedsParentheses(rite, false); | ||
2275 | if(riteneedsparen) | ||
2276 | sb.Append('('); | ||
2277 | sb.Append(rite.PrintableString); | ||
2278 | if(riteneedsparen) | ||
2279 | sb.Append(')'); | ||
2280 | |||
2281 | return sb.ToString(); | ||
2282 | } | ||
2283 | } | ||
2284 | |||
2285 | /** | ||
2286 | * See if source code representation requires parentheses around the given operand. | ||
2287 | * @param it = the other operand to decide about | ||
2288 | * @param itleft = true: 'it' is on the left of this operand (A $ B) # C | ||
2289 | * false: 'it' is on the right of this operand A $ (B # C) | ||
2290 | */ | ||
2291 | private bool ItNeedsParentheses(OTOpnd it, bool itleft) | ||
2292 | { | ||
2293 | if(!(it is OTOpndBinOp)) | ||
2294 | return false; | ||
2295 | string itop = ((OTOpndBinOp)it).opCode.source; | ||
2296 | string myop = opCode.source; | ||
2297 | |||
2298 | // find them in table. higher number is for *, lower is for +. | ||
2299 | int itpi, mypi; | ||
2300 | if(!precedence.TryGetValue(itop, out itpi)) | ||
2301 | return true; | ||
2302 | if(!precedence.TryGetValue(myop, out mypi)) | ||
2303 | return true; | ||
2304 | int itpiabs = Math.Abs(itpi); | ||
2305 | int mypiabs = Math.Abs(mypi); | ||
2306 | |||
2307 | // if its precedence is lower (eg +) than my precedence (eg *), it needs parentheses | ||
2308 | if(itpiabs < mypiabs) | ||
2309 | return true; | ||
2310 | |||
2311 | // if its precedence is higher (eg *) than my precedence (eg +), it doesn't needs parentheses | ||
2312 | if(itpiabs > mypiabs) | ||
2313 | return false; | ||
2314 | |||
2315 | // if (A $ B) # C, we can safely go without the parentheses | ||
2316 | if(itleft) | ||
2317 | return false; | ||
2318 | |||
2319 | // my it | ||
2320 | // A $ (B # C) only works without parentheses for commutative $ | ||
2321 | // A - (B + C) and A - (B - C) require parentheses | ||
2322 | // A + (B - C) does not | ||
2323 | return mypi < 0; // neg: things like -, /, etc require parentheses | ||
2324 | // pos: things like +, *, etc do not need parens | ||
2325 | } | ||
2326 | |||
2327 | // see MMRScriptReduce.PrecedenceInit() | ||
2328 | private static Dictionary<string, int> precedence = InitPrecedence(); | ||
2329 | private static Dictionary<string, int> InitPrecedence() | ||
2330 | { | ||
2331 | Dictionary<string, int> d = new Dictionary<string, int>(); | ||
2332 | d["|"] = 140; | ||
2333 | d["^"] = 160; | ||
2334 | d["&"] = 180; | ||
2335 | d["<<"] = -260; | ||
2336 | d[">>"] = -260; | ||
2337 | d["+"] = 280; | ||
2338 | d["-"] = -280; | ||
2339 | d["*"] = 320; | ||
2340 | d["/"] = -320; | ||
2341 | d["%"] = -320; | ||
2342 | return d; | ||
2343 | } | ||
2344 | } | ||
2345 | |||
2346 | /** | ||
2347 | * Call with or without return value. | ||
2348 | */ | ||
2349 | private class OTOpndCall: OTOpnd | ||
2350 | { | ||
2351 | private static Dictionary<string, MethodInfo> mathmeths = InitMathMeths(); | ||
2352 | private static Dictionary<string, MethodInfo> InitMathMeths() | ||
2353 | { | ||
2354 | Dictionary<string, MethodInfo> d = new Dictionary<string, MethodInfo>(); | ||
2355 | d["Acos"] = typeof(ScriptBaseClass).GetMethod("llAcos"); | ||
2356 | d["Asin"] = typeof(ScriptBaseClass).GetMethod("llAsin"); | ||
2357 | d["Atan"] = typeof(ScriptBaseClass).GetMethod("llAtan"); | ||
2358 | d["Cos"] = typeof(ScriptBaseClass).GetMethod("llCos"); | ||
2359 | d["Abs"] = typeof(ScriptBaseClass).GetMethod("llFabs"); | ||
2360 | d["Log"] = typeof(ScriptBaseClass).GetMethod("llLog"); | ||
2361 | d["Log10"] = typeof(ScriptBaseClass).GetMethod("llLog10"); | ||
2362 | d["Round"] = typeof(ScriptBaseClass).GetMethod("llRound"); | ||
2363 | d["Sin"] = typeof(ScriptBaseClass).GetMethod("llSin"); | ||
2364 | d["Sqrt"] = typeof(ScriptBaseClass).GetMethod("llSqrt"); | ||
2365 | d["Tan"] = typeof(ScriptBaseClass).GetMethod("llTan"); | ||
2366 | return d; | ||
2367 | } | ||
2368 | |||
2369 | public MethodInfo method; | ||
2370 | public OTOpnd[] args; | ||
2371 | |||
2372 | // pushes on stack for return-value functions | ||
2373 | // pushes to end of instruction stream for return-void functions | ||
2374 | public static void AddLast(OTDecompile decompile, MethodInfo method, OTOpnd[] args) | ||
2375 | { | ||
2376 | int nargs = args.Length; | ||
2377 | |||
2378 | // heap tracker push is just the single arg value as far as we're concerned | ||
2379 | if((nargs == 1) && (method.Name == _heapTrackerPush) && method.DeclaringType.Name.StartsWith("HeapTracker")) | ||
2380 | { | ||
2381 | decompile.opstack.Push(args[0]); | ||
2382 | return; | ||
2383 | } | ||
2384 | |||
2385 | // heap tracker pop is just a store as far as we're concerned | ||
2386 | if((nargs == 2) && (method.Name == _heapTrackerPop) && method.DeclaringType.Name.StartsWith("HeapTracker")) | ||
2387 | { | ||
2388 | OTStmtStore.AddLast(decompile, args[0], args[1]); | ||
2389 | return; | ||
2390 | } | ||
2391 | |||
2392 | // string.Compare() is its own thing cuz it has to decompile many ways | ||
2393 | if((nargs == 2) && (method.DeclaringType == typeof(string)) && (method.Name == "Compare")) | ||
2394 | { | ||
2395 | decompile.opstack.Push(new OTOpndStrCmp(args[0], args[1])); | ||
2396 | return; | ||
2397 | } | ||
2398 | |||
2399 | // ObjectToString, etc, should appear as casts | ||
2400 | if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToBool")) | ||
2401 | { | ||
2402 | MethodInfo meth = typeof(XMRInstAbstract).GetMethod("xmr" + method.Name); | ||
2403 | AddLast(decompile, meth, new OTOpnd[] { new OTOpndNull(), args[0] }); | ||
2404 | return; | ||
2405 | } | ||
2406 | if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToFloat")) | ||
2407 | { | ||
2408 | decompile.opstack.Push(new OTOpndCast(typeof(double), args[0])); | ||
2409 | return; | ||
2410 | } | ||
2411 | if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToInteger")) | ||
2412 | { | ||
2413 | decompile.opstack.Push(new OTOpndCast(typeof(int), args[0])); | ||
2414 | return; | ||
2415 | } | ||
2416 | if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToList")) | ||
2417 | { | ||
2418 | decompile.opstack.Push(new OTOpndCast(typeof(LSL_List), args[0])); | ||
2419 | return; | ||
2420 | } | ||
2421 | if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToRotation")) | ||
2422 | { | ||
2423 | decompile.opstack.Push(new OTOpndCast(typeof(LSL_Rotation), args[0])); | ||
2424 | return; | ||
2425 | } | ||
2426 | if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToString")) | ||
2427 | { | ||
2428 | decompile.opstack.Push(new OTOpndCast(typeof(string), args[0])); | ||
2429 | return; | ||
2430 | } | ||
2431 | if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.EndsWith("ToVector")) | ||
2432 | { | ||
2433 | decompile.opstack.Push(new OTOpndCast(typeof(LSL_Vector), args[0])); | ||
2434 | return; | ||
2435 | } | ||
2436 | |||
2437 | if((method.DeclaringType == typeof(XMRInstAbstract)) && (method.Name == "xmrHeapLeft")) | ||
2438 | { | ||
2439 | AddLast(decompile, typeof(ScriptBaseClass).GetMethod("llGetFreeMemory"), new OTOpnd[] { new OTOpndNull() }); | ||
2440 | return; | ||
2441 | } | ||
2442 | |||
2443 | // pop to entry in the list/object/string array | ||
2444 | if(PopToGlobalArray(decompile, method, args)) | ||
2445 | return; | ||
2446 | |||
2447 | // strip off event handler argument unwrapper calls | ||
2448 | if((nargs == 1) && (method.DeclaringType == typeof(TypeCast)) && method.Name.StartsWith("EHArgUnwrap")) | ||
2449 | { | ||
2450 | decompile.opstack.Push(args[0]); | ||
2451 | return; | ||
2452 | } | ||
2453 | |||
2454 | // translate Math method to ll method | ||
2455 | MethodInfo mathmeth; | ||
2456 | if((method.DeclaringType == typeof(Math)) && mathmeths.TryGetValue(method.Name, out mathmeth)) | ||
2457 | { | ||
2458 | AddLast(decompile, mathmeth, new OTOpnd[] { new OTOpndNull(), args[0] }); | ||
2459 | return; | ||
2460 | } | ||
2461 | if((method.DeclaringType == typeof(Math)) && (method.Name == "Atan2")) | ||
2462 | { | ||
2463 | AddLast(decompile, typeof(ScriptBaseClass).GetMethod("llAtan2"), new OTOpnd[] { new OTOpndNull(), args[0], args[1] }); | ||
2464 | return; | ||
2465 | } | ||
2466 | if((method.DeclaringType == typeof(Math)) && (method.Name == "Pow")) | ||
2467 | { | ||
2468 | AddLast(decompile, typeof(ScriptBaseClass).GetMethod("llPow"), new OTOpnd[] { new OTOpndNull(), args[0], args[1] }); | ||
2469 | return; | ||
2470 | } | ||
2471 | |||
2472 | // string concat should be a bunch of adds | ||
2473 | if((method.Name == "Concat") && (method.DeclaringType == typeof(string))) | ||
2474 | { | ||
2475 | int k = args.Length; | ||
2476 | while(k > 1) | ||
2477 | { | ||
2478 | int j = 0; | ||
2479 | int i; | ||
2480 | for(i = 0; i + 2 <= k; i += 2) | ||
2481 | { | ||
2482 | args[j++] = OTOpndBinOp.Make(args[i + 0], MyOp.Add, args[i + 1]); | ||
2483 | } | ||
2484 | while(i < k) | ||
2485 | args[j++] = args[i++]; | ||
2486 | k = j; | ||
2487 | } | ||
2488 | if(k > 0) | ||
2489 | decompile.opstack.Push(args[0]); | ||
2490 | return; | ||
2491 | } | ||
2492 | |||
2493 | // bunch of calls for rotation and vector arithmetic | ||
2494 | if((method.DeclaringType == typeof(BinOpStr)) && BinOpStrCall(decompile, method, args)) | ||
2495 | return; | ||
2496 | if((method.DeclaringType == typeof(ScriptCodeGen)) && (method.Name == "LSLRotationNegate")) | ||
2497 | { | ||
2498 | decompile.opstack.Push(OTOpndUnOp.Make(MyOp.Neg, args[0])); | ||
2499 | return; | ||
2500 | } | ||
2501 | if((method.DeclaringType == typeof(ScriptCodeGen)) && (method.Name == "LSLVectorNegate")) | ||
2502 | { | ||
2503 | decompile.opstack.Push(OTOpndUnOp.Make(MyOp.Neg, args[0])); | ||
2504 | return; | ||
2505 | } | ||
2506 | |||
2507 | // otherwise process it as a call | ||
2508 | OTOpndCall call = new OTOpndCall(); | ||
2509 | call.method = method; | ||
2510 | call.args = args; | ||
2511 | if(method.ReturnType == typeof(void)) | ||
2512 | { | ||
2513 | OTStmtVoid.AddLast(decompile, call); | ||
2514 | } | ||
2515 | else | ||
2516 | { | ||
2517 | decompile.opstack.Push(call); | ||
2518 | } | ||
2519 | } | ||
2520 | |||
2521 | public override bool HasSideEffects | ||
2522 | { | ||
2523 | get | ||
2524 | { | ||
2525 | return true; | ||
2526 | } | ||
2527 | } | ||
2528 | |||
2529 | /** | ||
2530 | * Handle a call to XMRInstArrays.Pop<List,Object,String> | ||
2531 | * by converting it to a store directly into the array. | ||
2532 | */ | ||
2533 | private static bool PopToGlobalArray(OTDecompile decompile, MethodInfo method, OTOpnd[] args) | ||
2534 | { | ||
2535 | if(method.DeclaringType != typeof(XMRInstArrays)) | ||
2536 | return false; | ||
2537 | if(args.Length != 3) | ||
2538 | return false; | ||
2539 | |||
2540 | string array = null; | ||
2541 | if(method.Name == "PopList") | ||
2542 | array = "iarLists"; | ||
2543 | if(method.Name == "PopObject") | ||
2544 | array = "iarObjects"; | ||
2545 | if(method.Name == "PopString") | ||
2546 | array = "iarStrings"; | ||
2547 | if(array == null) | ||
2548 | return false; | ||
2549 | |||
2550 | // make token that points to the iar<whatever> array | ||
2551 | FieldInfo field = typeof(XMRInstArrays).GetField(array); | ||
2552 | OTOpnd arrayfield = OTOpndField.Make(args[0], field); | ||
2553 | |||
2554 | // make token that points to the element to be popped to | ||
2555 | OTOpnd element = OTOpndArrayElem.Make(arrayfield, args[1], false, decompile); | ||
2556 | |||
2557 | // make a statement to store value in that element | ||
2558 | OTStmtStore.AddLast(decompile, element, args[2]); | ||
2559 | |||
2560 | return true; | ||
2561 | } | ||
2562 | |||
2563 | /** | ||
2564 | * BinOpStr has a bunch of calls to do funky arithmetic. | ||
2565 | * Instead of generating a call, put back the original source. | ||
2566 | */ | ||
2567 | private static bool BinOpStrCall(OTDecompile decompile, MethodInfo method, OTOpnd[] args) | ||
2568 | { | ||
2569 | switch(method.Name) | ||
2570 | { | ||
2571 | case "MethFloatAddList": | ||
2572 | case "MethIntAddList": | ||
2573 | case "MethKeyAddList": | ||
2574 | case "MethListAddFloat": | ||
2575 | case "MethListAddInt": | ||
2576 | case "MethListAddKey": | ||
2577 | case "MethListAddList": | ||
2578 | case "MethListAddObj": | ||
2579 | case "MethListAddRot": | ||
2580 | case "MethListAddStr": | ||
2581 | case "MethListAddVec": | ||
2582 | case "MethObjAddList": | ||
2583 | case "MethRotAddList": | ||
2584 | case "MethRotAddRot": | ||
2585 | case "MethStrAddList": | ||
2586 | case "MethVecAddList": | ||
2587 | case "MethVecAddVec": | ||
2588 | { | ||
2589 | decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Add, args[1])); | ||
2590 | return true; | ||
2591 | } | ||
2592 | |||
2593 | case "MethListEqList": | ||
2594 | case "MethRotEqRot": | ||
2595 | case "MethVecEqVec": | ||
2596 | { | ||
2597 | decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Ceq, args[1])); | ||
2598 | return true; | ||
2599 | } | ||
2600 | |||
2601 | case "MethListNeList": | ||
2602 | case "MethRotNeRot": | ||
2603 | case "MethVecNeVec": | ||
2604 | { | ||
2605 | decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Cne, args[1])); | ||
2606 | return true; | ||
2607 | } | ||
2608 | |||
2609 | case "MethRotSubRot": | ||
2610 | case "MethVecSubVec": | ||
2611 | { | ||
2612 | decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Sub, args[1])); | ||
2613 | return true; | ||
2614 | } | ||
2615 | |||
2616 | case "MethFloatMulVec": | ||
2617 | case "MethIntMulVec": | ||
2618 | case "MethRotMulRot": | ||
2619 | case "MethVecMulFloat": | ||
2620 | case "MethVecMulInt": | ||
2621 | case "MethVecMulRot": | ||
2622 | case "MethVecMulVec": | ||
2623 | { | ||
2624 | decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Mul, args[1])); | ||
2625 | return true; | ||
2626 | } | ||
2627 | |||
2628 | case "MethRotDivRot": | ||
2629 | case "MethVecDivFloat": | ||
2630 | case "MethVecDivInt": | ||
2631 | case "MethVecDivRot": | ||
2632 | { | ||
2633 | decompile.opstack.Push(OTOpndBinOp.Make(args[0], MyOp.Div, args[1])); | ||
2634 | return true; | ||
2635 | } | ||
2636 | |||
2637 | default: | ||
2638 | return false; | ||
2639 | } | ||
2640 | } | ||
2641 | |||
2642 | private OTOpndCall() | ||
2643 | { | ||
2644 | } | ||
2645 | |||
2646 | public override void CountRefs(bool writing) | ||
2647 | { | ||
2648 | foreach(OTOpnd arg in args) | ||
2649 | { | ||
2650 | arg.CountRefs(false); | ||
2651 | } | ||
2652 | } | ||
2653 | |||
2654 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
2655 | { | ||
2656 | for(int i = 0; i < args.Length; i++) | ||
2657 | { | ||
2658 | args[i] = args[i].ReplaceOperand(oldopnd, newopnd, ref rc); | ||
2659 | } | ||
2660 | return this; | ||
2661 | } | ||
2662 | |||
2663 | public override bool SameAs(OTOpnd other) | ||
2664 | { | ||
2665 | return false; | ||
2666 | } | ||
2667 | |||
2668 | public override string PrintableString | ||
2669 | { | ||
2670 | get | ||
2671 | { | ||
2672 | StringBuilder sb = new StringBuilder(); | ||
2673 | |||
2674 | // GetByKey(a,i) => a[i] | ||
2675 | if((method.DeclaringType == typeof(XMR_Array)) && (method.Name == "GetByKey") && (args.Length == 2)) | ||
2676 | { | ||
2677 | sb.Append(args[0].PrintableString); | ||
2678 | sb.Append('['); | ||
2679 | sb.Append(args[1].PrintableString); | ||
2680 | sb.Append(']'); | ||
2681 | return sb.ToString(); | ||
2682 | } | ||
2683 | |||
2684 | // SetByKey(a,i,v) => a[i] = v | ||
2685 | if((method.DeclaringType == typeof(XMR_Array)) && (method.Name == "SetByKey") && (args.Length == 3)) | ||
2686 | { | ||
2687 | sb.Append(args[0].PrintableString); | ||
2688 | sb.Append('['); | ||
2689 | sb.Append(args[1].PrintableString); | ||
2690 | sb.Append("] = "); | ||
2691 | sb.Append(args[2].PrintableString); | ||
2692 | return sb.ToString(); | ||
2693 | } | ||
2694 | |||
2695 | // CompValuListEl.GetElementFromList accesses list elements like an array. | ||
2696 | if((method.DeclaringType == typeof(CompValuListEl)) && (method.Name == "GetElementFromList")) | ||
2697 | { | ||
2698 | sb.Append(args[0].PrintableString); | ||
2699 | sb.Append('['); | ||
2700 | sb.Append(args[1].PrintableString); | ||
2701 | sb.Append(']'); | ||
2702 | return sb.ToString(); | ||
2703 | } | ||
2704 | |||
2705 | // methods that are part of ScriptBaseClass are LSL functions such as llSay() | ||
2706 | // so we want to skip outputting "arg$0," as it is the hidden "this" argument. | ||
2707 | // and there are also XMRInstAbstract functions such as xmrEventDequeue(). | ||
2708 | int starti = 0; | ||
2709 | if((method.DeclaringType == typeof(ScriptBaseClass)) && !method.IsStatic) | ||
2710 | starti = 1; | ||
2711 | if((method.DeclaringType == typeof(XMRInstAbstract)) && !method.IsStatic) | ||
2712 | starti = 1; | ||
2713 | |||
2714 | // likewise, method that have null as the declaring type are script-defined | ||
2715 | // dynamic methods which have a hidden "this" argument passed as "arg$0". | ||
2716 | if(method.DeclaringType == null) | ||
2717 | starti = 1; | ||
2718 | |||
2719 | // all others we want to show the type name (such as Math.Abs, String.Compare, etc) | ||
2720 | if(starti == 0) | ||
2721 | { | ||
2722 | sb.Append(AbbrType(method.DeclaringType)); | ||
2723 | sb.Append('.'); | ||
2724 | } | ||
2725 | |||
2726 | // script-defined functions have the param types as part of their name | ||
2727 | // so strip them off here so they don't clutter things up | ||
2728 | int i = method.Name.IndexOf('('); | ||
2729 | if(i < 0) | ||
2730 | sb.Append(method.Name); | ||
2731 | else | ||
2732 | sb.Append(method.Name.Substring(0, i)); | ||
2733 | |||
2734 | // now add the call arguments | ||
2735 | sb.Append(" ("); | ||
2736 | bool first = true; | ||
2737 | foreach(OTOpnd arg in args) | ||
2738 | { | ||
2739 | if(--starti < 0) | ||
2740 | { | ||
2741 | if(!first) | ||
2742 | sb.Append(", "); | ||
2743 | sb.Append(arg.PrintableString); | ||
2744 | first = false; | ||
2745 | } | ||
2746 | } | ||
2747 | sb.Append(')'); | ||
2748 | return sb.ToString(); | ||
2749 | } | ||
2750 | } | ||
2751 | } | ||
2752 | |||
2753 | /** | ||
2754 | * Cast value to the given type. | ||
2755 | */ | ||
2756 | private class OTOpndCast: OTOpnd | ||
2757 | { | ||
2758 | public Type type; | ||
2759 | public OTOpnd value; | ||
2760 | |||
2761 | public OTOpndCast(Type type, OTOpnd value) | ||
2762 | { | ||
2763 | this.type = type; | ||
2764 | this.value = value; | ||
2765 | } | ||
2766 | |||
2767 | public override bool HasSideEffects | ||
2768 | { | ||
2769 | get | ||
2770 | { | ||
2771 | return value.HasSideEffects; | ||
2772 | } | ||
2773 | } | ||
2774 | |||
2775 | public override void CountRefs(bool writing) | ||
2776 | { | ||
2777 | value.CountRefs(false); | ||
2778 | } | ||
2779 | |||
2780 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
2781 | { | ||
2782 | if(SameAs(oldopnd)) | ||
2783 | { | ||
2784 | rc = true; | ||
2785 | return newopnd; | ||
2786 | } | ||
2787 | value = value.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
2788 | return this; | ||
2789 | } | ||
2790 | |||
2791 | public override bool SameAs(OTOpnd other) | ||
2792 | { | ||
2793 | if(!(other is OTOpndCast)) | ||
2794 | return false; | ||
2795 | OTOpndCast othercast = (OTOpndCast)other; | ||
2796 | return (type == othercast.type) && value.SameAs(othercast.value); | ||
2797 | } | ||
2798 | |||
2799 | public override string PrintableString | ||
2800 | { | ||
2801 | get | ||
2802 | { | ||
2803 | StringBuilder sb = new StringBuilder(); | ||
2804 | sb.Append('('); | ||
2805 | sb.Append(AbbrType(type)); | ||
2806 | sb.Append(") "); | ||
2807 | if(value is OTOpndBinOp) | ||
2808 | sb.Append('('); | ||
2809 | sb.Append(value.PrintableString); | ||
2810 | if(value is OTOpndBinOp) | ||
2811 | sb.Append(')'); | ||
2812 | return sb.ToString(); | ||
2813 | } | ||
2814 | } | ||
2815 | } | ||
2816 | |||
2817 | /** | ||
2818 | * Duplicate stack value without re-performing computation. | ||
2819 | * Semantics just like local var except it doesn't have a declaration. | ||
2820 | */ | ||
2821 | private class OTOpndDup: OTOpnd | ||
2822 | { | ||
2823 | public int index; | ||
2824 | public int ndupreads; | ||
2825 | |||
2826 | public OTOpndDup(int index) | ||
2827 | { | ||
2828 | this.index = index; | ||
2829 | } | ||
2830 | |||
2831 | public override bool HasSideEffects | ||
2832 | { | ||
2833 | get | ||
2834 | { | ||
2835 | return false; | ||
2836 | } | ||
2837 | } | ||
2838 | |||
2839 | public override void CountRefs(bool writing) | ||
2840 | { | ||
2841 | if(!writing) | ||
2842 | ndupreads++; | ||
2843 | } | ||
2844 | |||
2845 | public override bool SameAs(OTOpnd other) | ||
2846 | { | ||
2847 | if(!(other is OTOpndDup)) | ||
2848 | return false; | ||
2849 | return ((OTOpndDup)other).index == index; | ||
2850 | } | ||
2851 | |||
2852 | public override string PrintableString | ||
2853 | { | ||
2854 | get | ||
2855 | { | ||
2856 | return "dup$" + index; | ||
2857 | } | ||
2858 | } | ||
2859 | } | ||
2860 | |||
2861 | /** | ||
2862 | * Field of an object. | ||
2863 | */ | ||
2864 | private class OTOpndField: OTOpnd | ||
2865 | { | ||
2866 | public OTOpnd obj; | ||
2867 | public FieldInfo field; | ||
2868 | |||
2869 | public static OTOpnd Make(OTOpnd obj, FieldInfo field) | ||
2870 | { | ||
2871 | // LSL_Float.value => the object itself | ||
2872 | if((field.DeclaringType == typeof(LSL_Float)) && (field.Name == "value")) | ||
2873 | { | ||
2874 | return obj; | ||
2875 | } | ||
2876 | |||
2877 | // LSL_Integer.value => the object itself | ||
2878 | if((field.DeclaringType == typeof(LSL_Integer)) && (field.Name == "value")) | ||
2879 | { | ||
2880 | return obj; | ||
2881 | } | ||
2882 | |||
2883 | // LSL_String.m_string => the object itself | ||
2884 | if((field.DeclaringType == typeof(LSL_String)) && (field.Name == "m_string")) | ||
2885 | { | ||
2886 | return obj; | ||
2887 | } | ||
2888 | |||
2889 | // some other field, output code to access it | ||
2890 | // sometimes the object comes as by reference (value types), so we might need to deref it first | ||
2891 | OTOpndField it = new OTOpndField(); | ||
2892 | it.obj = obj.GetNonByRefOpnd(); | ||
2893 | it.field = field; | ||
2894 | return it; | ||
2895 | } | ||
2896 | |||
2897 | private OTOpndField() | ||
2898 | { | ||
2899 | } | ||
2900 | |||
2901 | public override bool HasSideEffects | ||
2902 | { | ||
2903 | get | ||
2904 | { | ||
2905 | return obj.HasSideEffects; | ||
2906 | } | ||
2907 | } | ||
2908 | |||
2909 | public override void CountRefs(bool writing) | ||
2910 | { | ||
2911 | // the field may be getting written to, but the object is being read | ||
2912 | obj.CountRefs(false); | ||
2913 | } | ||
2914 | |||
2915 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
2916 | { | ||
2917 | if(SameAs(oldopnd)) | ||
2918 | { | ||
2919 | rc = true; | ||
2920 | return newopnd; | ||
2921 | } | ||
2922 | obj = obj.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
2923 | return this; | ||
2924 | } | ||
2925 | |||
2926 | public override bool SameAs(OTOpnd other) | ||
2927 | { | ||
2928 | if(!(other is OTOpndField)) | ||
2929 | return false; | ||
2930 | OTOpndField otherfield = (OTOpndField)other; | ||
2931 | return (field.Name == otherfield.field.Name) && obj.SameAs(otherfield.obj); | ||
2932 | } | ||
2933 | |||
2934 | public override string PrintableString | ||
2935 | { | ||
2936 | get | ||
2937 | { | ||
2938 | StringBuilder sb = new StringBuilder(); | ||
2939 | if(obj is OTOpndBinOp) | ||
2940 | sb.Append('('); | ||
2941 | sb.Append(obj.PrintableString); | ||
2942 | if(obj is OTOpndBinOp) | ||
2943 | sb.Append(')'); | ||
2944 | sb.Append('.'); | ||
2945 | sb.Append(field.Name); | ||
2946 | return sb.ToString(); | ||
2947 | } | ||
2948 | } | ||
2949 | } | ||
2950 | |||
2951 | /** | ||
2952 | * Script-level global variable. | ||
2953 | */ | ||
2954 | private class OTOpndGlobal: OTOpnd | ||
2955 | { | ||
2956 | public string iartypename; | ||
2957 | public int iararrayidx; | ||
2958 | public bool byref; | ||
2959 | public ScriptObjCode scriptObjCode; | ||
2960 | |||
2961 | public OTOpndGlobal(string iartypename, int iararrayidx, bool byref, ScriptObjCode scriptObjCode) | ||
2962 | { | ||
2963 | this.iartypename = iartypename; | ||
2964 | this.iararrayidx = iararrayidx; | ||
2965 | this.byref = byref; | ||
2966 | this.scriptObjCode = scriptObjCode; | ||
2967 | } | ||
2968 | |||
2969 | public override bool HasSideEffects | ||
2970 | { | ||
2971 | get | ||
2972 | { | ||
2973 | return false; | ||
2974 | } | ||
2975 | } | ||
2976 | |||
2977 | public override OTOpnd GetNonByRefOpnd() | ||
2978 | { | ||
2979 | if(!byref) | ||
2980 | return this; | ||
2981 | return new OTOpndGlobal(iartypename, iararrayidx, false, scriptObjCode); | ||
2982 | } | ||
2983 | |||
2984 | public override bool SameAs(OTOpnd other) | ||
2985 | { | ||
2986 | if(!(other is OTOpndGlobal)) | ||
2987 | return false; | ||
2988 | OTOpndGlobal otherglobal = (OTOpndGlobal)other; | ||
2989 | return (iartypename == otherglobal.iartypename) && (iararrayidx == otherglobal.iararrayidx); | ||
2990 | } | ||
2991 | |||
2992 | public override string PrintableString | ||
2993 | { | ||
2994 | get | ||
2995 | { | ||
2996 | return (byref ? "ref " : "") + scriptObjCode.globalVarNames[iartypename][iararrayidx]; | ||
2997 | } | ||
2998 | } | ||
2999 | } | ||
3000 | |||
3001 | /** | ||
3002 | * List initialization. | ||
3003 | */ | ||
3004 | private class OTOpndListIni: OTOpnd | ||
3005 | { | ||
3006 | public OTOpnd[] values; | ||
3007 | |||
3008 | /** | ||
3009 | * Try to detect list initialization building idiom: | ||
3010 | * dup$<n> = newarr object[<m>] << link points here | ||
3011 | * dup$<n>[0] = bla | ||
3012 | * dup$<n>[1] = bla | ||
3013 | * ... | ||
3014 | * ... newobj list (dup$<n>) ... | ||
3015 | */ | ||
3016 | public static bool Detect(LinkedListNode<OTStmt> link) | ||
3017 | { | ||
3018 | if(link == null) | ||
3019 | return false; | ||
3020 | |||
3021 | /* | ||
3022 | * Check for 'dup$<n> = newarr object[<m>]' and get listsize from <m>. | ||
3023 | */ | ||
3024 | OTStmtStore store = (OTStmtStore)link.Value; | ||
3025 | if(!(store.varwr is OTOpndDup)) | ||
3026 | return false; | ||
3027 | if(!(store.value is OTOpndNewarr)) | ||
3028 | return false; | ||
3029 | OTOpndDup storevar = (OTOpndDup)store.varwr; | ||
3030 | OTOpndNewarr storeval = (OTOpndNewarr)store.value; | ||
3031 | if(storeval.type != typeof(object)) | ||
3032 | return false; | ||
3033 | if(!(storeval.index is OTOpndInt)) | ||
3034 | return false; | ||
3035 | int listsize = ((OTOpndInt)storeval.index).value; | ||
3036 | |||
3037 | // Good chance of having list initializer, malloc an object to hold it. | ||
3038 | OTOpndListIni it = new OTOpndListIni(); | ||
3039 | it.values = new OTOpnd[listsize]; | ||
3040 | |||
3041 | // There should be exactly listsize statements following that of the form: | ||
3042 | // dup$<n>[<i>] = bla | ||
3043 | // If so, save the bla values in the values[] array. | ||
3044 | LinkedListNode<OTStmt> vallink = link; | ||
3045 | for(int i = 0; i < listsize; i++) | ||
3046 | { | ||
3047 | vallink = vallink.Next; | ||
3048 | if(vallink == null) | ||
3049 | return false; | ||
3050 | if(!(vallink.Value is OTStmtStore)) | ||
3051 | return false; | ||
3052 | OTStmtStore valstore = (OTStmtStore)vallink.Value; | ||
3053 | if(!(valstore.varwr is OTOpndArrayElem)) | ||
3054 | return false; | ||
3055 | OTOpndArrayElem varelem = (OTOpndArrayElem)valstore.varwr; | ||
3056 | if(varelem.array != storevar) | ||
3057 | return false; | ||
3058 | if(!(varelem.index is OTOpndInt)) | ||
3059 | return false; | ||
3060 | if(((OTOpndInt)varelem.index).value != i) | ||
3061 | return false; | ||
3062 | it.values[i] = valstore.value; | ||
3063 | } | ||
3064 | |||
3065 | // The next statement should have a 'newobj list (dup$<n>)' in it somewhere | ||
3066 | // that we want to replace with 'it'. | ||
3067 | ConstructorInfo protoctor = typeof(LSL_List).GetConstructor(new Type[] { typeof(object[]) }); | ||
3068 | OTOpnd[] protoargs = new OTOpnd[] { storevar }; | ||
3069 | OTOpnd proto = OTOpndNewobj.Make(protoctor, protoargs); | ||
3070 | |||
3071 | vallink = vallink.Next; | ||
3072 | bool rc = vallink.Value.ReplaceOperand(proto, it); | ||
3073 | |||
3074 | // If successful, delete 'dup$n =' and all 'dup$n[i] =' statements. | ||
3075 | if(rc) | ||
3076 | { | ||
3077 | do | ||
3078 | { | ||
3079 | LinkedListNode<OTStmt> nextlink = link.Next; | ||
3080 | link.List.Remove(link); | ||
3081 | link = nextlink; | ||
3082 | } while(link != vallink); | ||
3083 | } | ||
3084 | |||
3085 | return rc; | ||
3086 | } | ||
3087 | |||
3088 | public override bool HasSideEffects | ||
3089 | { | ||
3090 | get | ||
3091 | { | ||
3092 | foreach(OTOpnd value in values) | ||
3093 | { | ||
3094 | if(value.HasSideEffects) | ||
3095 | return true; | ||
3096 | } | ||
3097 | return false; | ||
3098 | } | ||
3099 | } | ||
3100 | |||
3101 | public override void CountRefs(bool writing) | ||
3102 | { | ||
3103 | foreach(OTOpnd value in values) | ||
3104 | { | ||
3105 | value.CountRefs(false); | ||
3106 | } | ||
3107 | } | ||
3108 | |||
3109 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
3110 | { | ||
3111 | if(SameAs(oldopnd)) | ||
3112 | { | ||
3113 | rc = true; | ||
3114 | return newopnd; | ||
3115 | } | ||
3116 | for(int i = 0; i < values.Length; i++) | ||
3117 | { | ||
3118 | values[i] = values[i].ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3119 | } | ||
3120 | return this; | ||
3121 | } | ||
3122 | |||
3123 | public override bool SameAs(OTOpnd other) | ||
3124 | { | ||
3125 | if(!(other is OTOpndListIni)) | ||
3126 | return false; | ||
3127 | OTOpndListIni otherli = (OTOpndListIni)other; | ||
3128 | if(otherli.values.Length != values.Length) | ||
3129 | return false; | ||
3130 | for(int i = 0; i < values.Length; i++) | ||
3131 | { | ||
3132 | if(!values[i].SameAs(otherli.values[i])) | ||
3133 | return false; | ||
3134 | } | ||
3135 | return true; | ||
3136 | } | ||
3137 | |||
3138 | public override string PrintableString | ||
3139 | { | ||
3140 | get | ||
3141 | { | ||
3142 | StringBuilder sb = new StringBuilder(); | ||
3143 | sb.Append('['); | ||
3144 | for(int i = 0; i < values.Length; i++) | ||
3145 | { | ||
3146 | if(i > 0) | ||
3147 | sb.Append(','); | ||
3148 | sb.Append(' '); | ||
3149 | sb.Append(values[i].PrintableString); | ||
3150 | } | ||
3151 | sb.Append(" ]"); | ||
3152 | return sb.ToString(); | ||
3153 | } | ||
3154 | } | ||
3155 | } | ||
3156 | |||
3157 | /** | ||
3158 | * Local variable. | ||
3159 | */ | ||
3160 | private class OTOpndLocal: OTOpnd | ||
3161 | { | ||
3162 | public OTLocal local; | ||
3163 | |||
3164 | public OTOpndLocal(OTLocal local) | ||
3165 | { | ||
3166 | this.local = local; | ||
3167 | } | ||
3168 | |||
3169 | public override bool HasSideEffects | ||
3170 | { | ||
3171 | get | ||
3172 | { | ||
3173 | return false; | ||
3174 | } | ||
3175 | } | ||
3176 | |||
3177 | public override void CountRefs(bool writing) | ||
3178 | { | ||
3179 | if(writing) | ||
3180 | local.nlclwrites++; | ||
3181 | else | ||
3182 | local.nlclreads++; | ||
3183 | } | ||
3184 | |||
3185 | public override bool SameAs(OTOpnd other) | ||
3186 | { | ||
3187 | if(!(other is OTOpndLocal)) | ||
3188 | return false; | ||
3189 | OTOpndLocal otherlocal = (OTOpndLocal)other; | ||
3190 | return local == otherlocal.local; | ||
3191 | } | ||
3192 | |||
3193 | public override string PrintableString | ||
3194 | { | ||
3195 | get | ||
3196 | { | ||
3197 | return local.name; | ||
3198 | } | ||
3199 | } | ||
3200 | } | ||
3201 | private class OTOpndLocalRef: OTOpnd | ||
3202 | { | ||
3203 | public OTLocal local; | ||
3204 | |||
3205 | public OTOpndLocalRef(OTLocal local) | ||
3206 | { | ||
3207 | this.local = local; | ||
3208 | } | ||
3209 | |||
3210 | public override bool HasSideEffects | ||
3211 | { | ||
3212 | get | ||
3213 | { | ||
3214 | return true; | ||
3215 | } | ||
3216 | } | ||
3217 | |||
3218 | public override void CountRefs(bool writing) | ||
3219 | { | ||
3220 | local.nlclreads++; | ||
3221 | local.nlclwrites++; | ||
3222 | } | ||
3223 | |||
3224 | public override OTOpnd GetNonByRefOpnd() | ||
3225 | { | ||
3226 | return new OTOpndLocal(local); | ||
3227 | } | ||
3228 | |||
3229 | public override bool SameAs(OTOpnd other) | ||
3230 | { | ||
3231 | if(!(other is OTOpndLocal)) | ||
3232 | return false; | ||
3233 | OTOpndLocal otherlocal = (OTOpndLocal)other; | ||
3234 | return local == otherlocal.local; | ||
3235 | } | ||
3236 | |||
3237 | public override string PrintableString | ||
3238 | { | ||
3239 | get | ||
3240 | { | ||
3241 | return "ref " + local.name; | ||
3242 | } | ||
3243 | } | ||
3244 | } | ||
3245 | |||
3246 | /** | ||
3247 | * New C#-level array. | ||
3248 | */ | ||
3249 | private class OTOpndNewarr: OTOpnd | ||
3250 | { | ||
3251 | public Type type; | ||
3252 | public OTOpnd index; | ||
3253 | |||
3254 | public OTOpndNewarr(Type type, OTOpnd index) | ||
3255 | { | ||
3256 | this.type = type; | ||
3257 | this.index = index; | ||
3258 | } | ||
3259 | |||
3260 | public override bool HasSideEffects | ||
3261 | { | ||
3262 | get | ||
3263 | { | ||
3264 | return index.HasSideEffects; | ||
3265 | } | ||
3266 | } | ||
3267 | |||
3268 | public override void CountRefs(bool writing) | ||
3269 | { | ||
3270 | index.CountRefs(false); | ||
3271 | } | ||
3272 | |||
3273 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
3274 | { | ||
3275 | if(SameAs(oldopnd)) | ||
3276 | { | ||
3277 | rc = true; | ||
3278 | return newopnd; | ||
3279 | } | ||
3280 | index = index.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3281 | return this; | ||
3282 | } | ||
3283 | |||
3284 | public override bool SameAs(OTOpnd other) | ||
3285 | { | ||
3286 | return false; | ||
3287 | } | ||
3288 | |||
3289 | public override string PrintableString | ||
3290 | { | ||
3291 | get | ||
3292 | { | ||
3293 | return "newarr " + type.Name + "[" + index.PrintableString + "]"; | ||
3294 | } | ||
3295 | } | ||
3296 | } | ||
3297 | |||
3298 | /** | ||
3299 | * New C#-level object. | ||
3300 | */ | ||
3301 | private class OTOpndNewobj: OTOpnd | ||
3302 | { | ||
3303 | public ConstructorInfo ctor; | ||
3304 | public OTOpnd[] args; | ||
3305 | |||
3306 | public static OTOpnd Make(ConstructorInfo ctor, OTOpnd[] args) | ||
3307 | { | ||
3308 | // newobj LSL_Float (x) => x | ||
3309 | if((ctor.DeclaringType == typeof(LSL_Float)) && (args.Length == 1)) | ||
3310 | { | ||
3311 | Type ptype = ctor.GetParameters()[0].ParameterType; | ||
3312 | if(ptype == typeof(string)) | ||
3313 | { | ||
3314 | return new OTOpndCast(typeof(double), args[0]); | ||
3315 | } | ||
3316 | return args[0]; | ||
3317 | } | ||
3318 | |||
3319 | // newobj LSL_Integer (x) => x | ||
3320 | if((ctor.DeclaringType == typeof(LSL_Integer)) && (args.Length == 1)) | ||
3321 | { | ||
3322 | Type ptype = ctor.GetParameters()[0].ParameterType; | ||
3323 | if(ptype == typeof(string)) | ||
3324 | { | ||
3325 | return new OTOpndCast(typeof(int), args[0]); | ||
3326 | } | ||
3327 | return args[0]; | ||
3328 | } | ||
3329 | |||
3330 | // newobj LSL_String (x) => x | ||
3331 | if((ctor.DeclaringType == typeof(LSL_String)) && (args.Length == 1)) | ||
3332 | { | ||
3333 | return args[0]; | ||
3334 | } | ||
3335 | |||
3336 | // newobj LSL_Rotation (x, y, z, w) => <x, y, z, w> | ||
3337 | if((ctor.DeclaringType == typeof(LSL_Rotation)) && (args.Length == 4)) | ||
3338 | { | ||
3339 | return new OTOpndRot(args[0], args[1], args[2], args[3]); | ||
3340 | } | ||
3341 | |||
3342 | // newobj LSL_Vector (x, y, z) => <x, y, z> | ||
3343 | if((ctor.DeclaringType == typeof(LSL_Vector)) && (args.Length == 3)) | ||
3344 | { | ||
3345 | return new OTOpndVec(args[0], args[1], args[2]); | ||
3346 | } | ||
3347 | |||
3348 | // newobj LSL_Rotation (string) => (rotation) string | ||
3349 | if((ctor.DeclaringType == typeof(LSL_Rotation)) && (args.Length == 1)) | ||
3350 | { | ||
3351 | return new OTOpndCast(typeof(LSL_Rotation), args[0]); | ||
3352 | } | ||
3353 | |||
3354 | // newobj LSL_Vector (string) => (rotation) string | ||
3355 | if((ctor.DeclaringType == typeof(LSL_Vector)) && (args.Length == 1)) | ||
3356 | { | ||
3357 | return new OTOpndCast(typeof(LSL_Vector), args[0]); | ||
3358 | } | ||
3359 | |||
3360 | // newobj LSL_List (newarr object[0]) => [ ] | ||
3361 | if((ctor.DeclaringType == typeof(LSL_List)) && (args.Length == 1) && (args[0] is OTOpndNewarr)) | ||
3362 | { | ||
3363 | OTOpndNewarr arg0 = (OTOpndNewarr)args[0]; | ||
3364 | if((arg0.type == typeof(object)) && (arg0.index is OTOpndInt) && (((OTOpndInt)arg0.index).value == 0)) | ||
3365 | { | ||
3366 | OTOpndListIni listini = new OTOpndListIni(); | ||
3367 | listini.values = new OTOpnd[0]; | ||
3368 | return listini; | ||
3369 | } | ||
3370 | } | ||
3371 | |||
3372 | // something else, output as is | ||
3373 | OTOpndNewobj it = new OTOpndNewobj(); | ||
3374 | it.ctor = ctor; | ||
3375 | it.args = args; | ||
3376 | return it; | ||
3377 | } | ||
3378 | |||
3379 | private OTOpndNewobj() | ||
3380 | { | ||
3381 | } | ||
3382 | |||
3383 | public override bool HasSideEffects | ||
3384 | { | ||
3385 | get | ||
3386 | { | ||
3387 | foreach(OTOpnd arg in args) | ||
3388 | { | ||
3389 | if(arg.HasSideEffects) | ||
3390 | return true; | ||
3391 | } | ||
3392 | return false; | ||
3393 | } | ||
3394 | } | ||
3395 | |||
3396 | public override void CountRefs(bool writing) | ||
3397 | { | ||
3398 | foreach(OTOpnd arg in args) | ||
3399 | { | ||
3400 | arg.CountRefs(false); | ||
3401 | } | ||
3402 | } | ||
3403 | |||
3404 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
3405 | { | ||
3406 | if(SameAs(oldopnd)) | ||
3407 | { | ||
3408 | rc = true; | ||
3409 | return newopnd; | ||
3410 | } | ||
3411 | for(int i = 0; i < args.Length; i++) | ||
3412 | { | ||
3413 | args[i] = args[i].ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3414 | } | ||
3415 | return this; | ||
3416 | } | ||
3417 | |||
3418 | public override bool SameAs(OTOpnd other) | ||
3419 | { | ||
3420 | if(!(other is OTOpndNewobj)) | ||
3421 | return false; | ||
3422 | OTOpndNewobj otherno = (OTOpndNewobj)other; | ||
3423 | if(otherno.ctor.DeclaringType != ctor.DeclaringType) | ||
3424 | return false; | ||
3425 | if(otherno.args.Length != args.Length) | ||
3426 | return false; | ||
3427 | for(int i = 0; i < args.Length; i++) | ||
3428 | { | ||
3429 | if(!args[i].SameAs(otherno.args[i])) | ||
3430 | return false; | ||
3431 | } | ||
3432 | return true; | ||
3433 | } | ||
3434 | |||
3435 | public override string PrintableString | ||
3436 | { | ||
3437 | get | ||
3438 | { | ||
3439 | StringBuilder sb = new StringBuilder(); | ||
3440 | sb.Append("newobj "); | ||
3441 | sb.Append(ctor.DeclaringType.Name); | ||
3442 | sb.Append(" ("); | ||
3443 | bool first = true; | ||
3444 | foreach(OTOpnd arg in args) | ||
3445 | { | ||
3446 | if(!first) | ||
3447 | sb.Append(", "); | ||
3448 | sb.Append(arg.PrintableString); | ||
3449 | first = false; | ||
3450 | } | ||
3451 | sb.Append(')'); | ||
3452 | return sb.ToString(); | ||
3453 | } | ||
3454 | } | ||
3455 | } | ||
3456 | |||
3457 | /** | ||
3458 | * Rotation value. | ||
3459 | */ | ||
3460 | private class OTOpndRot: OTOpnd | ||
3461 | { | ||
3462 | private OTOpnd x, y, z, w; | ||
3463 | |||
3464 | public OTOpndRot(OTOpnd x, OTOpnd y, OTOpnd z, OTOpnd w) | ||
3465 | { | ||
3466 | this.x = StripFloatCast(x); | ||
3467 | this.y = StripFloatCast(y); | ||
3468 | this.z = StripFloatCast(z); | ||
3469 | this.w = StripFloatCast(w); | ||
3470 | } | ||
3471 | |||
3472 | public override bool HasSideEffects | ||
3473 | { | ||
3474 | get | ||
3475 | { | ||
3476 | return x.HasSideEffects || y.HasSideEffects || z.HasSideEffects || w.HasSideEffects; | ||
3477 | } | ||
3478 | } | ||
3479 | |||
3480 | public override void CountRefs(bool writing) | ||
3481 | { | ||
3482 | x.CountRefs(false); | ||
3483 | y.CountRefs(false); | ||
3484 | z.CountRefs(false); | ||
3485 | w.CountRefs(false); | ||
3486 | } | ||
3487 | |||
3488 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
3489 | { | ||
3490 | if(SameAs(oldopnd)) | ||
3491 | { | ||
3492 | rc = true; | ||
3493 | return newopnd; | ||
3494 | } | ||
3495 | x = x.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3496 | y = y.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3497 | z = z.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3498 | w = w.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3499 | return this; | ||
3500 | } | ||
3501 | |||
3502 | public override bool SameAs(OTOpnd other) | ||
3503 | { | ||
3504 | if(!(other is OTOpndRot)) | ||
3505 | return false; | ||
3506 | OTOpndRot otherv = (OTOpndRot)other; | ||
3507 | return otherv.x.SameAs(x) && otherv.y.SameAs(y) && otherv.z.SameAs(z) && otherv.w.SameAs(w); | ||
3508 | } | ||
3509 | |||
3510 | public override string PrintableString | ||
3511 | { | ||
3512 | get | ||
3513 | { | ||
3514 | return "<" + x.PrintableString + ", " + y.PrintableString + ", " + z.PrintableString + ", " + w.PrintableString + ">"; | ||
3515 | } | ||
3516 | } | ||
3517 | } | ||
3518 | |||
3519 | /** | ||
3520 | * Static field. | ||
3521 | */ | ||
3522 | private class OTOpndSField: OTOpnd | ||
3523 | { | ||
3524 | private FieldInfo field; | ||
3525 | |||
3526 | public OTOpndSField(FieldInfo field) | ||
3527 | { | ||
3528 | this.field = field; | ||
3529 | } | ||
3530 | |||
3531 | public override bool HasSideEffects | ||
3532 | { | ||
3533 | get | ||
3534 | { | ||
3535 | return false; | ||
3536 | } | ||
3537 | } | ||
3538 | |||
3539 | public override bool SameAs(OTOpnd other) | ||
3540 | { | ||
3541 | if(!(other is OTOpndSField)) | ||
3542 | return false; | ||
3543 | OTOpndSField othersfield = (OTOpndSField)other; | ||
3544 | return (field.Name == othersfield.field.Name) && (field.DeclaringType == othersfield.field.DeclaringType); | ||
3545 | } | ||
3546 | |||
3547 | public override string PrintableString | ||
3548 | { | ||
3549 | get | ||
3550 | { | ||
3551 | if(field.DeclaringType == typeof(ScriptBaseClass)) | ||
3552 | return field.Name; | ||
3553 | return field.DeclaringType.Name + "." + field.Name; | ||
3554 | } | ||
3555 | } | ||
3556 | } | ||
3557 | |||
3558 | /** | ||
3559 | * Call to string.Compare(). | ||
3560 | * See use cases in BinOpStr: | ||
3561 | * strcmp (a, b) ceq 0 | ||
3562 | * (strcmp (a, b) ceq 0) xor 1 => we translate to: strcmp (a, b) cne 0 | ||
3563 | * strcmp (a, b) clt 0 | ||
3564 | * strcmp (a, b) clt 1 // <= | ||
3565 | * strcmp (a, b) cgt 0 | ||
3566 | * strcmp (a, b) cgt -1 // >= | ||
3567 | * ...but then optimized by ScriptCollector if followed by br{false,true}: | ||
3568 | * ceq + xor 1 + brtrue => bne.un | ||
3569 | * ceq + xor 1 + brfalse => beq | ||
3570 | * ceq + brtrue => beq | ||
3571 | * ceq + brfalse => bne.un | ||
3572 | * cgt + brtrue => bgt | ||
3573 | * cgt + brfalse => ble | ||
3574 | * clt + brtrue => blt | ||
3575 | * clt + brfalse => bge | ||
3576 | * So we end up with these cases: | ||
3577 | * strcmp (a, b) ceq 0 | ||
3578 | * strcmp (a, b) cne 0 | ||
3579 | * strcmp (a, b) clt 0 | ||
3580 | * strcmp (a, b) clt 1 | ||
3581 | * strcmp (a, b) cgt 0 | ||
3582 | * strcmp (a, b) cgt -1 | ||
3583 | * strcmp (a, b) beq 0 | ||
3584 | * strcmp (a, b) bne.un 0 | ||
3585 | * strcmp (a, b) bgt 0 | ||
3586 | * strcmp (a, b) ble 0 | ||
3587 | * strcmp (a, b) bgt -1 | ||
3588 | * strcmp (a, b) ble -1 | ||
3589 | * strcmp (a, b) blt 0 | ||
3590 | * strcmp (a, b) bge 0 | ||
3591 | * strcmp (a, b) blt 1 | ||
3592 | * strcmp (a, b) bge 1 | ||
3593 | * ... so we pretty them up in OTOpndBinOp | ||
3594 | */ | ||
3595 | private class OTOpndStrCmp: OTOpnd | ||
3596 | { | ||
3597 | private static Dictionary<string, string> binops = InitBinops(); | ||
3598 | private static Dictionary<string, string> InitBinops() | ||
3599 | { | ||
3600 | Dictionary<string, string> d = new Dictionary<string, string>(); | ||
3601 | d["ceq 0"] = "ceq"; | ||
3602 | d["cne 0"] = "cne"; | ||
3603 | d["clt 0"] = "clt"; | ||
3604 | d["clt 1"] = "cle"; | ||
3605 | d["cgt 0"] = "cgt"; | ||
3606 | d["cgt -1"] = "cge"; | ||
3607 | d["beq 0"] = "ceq"; | ||
3608 | d["bne.un 0"] = "cne"; | ||
3609 | d["bgt 0"] = "cgt"; | ||
3610 | d["ble 0"] = "cle"; | ||
3611 | d["bgt -1"] = "cge"; | ||
3612 | d["ble -1"] = "clt"; | ||
3613 | d["blt 0"] = "clt"; | ||
3614 | d["bge 0"] = "cge"; | ||
3615 | d["blt 1"] = "cle"; | ||
3616 | d["bge 1"] = "cgt"; | ||
3617 | return d; | ||
3618 | } | ||
3619 | |||
3620 | private OTOpnd arg0; | ||
3621 | private OTOpnd arg1; | ||
3622 | |||
3623 | public OTOpndStrCmp(OTOpnd arg0, OTOpnd arg1) | ||
3624 | { | ||
3625 | this.arg0 = arg0; | ||
3626 | this.arg1 = arg1; | ||
3627 | } | ||
3628 | |||
3629 | /** | ||
3630 | * Try to make something a script writer would recognize. | ||
3631 | * If we can't, then we leave it as a call to xmrStringCompare(). | ||
3632 | * this = some strcmp(a,b) | ||
3633 | * opCode = hopefully some cxx or bxx from above table | ||
3634 | * rite = hopefully some constant from above table | ||
3635 | */ | ||
3636 | public OTOpnd MakeBinOp(MyOp opCode, OTOpnd rite) | ||
3637 | { | ||
3638 | if(!(rite is OTOpndInt)) | ||
3639 | return null; | ||
3640 | int riteint = ((OTOpndInt)rite).value; | ||
3641 | string key = opCode.name + ' ' + riteint; | ||
3642 | string cxxopname; | ||
3643 | if(!binops.TryGetValue(key, out cxxopname)) | ||
3644 | return null; | ||
3645 | return OTOpndBinOp.Make(arg0, MyOp.GetByName(cxxopname), arg1); | ||
3646 | } | ||
3647 | public OTOpnd MakeUnOp(MyOp opCode) | ||
3648 | { | ||
3649 | if(opCode == MyOp.Brfalse) | ||
3650 | return OTOpndBinOp.Make(arg0, MyOp.Ceq, arg1); | ||
3651 | if(opCode == MyOp.Brtrue) | ||
3652 | return OTOpndBinOp.Make(arg0, MyOp.Cne, arg1); | ||
3653 | return null; | ||
3654 | } | ||
3655 | |||
3656 | public override bool HasSideEffects | ||
3657 | { | ||
3658 | get | ||
3659 | { | ||
3660 | return false; | ||
3661 | } | ||
3662 | } | ||
3663 | |||
3664 | public override void CountRefs(bool writing) | ||
3665 | { | ||
3666 | arg0.CountRefs(writing); | ||
3667 | arg1.CountRefs(writing); | ||
3668 | } | ||
3669 | |||
3670 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
3671 | { | ||
3672 | if(SameAs(oldopnd)) | ||
3673 | { | ||
3674 | rc = true; | ||
3675 | return newopnd; | ||
3676 | } | ||
3677 | arg0 = arg0.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3678 | arg1 = arg1.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3679 | return this; | ||
3680 | } | ||
3681 | |||
3682 | public override bool SameAs(OTOpnd other) | ||
3683 | { | ||
3684 | if(!(other is OTOpndStrCmp)) | ||
3685 | return false; | ||
3686 | return arg0.SameAs(((OTOpndStrCmp)other).arg0) && arg1.SameAs(((OTOpndStrCmp)other).arg1); | ||
3687 | } | ||
3688 | |||
3689 | public override string PrintableString | ||
3690 | { | ||
3691 | get | ||
3692 | { | ||
3693 | return "xmrStringCompare (" + arg0.PrintableString + ", " + arg1.PrintableString + ")"; | ||
3694 | } | ||
3695 | } | ||
3696 | } | ||
3697 | |||
3698 | /** | ||
3699 | * Unary operator. | ||
3700 | */ | ||
3701 | private class OTOpndUnOp: OTOpnd | ||
3702 | { | ||
3703 | public MyOp opCode; | ||
3704 | public OTOpnd value; | ||
3705 | |||
3706 | private static Dictionary<string, string> brfops = InitBrfOps(); | ||
3707 | private static Dictionary<string, string> InitBrfOps() | ||
3708 | { | ||
3709 | Dictionary<string, string> d = new Dictionary<string, string>(); | ||
3710 | d["beq"] = "cne"; | ||
3711 | d["bge"] = "clt"; | ||
3712 | d["bgt"] = "cle"; | ||
3713 | d["ble"] = "cgt"; | ||
3714 | d["blt"] = "cge"; | ||
3715 | d["bne.un"] = "ceq"; | ||
3716 | d["ceq"] = "cne"; | ||
3717 | d["cge"] = "clt"; | ||
3718 | d["cgt"] = "cle"; | ||
3719 | d["cle"] = "cgt"; | ||
3720 | d["clt"] = "cge"; | ||
3721 | d["cne"] = "ceq"; | ||
3722 | return d; | ||
3723 | } | ||
3724 | |||
3725 | public static OTOpnd Make(MyOp opCode, OTOpnd value) | ||
3726 | { | ||
3727 | // (brfalse (brfalse (x))) => (brtrue (x)) | ||
3728 | if((opCode == MyOp.Brfalse) && (value is OTOpndUnOp) && (((OTOpndUnOp)value).opCode == MyOp.Brfalse)) | ||
3729 | { | ||
3730 | ((OTOpndUnOp)value).opCode = MyOp.Brtrue; | ||
3731 | return value; | ||
3732 | } | ||
3733 | |||
3734 | // (brfalse (brtrue (x))) => (brfalse (x)) | ||
3735 | if((opCode == MyOp.Brfalse) && (value is OTOpndUnOp) && (((OTOpndUnOp)value).opCode == MyOp.Brtrue)) | ||
3736 | { | ||
3737 | ((OTOpndUnOp)value).opCode = MyOp.Brfalse; | ||
3738 | return value; | ||
3739 | } | ||
3740 | |||
3741 | // (brtrue (brfalse (x))) => (brfalse (x)) | ||
3742 | if((opCode == MyOp.Brtrue) && (value is OTOpndUnOp) && (((OTOpndUnOp)value).opCode == MyOp.Brfalse)) | ||
3743 | { | ||
3744 | return value; | ||
3745 | } | ||
3746 | |||
3747 | // (brtrue (brtrue (x))) => (brtrue (x)) | ||
3748 | if((opCode == MyOp.Brtrue) && (value is OTOpndUnOp) && (((OTOpndUnOp)value).opCode == MyOp.Brtrue)) | ||
3749 | { | ||
3750 | return value; | ||
3751 | } | ||
3752 | |||
3753 | // (brfalse (x beq y)) => (x bne y) etc | ||
3754 | string brfop; | ||
3755 | if((opCode == MyOp.Brfalse) && (value is OTOpndBinOp) && brfops.TryGetValue(((OTOpndBinOp)value).opCode.name, out brfop)) | ||
3756 | { | ||
3757 | ((OTOpndBinOp)value).opCode = MyOp.GetByName(brfop); | ||
3758 | return value; | ||
3759 | } | ||
3760 | |||
3761 | // (brtrue (x beq y)) => (x beq y) etc | ||
3762 | if((opCode == MyOp.Brtrue) && (value is OTOpndBinOp) && brfops.ContainsKey(((OTOpndBinOp)value).opCode.name)) | ||
3763 | { | ||
3764 | return value; | ||
3765 | } | ||
3766 | |||
3767 | // strcmp() can be a special case | ||
3768 | if(value is OTOpndStrCmp) | ||
3769 | { | ||
3770 | OTOpnd strcmp = ((OTOpndStrCmp)value).MakeUnOp(opCode); | ||
3771 | if(strcmp != null) | ||
3772 | return strcmp; | ||
3773 | } | ||
3774 | |||
3775 | // nothing special, save opcode and value | ||
3776 | OTOpndUnOp it = new OTOpndUnOp(); | ||
3777 | it.opCode = opCode; | ||
3778 | it.value = value; | ||
3779 | return it; | ||
3780 | } | ||
3781 | |||
3782 | private OTOpndUnOp() | ||
3783 | { | ||
3784 | } | ||
3785 | |||
3786 | public override bool HasSideEffects | ||
3787 | { | ||
3788 | get | ||
3789 | { | ||
3790 | return value.HasSideEffects; | ||
3791 | } | ||
3792 | } | ||
3793 | |||
3794 | public override void CountRefs(bool writing) | ||
3795 | { | ||
3796 | value.CountRefs(false); | ||
3797 | } | ||
3798 | |||
3799 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
3800 | { | ||
3801 | if(SameAs(oldopnd)) | ||
3802 | { | ||
3803 | rc = true; | ||
3804 | return newopnd; | ||
3805 | } | ||
3806 | value = value.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3807 | return this; | ||
3808 | } | ||
3809 | |||
3810 | public override bool SameAs(OTOpnd other) | ||
3811 | { | ||
3812 | if(!(other is OTOpndUnOp)) | ||
3813 | return false; | ||
3814 | OTOpndUnOp otherop = (OTOpndUnOp)other; | ||
3815 | return (opCode.ToString() == otherop.opCode.ToString()) && value.SameAs(otherop.value); | ||
3816 | } | ||
3817 | |||
3818 | public override string PrintableString | ||
3819 | { | ||
3820 | get | ||
3821 | { | ||
3822 | StringBuilder sb = new StringBuilder(); | ||
3823 | sb.Append(opCode.source); | ||
3824 | sb.Append(' '); | ||
3825 | if(value is OTOpndBinOp) | ||
3826 | sb.Append('('); | ||
3827 | sb.Append(value.PrintableString); | ||
3828 | if(value is OTOpndBinOp) | ||
3829 | sb.Append(')'); | ||
3830 | return sb.ToString(); | ||
3831 | } | ||
3832 | } | ||
3833 | } | ||
3834 | |||
3835 | /** | ||
3836 | * Vector value. | ||
3837 | */ | ||
3838 | private class OTOpndVec: OTOpnd | ||
3839 | { | ||
3840 | private OTOpnd x, y, z; | ||
3841 | |||
3842 | public OTOpndVec(OTOpnd x, OTOpnd y, OTOpnd z) | ||
3843 | { | ||
3844 | this.x = StripFloatCast(x); | ||
3845 | this.y = StripFloatCast(y); | ||
3846 | this.z = StripFloatCast(z); | ||
3847 | } | ||
3848 | |||
3849 | public override bool HasSideEffects | ||
3850 | { | ||
3851 | get | ||
3852 | { | ||
3853 | return x.HasSideEffects || y.HasSideEffects || z.HasSideEffects; | ||
3854 | } | ||
3855 | } | ||
3856 | |||
3857 | public override void CountRefs(bool writing) | ||
3858 | { | ||
3859 | x.CountRefs(false); | ||
3860 | y.CountRefs(false); | ||
3861 | z.CountRefs(false); | ||
3862 | } | ||
3863 | |||
3864 | public override OTOpnd ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd, ref bool rc) | ||
3865 | { | ||
3866 | if(SameAs(oldopnd)) | ||
3867 | { | ||
3868 | rc = true; | ||
3869 | return newopnd; | ||
3870 | } | ||
3871 | x = x.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3872 | y = y.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3873 | z = z.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
3874 | return this; | ||
3875 | } | ||
3876 | |||
3877 | public override bool SameAs(OTOpnd other) | ||
3878 | { | ||
3879 | if(!(other is OTOpndVec)) | ||
3880 | return false; | ||
3881 | OTOpndVec otherv = (OTOpndVec)other; | ||
3882 | return otherv.x.SameAs(x) && otherv.y.SameAs(y) && otherv.z.SameAs(z); | ||
3883 | } | ||
3884 | |||
3885 | public override string PrintableString | ||
3886 | { | ||
3887 | get | ||
3888 | { | ||
3889 | return "<" + x.PrintableString + ", " + y.PrintableString + ", " + z.PrintableString + ">"; | ||
3890 | } | ||
3891 | } | ||
3892 | } | ||
3893 | |||
3894 | /** | ||
3895 | * Constants. | ||
3896 | */ | ||
3897 | private class OTOpndDouble: OTOpnd | ||
3898 | { | ||
3899 | public double value; | ||
3900 | public OTOpndDouble(double value) | ||
3901 | { | ||
3902 | this.value = value; | ||
3903 | } | ||
3904 | public override bool HasSideEffects | ||
3905 | { | ||
3906 | get | ||
3907 | { | ||
3908 | return false; | ||
3909 | } | ||
3910 | } | ||
3911 | public override bool SameAs(OTOpnd other) | ||
3912 | { | ||
3913 | if(!(other is OTOpndDouble)) | ||
3914 | return false; | ||
3915 | return ((OTOpndDouble)other).value == value; | ||
3916 | } | ||
3917 | public override string PrintableString | ||
3918 | { | ||
3919 | get | ||
3920 | { | ||
3921 | string s = value.ToString(); | ||
3922 | long i; | ||
3923 | if(long.TryParse(s, out i)) | ||
3924 | { | ||
3925 | s += ".0"; | ||
3926 | } | ||
3927 | return s; | ||
3928 | } | ||
3929 | } | ||
3930 | } | ||
3931 | private class OTOpndFloat: OTOpnd | ||
3932 | { | ||
3933 | public float value; | ||
3934 | public OTOpndFloat(float value) | ||
3935 | { | ||
3936 | this.value = value; | ||
3937 | } | ||
3938 | public override bool HasSideEffects | ||
3939 | { | ||
3940 | get | ||
3941 | { | ||
3942 | return false; | ||
3943 | } | ||
3944 | } | ||
3945 | public override bool SameAs(OTOpnd other) | ||
3946 | { | ||
3947 | if(!(other is OTOpndFloat)) | ||
3948 | return false; | ||
3949 | return ((OTOpndFloat)other).value == value; | ||
3950 | } | ||
3951 | public override string PrintableString | ||
3952 | { | ||
3953 | get | ||
3954 | { | ||
3955 | string s = value.ToString(); | ||
3956 | long i; | ||
3957 | if(long.TryParse(s, out i)) | ||
3958 | { | ||
3959 | s += ".0"; | ||
3960 | } | ||
3961 | return s; | ||
3962 | } | ||
3963 | } | ||
3964 | } | ||
3965 | private class OTOpndInt: OTOpnd | ||
3966 | { | ||
3967 | public int value; | ||
3968 | public OTOpndInt(int value) | ||
3969 | { | ||
3970 | this.value = value; | ||
3971 | } | ||
3972 | public override bool HasSideEffects | ||
3973 | { | ||
3974 | get | ||
3975 | { | ||
3976 | return false; | ||
3977 | } | ||
3978 | } | ||
3979 | public override bool SameAs(OTOpnd other) | ||
3980 | { | ||
3981 | if(!(other is OTOpndInt)) | ||
3982 | return false; | ||
3983 | return ((OTOpndInt)other).value == value; | ||
3984 | } | ||
3985 | public override string PrintableString | ||
3986 | { | ||
3987 | get | ||
3988 | { | ||
3989 | return value.ToString(); | ||
3990 | } | ||
3991 | } | ||
3992 | } | ||
3993 | private class OTOpndNull: OTOpnd | ||
3994 | { | ||
3995 | public override bool HasSideEffects | ||
3996 | { | ||
3997 | get | ||
3998 | { | ||
3999 | return false; | ||
4000 | } | ||
4001 | } | ||
4002 | public override bool SameAs(OTOpnd other) | ||
4003 | { | ||
4004 | return other is OTOpndNull; | ||
4005 | } | ||
4006 | public override string PrintableString | ||
4007 | { | ||
4008 | get | ||
4009 | { | ||
4010 | return "undef"; | ||
4011 | } | ||
4012 | } | ||
4013 | } | ||
4014 | private class OTOpndString: OTOpnd | ||
4015 | { | ||
4016 | public string value; | ||
4017 | public OTOpndString(string value) | ||
4018 | { | ||
4019 | this.value = value; | ||
4020 | } | ||
4021 | public override bool HasSideEffects | ||
4022 | { | ||
4023 | get | ||
4024 | { | ||
4025 | return false; | ||
4026 | } | ||
4027 | } | ||
4028 | public override bool SameAs(OTOpnd other) | ||
4029 | { | ||
4030 | if(!(other is OTOpndString)) | ||
4031 | return false; | ||
4032 | return ((OTOpndString)other).value == value; | ||
4033 | } | ||
4034 | public override string PrintableString | ||
4035 | { | ||
4036 | get | ||
4037 | { | ||
4038 | StringBuilder sb = new StringBuilder(); | ||
4039 | TokenDeclInline.PrintParamString(sb, value); | ||
4040 | return sb.ToString(); | ||
4041 | } | ||
4042 | } | ||
4043 | } | ||
4044 | |||
4045 | /****************************************\ | ||
4046 | * Tokens what are in statement list. * | ||
4047 | \****************************************/ | ||
4048 | |||
4049 | public abstract class OTStmt | ||
4050 | { | ||
4051 | |||
4052 | /** | ||
4053 | * Increment reference counts. | ||
4054 | */ | ||
4055 | public abstract void CountRefs(); | ||
4056 | |||
4057 | /** | ||
4058 | * Strip out any of the behind-the-scenes code such as stack capture/restore. | ||
4059 | * By default, there is no change. | ||
4060 | */ | ||
4061 | public virtual bool StripStuff(LinkedListNode<OTStmt> link) | ||
4062 | { | ||
4063 | return false; | ||
4064 | } | ||
4065 | |||
4066 | /** | ||
4067 | * Replace the oldopnd operand with the newopnd operand if it is present. | ||
4068 | * Return whether or not it was found and replaced. | ||
4069 | */ | ||
4070 | public abstract bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd); | ||
4071 | |||
4072 | /** | ||
4073 | * Detect and modify for do/for/if/while structures. | ||
4074 | */ | ||
4075 | public virtual bool DetectDoForIfWhile(LinkedListNode<OTStmt> link) | ||
4076 | { | ||
4077 | return false; | ||
4078 | } | ||
4079 | |||
4080 | /** | ||
4081 | * If this statement is the old statement, replace it with the given new statement. | ||
4082 | * Also search any sub-ordinate statements. | ||
4083 | * **NOTE**: minimally implemented to replace a Jump with a Break or Continue | ||
4084 | */ | ||
4085 | public abstract OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt); | ||
4086 | |||
4087 | /** | ||
4088 | * Print the statement out on the given printer with the given indenting. | ||
4089 | * The first line is already indented, subsequent lines must be indented as given. | ||
4090 | * This method should leave the printer at the end of the line. | ||
4091 | */ | ||
4092 | public abstract void PrintStmt(TextWriter twout, string indent); | ||
4093 | |||
4094 | /** | ||
4095 | * Strip all statements following this statement | ||
4096 | * because this statement jumps somewhere. | ||
4097 | */ | ||
4098 | protected bool StripStuffForTerminal(LinkedListNode<OTStmt> link) | ||
4099 | { | ||
4100 | // strip all statements following jump until seeing some label | ||
4101 | bool rc = false; | ||
4102 | if(link != null) | ||
4103 | { | ||
4104 | LinkedListNode<OTStmt> nextlink; | ||
4105 | while((nextlink = link.Next) != null) | ||
4106 | { | ||
4107 | if(nextlink.Value is OTStmtLabel) | ||
4108 | break; | ||
4109 | nextlink.List.Remove(nextlink); | ||
4110 | rc = true; | ||
4111 | } | ||
4112 | } | ||
4113 | return rc; | ||
4114 | } | ||
4115 | } | ||
4116 | |||
4117 | /**************************\ | ||
4118 | * Primitive statements * | ||
4119 | \**************************/ | ||
4120 | |||
4121 | /** | ||
4122 | * Begin catch block (catch). | ||
4123 | */ | ||
4124 | private class OTStmtBegCatBlk: OTStmt | ||
4125 | { | ||
4126 | public OTStmtBegExcBlk tryblock; | ||
4127 | public OTStmtBlock catchblock; | ||
4128 | |||
4129 | private Type excType; | ||
4130 | |||
4131 | public OTStmtBegCatBlk(Type excType) | ||
4132 | { | ||
4133 | this.excType = excType; | ||
4134 | } | ||
4135 | |||
4136 | public override void CountRefs() | ||
4137 | { | ||
4138 | catchblock.CountRefs(); | ||
4139 | } | ||
4140 | |||
4141 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
4142 | { | ||
4143 | return catchblock.StripStuff(null); | ||
4144 | } | ||
4145 | |||
4146 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
4147 | { | ||
4148 | return catchblock.ReplaceOperand(oldopnd, newopnd); | ||
4149 | } | ||
4150 | |||
4151 | public override bool DetectDoForIfWhile(LinkedListNode<OTStmt> link) | ||
4152 | { | ||
4153 | return catchblock.DetectDoForIfWhile(link); | ||
4154 | } | ||
4155 | |||
4156 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
4157 | { | ||
4158 | catchblock = (OTStmtBlock)catchblock.ReplaceStatement(oldstmt, newstmt); | ||
4159 | return this; | ||
4160 | } | ||
4161 | |||
4162 | /** | ||
4163 | * Print out the catch block including its enclosed statements. | ||
4164 | */ | ||
4165 | public override void PrintStmt(TextWriter twout, string indent) | ||
4166 | { | ||
4167 | twout.Write("catch (" + excType.Name + ") "); | ||
4168 | catchblock.PrintStmt(twout, indent); | ||
4169 | } | ||
4170 | } | ||
4171 | |||
4172 | /** | ||
4173 | * Begin exception block (try). | ||
4174 | */ | ||
4175 | private class OTStmtBegExcBlk: OTStmt | ||
4176 | { | ||
4177 | |||
4178 | // statements within the try { } not including any catch or finally | ||
4179 | public OTStmtBlock tryblock; | ||
4180 | |||
4181 | // list of all catch { } blocks associated with this try { } | ||
4182 | public LinkedList<OTStmtBegCatBlk> catches = new LinkedList<OTStmtBegCatBlk>(); | ||
4183 | |||
4184 | // possible single finally { } associated with this try | ||
4185 | public OTStmtBegFinBlk finblock; // might be null | ||
4186 | |||
4187 | public override void CountRefs() | ||
4188 | { | ||
4189 | tryblock.CountRefs(); | ||
4190 | foreach(OTStmtBegCatBlk catblock in catches) | ||
4191 | { | ||
4192 | catblock.CountRefs(); | ||
4193 | } | ||
4194 | if(finblock != null) | ||
4195 | finblock.CountRefs(); | ||
4196 | } | ||
4197 | |||
4198 | /** | ||
4199 | * Strip behind-the-scenes info from all the sub-blocks. | ||
4200 | */ | ||
4201 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
4202 | { | ||
4203 | // strip behind-the-scenes info from all the sub-blocks. | ||
4204 | bool rc = tryblock.StripStuff(null); | ||
4205 | foreach(OTStmtBegCatBlk catblk in catches) | ||
4206 | { | ||
4207 | rc |= catblk.StripStuff(null); | ||
4208 | } | ||
4209 | if(finblock != null) | ||
4210 | rc |= finblock.StripStuff(null); | ||
4211 | if(rc) | ||
4212 | return true; | ||
4213 | |||
4214 | // change: | ||
4215 | // try { | ||
4216 | // ... | ||
4217 | // } | ||
4218 | // to: | ||
4219 | // { | ||
4220 | // ... | ||
4221 | // } | ||
4222 | // note that an empty catch () { } has meaning so can't be stripped | ||
4223 | // empty finally { } blocks strips itself from the try | ||
4224 | if((catches.Count == 0) && (finblock == null) && (link != null)) | ||
4225 | { | ||
4226 | link.List.AddAfter(link, tryblock); | ||
4227 | tryblock = null; | ||
4228 | link.List.Remove(link); | ||
4229 | return true; | ||
4230 | } | ||
4231 | |||
4232 | return false; | ||
4233 | } | ||
4234 | |||
4235 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
4236 | { | ||
4237 | bool rc = tryblock.ReplaceOperand(oldopnd, newopnd); | ||
4238 | foreach(OTStmtBegCatBlk catblk in catches) | ||
4239 | { | ||
4240 | rc |= catblk.ReplaceOperand(oldopnd, newopnd); | ||
4241 | } | ||
4242 | if(finblock != null) | ||
4243 | rc |= finblock.ReplaceOperand(oldopnd, newopnd); | ||
4244 | return rc; | ||
4245 | } | ||
4246 | |||
4247 | public override bool DetectDoForIfWhile(LinkedListNode<OTStmt> link) | ||
4248 | { | ||
4249 | bool rc = tryblock.DetectDoForIfWhile(link); | ||
4250 | foreach(OTStmtBegCatBlk catblk in catches) | ||
4251 | { | ||
4252 | rc |= catblk.DetectDoForIfWhile(link); | ||
4253 | } | ||
4254 | if(finblock != null) | ||
4255 | rc |= finblock.DetectDoForIfWhile(link); | ||
4256 | return rc; | ||
4257 | } | ||
4258 | |||
4259 | /** | ||
4260 | * Assume we will never try to replace the try block itself. | ||
4261 | * But go through all our sub-ordinates statements. | ||
4262 | */ | ||
4263 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
4264 | { | ||
4265 | tryblock = (OTStmtBlock)tryblock.ReplaceStatement(oldstmt, newstmt); | ||
4266 | for(LinkedListNode<OTStmtBegCatBlk> catlink = catches.First; catlink != null; catlink = catlink.Next) | ||
4267 | { | ||
4268 | catlink.Value = (OTStmtBegCatBlk)catlink.Value.ReplaceStatement(oldstmt, newstmt); | ||
4269 | } | ||
4270 | if(finblock != null) | ||
4271 | finblock = (OTStmtBegFinBlk)finblock.ReplaceStatement(oldstmt, newstmt); | ||
4272 | return this; | ||
4273 | } | ||
4274 | |||
4275 | /** | ||
4276 | * Print out the try block including its enclosed statements. | ||
4277 | * And since the try is the only thing pushed to the outer block, | ||
4278 | * we also print out all the catch and finally blocks. | ||
4279 | */ | ||
4280 | public override void PrintStmt(TextWriter twout, string indent) | ||
4281 | { | ||
4282 | twout.Write("try "); | ||
4283 | tryblock.PrintStmt(twout, indent); | ||
4284 | foreach(OTStmtBegCatBlk catblk in catches) | ||
4285 | { | ||
4286 | twout.Write(' '); | ||
4287 | catblk.PrintStmt(twout, indent); | ||
4288 | } | ||
4289 | if(finblock != null) | ||
4290 | { | ||
4291 | twout.Write(' '); | ||
4292 | finblock.PrintStmt(twout, indent); | ||
4293 | } | ||
4294 | } | ||
4295 | } | ||
4296 | |||
4297 | /** | ||
4298 | * Begin finally block (finally). | ||
4299 | */ | ||
4300 | private class OTStmtBegFinBlk: OTStmt | ||
4301 | { | ||
4302 | public OTStmtBegExcBlk tryblock; | ||
4303 | public OTStmtBlock finblock; | ||
4304 | |||
4305 | public override void CountRefs() | ||
4306 | { | ||
4307 | finblock.CountRefs(); | ||
4308 | } | ||
4309 | |||
4310 | /** | ||
4311 | * Strip behind-the-scene parts from the finally block. | ||
4312 | */ | ||
4313 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
4314 | { | ||
4315 | // strip behind-the-scenes parts from finally block itself | ||
4316 | if(finblock.StripStuff(null)) | ||
4317 | return true; | ||
4318 | |||
4319 | // if finblock is empty, delete the finally from the try | ||
4320 | if(finblock.blkstmts.Count == 0) | ||
4321 | { | ||
4322 | tryblock.finblock = null; | ||
4323 | return true; | ||
4324 | } | ||
4325 | |||
4326 | return false; | ||
4327 | } | ||
4328 | |||
4329 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
4330 | { | ||
4331 | return finblock.ReplaceOperand(oldopnd, newopnd); | ||
4332 | } | ||
4333 | |||
4334 | public override bool DetectDoForIfWhile(LinkedListNode<OTStmt> link) | ||
4335 | { | ||
4336 | return finblock.DetectDoForIfWhile(link); | ||
4337 | } | ||
4338 | |||
4339 | /** | ||
4340 | * Assume we will never try to replace the finally block itself. | ||
4341 | * But go through all our sub-ordinates statements. | ||
4342 | */ | ||
4343 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
4344 | { | ||
4345 | finblock = (OTStmtBlock)finblock.ReplaceStatement(oldstmt, newstmt); | ||
4346 | return this; | ||
4347 | } | ||
4348 | |||
4349 | /** | ||
4350 | * Print out the finally block including its enclosed statements. | ||
4351 | */ | ||
4352 | public override void PrintStmt(TextWriter twout, string indent) | ||
4353 | { | ||
4354 | twout.Write("finally "); | ||
4355 | finblock.PrintStmt(twout, indent); | ||
4356 | } | ||
4357 | } | ||
4358 | |||
4359 | /** | ||
4360 | * Simple if jump/break/continue statement. | ||
4361 | */ | ||
4362 | private class OTStmtCond: OTStmt | ||
4363 | { | ||
4364 | public OTOpnd valu; | ||
4365 | public OTStmt stmt; // jump, break, continue only | ||
4366 | |||
4367 | public OTStmtCond(OTOpnd valu, OTStmt stmt) | ||
4368 | { | ||
4369 | this.valu = valu; | ||
4370 | this.stmt = stmt; | ||
4371 | } | ||
4372 | |||
4373 | public override void CountRefs() | ||
4374 | { | ||
4375 | valu.CountRefs(false); | ||
4376 | stmt.CountRefs(); | ||
4377 | } | ||
4378 | |||
4379 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
4380 | { | ||
4381 | // we assume that callMode is always CallMode_NORMAL, ie, not doing a stack capture or restore | ||
4382 | // so the 'if (arg$0.callMode bne.un 0) ...' is deleted | ||
4383 | // and the 'if (arg$0.callMode bne.un 1) ...' becomes unconditional | ||
4384 | // it can also be __xmrinst.callMode instead of arg$0 | ||
4385 | if(valu is OTOpndBinOp) | ||
4386 | { | ||
4387 | OTOpndBinOp binop = (OTOpndBinOp)valu; | ||
4388 | if((binop.left is OTOpndField) && (binop.opCode.ToString() == "bne.un") && (binop.rite is OTOpndInt)) | ||
4389 | { | ||
4390 | OTOpndField leftfield = (OTOpndField)binop.left; | ||
4391 | if(leftfield.field.Name == _callMode) | ||
4392 | { | ||
4393 | bool ok = false; | ||
4394 | if(leftfield.obj is OTOpndArg) | ||
4395 | { | ||
4396 | ok = ((OTOpndArg)leftfield.obj).index == 0; | ||
4397 | } | ||
4398 | if(leftfield.obj is OTOpndLocal) | ||
4399 | { | ||
4400 | ok = ((OTOpndLocal)leftfield.obj).local.name.StartsWith(_xmrinstlocal); | ||
4401 | } | ||
4402 | if(ok) | ||
4403 | { | ||
4404 | OTOpndInt riteint = (OTOpndInt)binop.rite; | ||
4405 | |||
4406 | // delete 'if ((arg$0).callMode bne.un 0) ...' | ||
4407 | if(riteint.value == XMRInstAbstract.CallMode_NORMAL) | ||
4408 | { | ||
4409 | link.List.Remove(link); | ||
4410 | return true; | ||
4411 | } | ||
4412 | |||
4413 | // make 'if ((arg$0).callMode bne.un 1) ...' unconditional | ||
4414 | if(riteint.value == XMRInstAbstract.CallMode_SAVE) | ||
4415 | { | ||
4416 | link.Value = stmt; | ||
4417 | return true; | ||
4418 | } | ||
4419 | } | ||
4420 | } | ||
4421 | } | ||
4422 | } | ||
4423 | |||
4424 | // similarly we assume that doGblInit is always 0 to eliminate the code at beginning of default state_entry() | ||
4425 | // so the 'if (brfalse __xmrinst.doGblInit) ...' is made unconditional | ||
4426 | if(valu is OTOpndUnOp) | ||
4427 | { | ||
4428 | OTOpndUnOp unop = (OTOpndUnOp)valu; | ||
4429 | if((unop.opCode == MyOp.Brfalse) && (unop.value is OTOpndField)) | ||
4430 | { | ||
4431 | OTOpndField valuefield = (OTOpndField)unop.value; | ||
4432 | if(valuefield.field.Name == _doGblInit) | ||
4433 | { | ||
4434 | bool ok = false; | ||
4435 | if(valuefield.obj is OTOpndLocal) | ||
4436 | { | ||
4437 | ok = ((OTOpndLocal)valuefield.obj).local.name.StartsWith(_xmrinstlocal); | ||
4438 | } | ||
4439 | if(ok) | ||
4440 | { | ||
4441 | |||
4442 | // make 'if (brfalse __xmrinst.doGblInit) ...' unconditional | ||
4443 | link.Value = stmt; | ||
4444 | return true; | ||
4445 | } | ||
4446 | } | ||
4447 | } | ||
4448 | } | ||
4449 | |||
4450 | return false; | ||
4451 | } | ||
4452 | |||
4453 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
4454 | { | ||
4455 | bool rc = stmt.ReplaceOperand(oldopnd, newopnd); | ||
4456 | valu = valu.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
4457 | return rc; | ||
4458 | } | ||
4459 | |||
4460 | /** | ||
4461 | * Maybe this simple if statement is part of a script-level if/then/else statement. | ||
4462 | */ | ||
4463 | public override bool DetectDoForIfWhile(LinkedListNode<OTStmt> link) | ||
4464 | { | ||
4465 | return OTStmtIf.Detect(link); | ||
4466 | } | ||
4467 | |||
4468 | /** | ||
4469 | * Assume we won't replace the if statement itself. | ||
4470 | * But search all our sub-ordinate statements. | ||
4471 | */ | ||
4472 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
4473 | { | ||
4474 | stmt = stmt.ReplaceStatement(oldstmt, newstmt); | ||
4475 | return this; | ||
4476 | } | ||
4477 | |||
4478 | public override void PrintStmt(TextWriter twout, string indent) | ||
4479 | { | ||
4480 | twout.Write("if (" + StripBrtrue(valu).PrintableString + ") "); | ||
4481 | stmt.PrintStmt(twout, indent); | ||
4482 | } | ||
4483 | |||
4484 | /** | ||
4485 | * Scan forward for a given label definition. | ||
4486 | * Put intervening statements in a statement block. | ||
4487 | * @param link = start scanning after this statement | ||
4488 | * @param label = look for this label definition | ||
4489 | * @param block = where to return intervening statement block | ||
4490 | * @returns null: label definition not found | ||
4491 | * else: label definition statement | ||
4492 | */ | ||
4493 | private static LinkedListNode<OTStmt> ScanForLabel(LinkedListNode<OTStmt> link, | ||
4494 | OTLabel label, out OTStmtBlock block) | ||
4495 | { | ||
4496 | block = new OTStmtBlock(); | ||
4497 | while((link = link.Next) != null) | ||
4498 | { | ||
4499 | if(link.Value is OTStmtLabel) | ||
4500 | { | ||
4501 | if(((OTStmtLabel)link.Value).label == label) | ||
4502 | break; | ||
4503 | } | ||
4504 | block.blkstmts.AddLast(link.Value); | ||
4505 | } | ||
4506 | return link; | ||
4507 | } | ||
4508 | |||
4509 | /** | ||
4510 | * Strip statements after link up to and including donelink. | ||
4511 | */ | ||
4512 | private static void StripInterveningStatements(LinkedListNode<OTStmt> link, LinkedListNode<OTStmt> donelink) | ||
4513 | { | ||
4514 | LinkedListNode<OTStmt> striplink; | ||
4515 | do | ||
4516 | { | ||
4517 | striplink = link.Next; | ||
4518 | striplink.List.Remove(striplink); | ||
4519 | } while(striplink != donelink); | ||
4520 | } | ||
4521 | } | ||
4522 | |||
4523 | /** | ||
4524 | * Jump to a label. | ||
4525 | */ | ||
4526 | private class OTStmtJump: OTStmt | ||
4527 | { | ||
4528 | public OTLabel label; | ||
4529 | |||
4530 | public static OTStmt Make(OTLabel label) | ||
4531 | { | ||
4532 | // jumps to __retlbl are return statements | ||
4533 | // note that is is safe to say it is a valueless return because | ||
4534 | // valued returns are done with this construct: | ||
4535 | // __retval = ....; | ||
4536 | // jump __retlbl; | ||
4537 | // and those __retval = statements have been changed to return statements already | ||
4538 | if(label.name.StartsWith(_retlbl)) | ||
4539 | return new OTStmtRet(null); | ||
4540 | |||
4541 | // other jumps are really jumps | ||
4542 | OTStmtJump it = new OTStmtJump(); | ||
4543 | it.label = label; | ||
4544 | return it; | ||
4545 | } | ||
4546 | |||
4547 | private OTStmtJump() | ||
4548 | { | ||
4549 | } | ||
4550 | |||
4551 | public override void CountRefs() | ||
4552 | { | ||
4553 | label.lbljumps++; | ||
4554 | } | ||
4555 | |||
4556 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
4557 | { | ||
4558 | if(link == null) | ||
4559 | return false; | ||
4560 | |||
4561 | // strip statements following unconditional jump until next label | ||
4562 | bool rc = StripStuffForTerminal(link); | ||
4563 | |||
4564 | // if we (now) have: | ||
4565 | // jump label; | ||
4566 | // @label; | ||
4567 | // ... delete this jump | ||
4568 | if(link.Next != null) | ||
4569 | { | ||
4570 | OTStmtLabel nextlabel = (OTStmtLabel)link.Next.Value; | ||
4571 | if(nextlabel.label == label) | ||
4572 | { | ||
4573 | link.List.Remove(link); | ||
4574 | rc = true; | ||
4575 | } | ||
4576 | } | ||
4577 | |||
4578 | return rc; | ||
4579 | } | ||
4580 | |||
4581 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
4582 | { | ||
4583 | return false; | ||
4584 | } | ||
4585 | |||
4586 | /** | ||
4587 | * This is actually what ReplaceStatement() is currently used for. | ||
4588 | * It replaces a jump with a break or a continue. | ||
4589 | */ | ||
4590 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
4591 | { | ||
4592 | if((oldstmt is OTStmtJump) && (((OTStmtJump)oldstmt).label == label)) | ||
4593 | return newstmt; | ||
4594 | return this; | ||
4595 | } | ||
4596 | |||
4597 | public override void PrintStmt(TextWriter twout, string indent) | ||
4598 | { | ||
4599 | twout.Write("jump " + label.PrintableName + ';'); | ||
4600 | } | ||
4601 | } | ||
4602 | |||
4603 | /** | ||
4604 | * Label definition point. | ||
4605 | */ | ||
4606 | private class OTStmtLabel: OTStmt | ||
4607 | { | ||
4608 | public OTLabel label; | ||
4609 | |||
4610 | private OTDecompile decompile; | ||
4611 | |||
4612 | public static void AddLast(OTDecompile decompile, OTLabel label) | ||
4613 | { | ||
4614 | OTStmtLabel it = new OTStmtLabel(); | ||
4615 | it.label = label; | ||
4616 | it.decompile = decompile; | ||
4617 | decompile.AddLastStmt(it); | ||
4618 | } | ||
4619 | |||
4620 | private OTStmtLabel() | ||
4621 | { | ||
4622 | } | ||
4623 | |||
4624 | public override void CountRefs() | ||
4625 | { | ||
4626 | // don't increment label.lbljumps | ||
4627 | // cuz we don't want the positioning | ||
4628 | // to count as a reference, only jumps | ||
4629 | // to the label should count | ||
4630 | } | ||
4631 | |||
4632 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
4633 | { | ||
4634 | // if label has nothing jumping to it, remove the label | ||
4635 | if(link != null) | ||
4636 | { | ||
4637 | label.lbljumps = 0; | ||
4638 | decompile.topBlock.CountRefs(); | ||
4639 | if(label.lbljumps == 0) | ||
4640 | { | ||
4641 | link.List.Remove(link); | ||
4642 | return true; | ||
4643 | } | ||
4644 | } | ||
4645 | |||
4646 | return false; | ||
4647 | } | ||
4648 | |||
4649 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
4650 | { | ||
4651 | return false; | ||
4652 | } | ||
4653 | |||
4654 | public override bool DetectDoForIfWhile(LinkedListNode<OTStmt> link) | ||
4655 | { | ||
4656 | if(OTStmtDo.Detect(link)) | ||
4657 | return true; | ||
4658 | if(OTStmtFor.Detect(link, true)) | ||
4659 | return true; | ||
4660 | if(OTStmtFor.Detect(link, false)) | ||
4661 | return true; | ||
4662 | return false; | ||
4663 | } | ||
4664 | |||
4665 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
4666 | { | ||
4667 | return this; | ||
4668 | } | ||
4669 | |||
4670 | public override void PrintStmt(TextWriter twout, string indent) | ||
4671 | { | ||
4672 | twout.Write("@" + label.PrintableName + ';'); | ||
4673 | } | ||
4674 | } | ||
4675 | |||
4676 | /** | ||
4677 | * Return with or without value. | ||
4678 | */ | ||
4679 | private class OTStmtRet: OTStmt | ||
4680 | { | ||
4681 | public OTOpnd value; // might be null | ||
4682 | |||
4683 | public OTStmtRet(OTOpnd value) | ||
4684 | { | ||
4685 | this.value = value; | ||
4686 | } | ||
4687 | |||
4688 | public override void CountRefs() | ||
4689 | { | ||
4690 | if(value != null) | ||
4691 | value.CountRefs(false); | ||
4692 | } | ||
4693 | |||
4694 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
4695 | { | ||
4696 | return StripStuffForTerminal(link); | ||
4697 | } | ||
4698 | |||
4699 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
4700 | { | ||
4701 | bool rc = false; | ||
4702 | if(value != null) | ||
4703 | value = value.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
4704 | return rc; | ||
4705 | } | ||
4706 | |||
4707 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
4708 | { | ||
4709 | return this; | ||
4710 | } | ||
4711 | |||
4712 | public override void PrintStmt(TextWriter twout, string indent) | ||
4713 | { | ||
4714 | if(value == null) | ||
4715 | { | ||
4716 | twout.Write("return;"); | ||
4717 | } | ||
4718 | else | ||
4719 | { | ||
4720 | twout.Write("return " + value.PrintableString + ';'); | ||
4721 | } | ||
4722 | } | ||
4723 | } | ||
4724 | |||
4725 | /** | ||
4726 | * Store value in variable. | ||
4727 | */ | ||
4728 | private class OTStmtStore: OTStmt | ||
4729 | { | ||
4730 | public OTOpnd varwr; | ||
4731 | public OTOpnd value; | ||
4732 | |||
4733 | private OTDecompile decompile; | ||
4734 | |||
4735 | public static void AddLast(OTDecompile decompile, OTOpnd varwr, OTOpnd value) | ||
4736 | { | ||
4737 | OTStmtStore it = new OTStmtStore(varwr, value, decompile); | ||
4738 | decompile.AddLastStmt(it); | ||
4739 | } | ||
4740 | |||
4741 | public OTStmtStore(OTOpnd varwr, OTOpnd value, OTDecompile decompile) | ||
4742 | { | ||
4743 | this.varwr = varwr; | ||
4744 | this.value = value; | ||
4745 | this.decompile = decompile; | ||
4746 | } | ||
4747 | |||
4748 | public override void CountRefs() | ||
4749 | { | ||
4750 | varwr.CountRefs(true); | ||
4751 | value.CountRefs(false); | ||
4752 | } | ||
4753 | |||
4754 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
4755 | { | ||
4756 | // strip out stores to __mainCallNo | ||
4757 | if(varwr is OTOpndLocal) | ||
4758 | { | ||
4759 | OTOpndLocal local = (OTOpndLocal)varwr; | ||
4760 | if(local.local.name.StartsWith(_mainCallNo)) | ||
4761 | { | ||
4762 | link.List.Remove(link); | ||
4763 | return true; | ||
4764 | } | ||
4765 | } | ||
4766 | |||
4767 | // strip out stores to local vars where the var is not read | ||
4768 | // but convert the value to an OTStmtVoid in case it is a call | ||
4769 | if(varwr is OTOpndLocal) | ||
4770 | { | ||
4771 | OTOpndLocal local = (OTOpndLocal)varwr; | ||
4772 | local.local.nlclreads = 0; | ||
4773 | decompile.topBlock.CountRefs(); | ||
4774 | if(local.local.nlclreads == 0) | ||
4775 | { | ||
4776 | OTStmt voidstmt = OTStmtVoid.Make(value); | ||
4777 | if(voidstmt == null) | ||
4778 | link.List.Remove(link); | ||
4779 | else | ||
4780 | link.Value = voidstmt; | ||
4781 | return true; | ||
4782 | } | ||
4783 | } | ||
4784 | |||
4785 | // strip out bla = newobj HeapTrackerList (...); | ||
4786 | if(value is OTOpndNewobj) | ||
4787 | { | ||
4788 | OTOpndNewobj valueno = (OTOpndNewobj)value; | ||
4789 | if(valueno.ctor.DeclaringType == typeof(HeapTrackerList)) | ||
4790 | { | ||
4791 | link.List.Remove(link); | ||
4792 | return true; | ||
4793 | } | ||
4794 | } | ||
4795 | |||
4796 | // strip out bla = newobj HeapTrackerObject (...); | ||
4797 | if(value is OTOpndNewobj) | ||
4798 | { | ||
4799 | OTOpndNewobj valueno = (OTOpndNewobj)value; | ||
4800 | if(valueno.ctor.DeclaringType == typeof(HeapTrackerObject)) | ||
4801 | { | ||
4802 | link.List.Remove(link); | ||
4803 | return true; | ||
4804 | } | ||
4805 | } | ||
4806 | |||
4807 | // strip out bla = newobj HeapTrackerString (...); | ||
4808 | if(value is OTOpndNewobj) | ||
4809 | { | ||
4810 | OTOpndNewobj valueno = (OTOpndNewobj)value; | ||
4811 | if(valueno.ctor.DeclaringType == typeof(HeapTrackerString)) | ||
4812 | { | ||
4813 | link.List.Remove(link); | ||
4814 | return true; | ||
4815 | } | ||
4816 | } | ||
4817 | |||
4818 | // convert tmp$n = bla bla; | ||
4819 | // .... tmp$n ....; | ||
4820 | // to | ||
4821 | // .... bla bla ....; | ||
4822 | // gets rid of vast majority of temps | ||
4823 | if(varwr is OTOpndLocal) | ||
4824 | { | ||
4825 | OTOpndLocal temp = (OTOpndLocal)varwr; | ||
4826 | if(temp.local.name.StartsWith("tmp$")) | ||
4827 | { | ||
4828 | temp.local.nlclreads = 0; | ||
4829 | temp.local.nlclwrites = 0; | ||
4830 | decompile.topBlock.CountRefs(); | ||
4831 | if((temp.local.nlclreads == 1) && (temp.local.nlclwrites == 1) && (link.Next != null)) | ||
4832 | { | ||
4833 | OTStmt nextstmt = link.Next.Value; | ||
4834 | if(!(nextstmt is OTStmtBlock)) | ||
4835 | { | ||
4836 | if(nextstmt.ReplaceOperand(varwr, value)) | ||
4837 | { | ||
4838 | link.List.Remove(link); | ||
4839 | return true; | ||
4840 | } | ||
4841 | } | ||
4842 | } | ||
4843 | |||
4844 | // also try to convert: | ||
4845 | // tmp$n = ... asdf ... << we are here (link) | ||
4846 | // lcl = tmp$n; << nextstore | ||
4847 | // ... qwer tmp$n ... | ||
4848 | // ... no further references to tmp$n | ||
4849 | // to: | ||
4850 | // lcl = ... asdf ... | ||
4851 | // ... qwer lcl ... | ||
4852 | if((temp.local.nlclreads == 2) && (temp.local.nlclwrites == 1) && | ||
4853 | (link.Next != null) && (link.Next.Value is OTStmtStore)) | ||
4854 | { | ||
4855 | OTStmtStore nextstore = (OTStmtStore)link.Next.Value; | ||
4856 | if((nextstore.varwr is OTOpndLocal) && (nextstore.value is OTOpndLocal) && (link.Next.Next != null)) | ||
4857 | { | ||
4858 | OTOpndLocal localopnd = (OTOpndLocal)nextstore.varwr; | ||
4859 | OTOpndLocal tempopnd = (OTOpndLocal)nextstore.value; | ||
4860 | if(tempopnd.local == temp.local) | ||
4861 | { | ||
4862 | OTStmt finalstmt = link.Next.Next.Value; | ||
4863 | if(finalstmt.ReplaceOperand(tempopnd, localopnd)) | ||
4864 | { | ||
4865 | nextstore.value = value; | ||
4866 | link.List.Remove(link); | ||
4867 | return true; | ||
4868 | } | ||
4869 | } | ||
4870 | } | ||
4871 | } | ||
4872 | } | ||
4873 | } | ||
4874 | |||
4875 | // convert: | ||
4876 | // dup$n = ... asdf ... << we are here | ||
4877 | // lcl = dup$n; | ||
4878 | // ... qwer dup$n ... | ||
4879 | // ... no further references to dup$n | ||
4880 | // to: | ||
4881 | // lcl = ... asdf ... | ||
4882 | // ... qwer lcl ... | ||
4883 | if((varwr is OTOpndDup) && (link != null)) | ||
4884 | { | ||
4885 | OTOpndDup vardup = (OTOpndDup)varwr; | ||
4886 | LinkedListNode<OTStmt> nextlink = link.Next; | ||
4887 | vardup.ndupreads = 0; | ||
4888 | decompile.topBlock.CountRefs(); | ||
4889 | if((vardup.ndupreads == 2) && (nextlink != null) && (nextlink.Value is OTStmtStore)) | ||
4890 | { | ||
4891 | |||
4892 | // point to the supposed lcl = dup$n statement | ||
4893 | OTStmtStore nextstore = (OTStmtStore)nextlink.Value; | ||
4894 | LinkedListNode<OTStmt> nextlink2 = nextlink.Next; | ||
4895 | if((nextstore.varwr is OTOpndLocal) && (nextstore.value == vardup) && (nextlink2 != null)) | ||
4896 | { | ||
4897 | |||
4898 | // get the local var being written and point to the ... qwer dup$n ... statement | ||
4899 | OTOpndLocal varlcl = (OTOpndLocal)nextstore.varwr; | ||
4900 | OTStmt nextstmt2 = nextlink2.Value; | ||
4901 | |||
4902 | // try to replace dup$n in qwer with lcl | ||
4903 | if(nextstmt2.ReplaceOperand(vardup, varlcl)) | ||
4904 | { | ||
4905 | |||
4906 | // successful, replace dup$n in asdf with lcl | ||
4907 | // and delete the lcl = dup$n statement | ||
4908 | varwr = varlcl; | ||
4909 | nextlink.List.Remove(nextlink); | ||
4910 | return true; | ||
4911 | } | ||
4912 | } | ||
4913 | } | ||
4914 | } | ||
4915 | |||
4916 | // convert: | ||
4917 | // dup$n = ... asdf ... << we are here | ||
4918 | // ... qwer dup$n ... | ||
4919 | // ... no further references to dup$n | ||
4920 | // to: | ||
4921 | // ... qwer ... asdf ... ... | ||
4922 | if((varwr is OTOpndDup) && (link != null)) | ||
4923 | { | ||
4924 | OTOpndDup vardup = (OTOpndDup)varwr; | ||
4925 | LinkedListNode<OTStmt> nextlink = link.Next; | ||
4926 | vardup.ndupreads = 0; | ||
4927 | decompile.topBlock.CountRefs(); | ||
4928 | if((vardup.ndupreads == 1) && (nextlink != null)) | ||
4929 | { | ||
4930 | |||
4931 | // point to the ... qwer dup$n ... statement | ||
4932 | OTStmt nextstmt = nextlink.Value; | ||
4933 | |||
4934 | // try to replace dup$n in qwer with ... asdf ... | ||
4935 | if(nextstmt.ReplaceOperand(vardup, value)) | ||
4936 | { | ||
4937 | |||
4938 | // successful, delete the dup$n = ... asdf ... statement | ||
4939 | link.List.Remove(link); | ||
4940 | return true; | ||
4941 | } | ||
4942 | } | ||
4943 | } | ||
4944 | |||
4945 | // look for list initialization [ ... ] | ||
4946 | if(OTOpndListIni.Detect(link)) | ||
4947 | return true; | ||
4948 | |||
4949 | // __xmrinst = (XMRInstAbstract) arg$0 indicates this is an event handler | ||
4950 | // so strip it out and set the flag | ||
4951 | if((varwr is OTOpndLocal) && (value is OTOpndCast)) | ||
4952 | { | ||
4953 | OTOpndLocal lcl = (OTOpndLocal)varwr; | ||
4954 | OTOpndCast cast = (OTOpndCast)value; | ||
4955 | if(lcl.local.name.StartsWith(_xmrinstlocal) && (cast.value is OTOpndArg)) | ||
4956 | { | ||
4957 | link.List.Remove(link); | ||
4958 | return true; | ||
4959 | } | ||
4960 | } | ||
4961 | |||
4962 | // local = [ (optional cast) ] __xmrinst.ehArgs[n] is a definition of event handler arg #n | ||
4963 | // if found, make it event handler arg list definition | ||
4964 | OTOpnd valuenocast = value; | ||
4965 | if(valuenocast is OTOpndCast) | ||
4966 | valuenocast = ((OTOpndCast)value).value; | ||
4967 | if((varwr is OTOpndLocal) && (valuenocast is OTOpndArrayElem)) | ||
4968 | { | ||
4969 | OTOpndArrayElem array = (OTOpndArrayElem)valuenocast; | ||
4970 | if((array.array is OTOpndField) && (array.index is OTOpndInt)) | ||
4971 | { | ||
4972 | OTOpndField arrayfield = (OTOpndField)array.array; | ||
4973 | if((arrayfield.obj is OTOpndLocal) && | ||
4974 | ((OTOpndLocal)arrayfield.obj).local.name.StartsWith(_xmrinstlocal) && | ||
4975 | (arrayfield.field.Name == _ehArgs)) | ||
4976 | { | ||
4977 | int index = ((OTOpndInt)array.index).value; | ||
4978 | decompile.eharglist[index] = ((OTOpndLocal)varwr).local; | ||
4979 | link.List.Remove(link); | ||
4980 | return true; | ||
4981 | } | ||
4982 | } | ||
4983 | } | ||
4984 | |||
4985 | // __retval$n = ...; => return ...; | ||
4986 | if(varwr is OTOpndLocal) | ||
4987 | { | ||
4988 | OTOpndLocal lcl = (OTOpndLocal)varwr; | ||
4989 | if(lcl.local.name.StartsWith(_retval)) | ||
4990 | { | ||
4991 | link.Value = new OTStmtRet(value); | ||
4992 | return true; | ||
4993 | } | ||
4994 | } | ||
4995 | |||
4996 | return false; | ||
4997 | } | ||
4998 | |||
4999 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
5000 | { | ||
5001 | bool rc = false; | ||
5002 | if(value != null) | ||
5003 | value = value.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
5004 | return rc; | ||
5005 | } | ||
5006 | |||
5007 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
5008 | { | ||
5009 | return this; | ||
5010 | } | ||
5011 | |||
5012 | public override void PrintStmt(TextWriter twout, string indent) | ||
5013 | { | ||
5014 | // print x = x + 1 as x += 1, but don't print x = x < 3 as x <= 3 | ||
5015 | if(value is OTOpndBinOp) | ||
5016 | { | ||
5017 | OTOpndBinOp valuebo = (OTOpndBinOp)value; | ||
5018 | if(varwr.SameAs(valuebo.left) && " add and div mul or rem shl shr sub xor ".Contains(' ' + valuebo.opCode.name + ' ')) | ||
5019 | { | ||
5020 | twout.Write(varwr.PrintableString + ' ' + valuebo.opCode.source + "= " + valuebo.rite.PrintableString + ';'); | ||
5021 | return; | ||
5022 | } | ||
5023 | } | ||
5024 | |||
5025 | twout.Write(varwr.PrintableString + " = " + value.PrintableString + ';'); | ||
5026 | } | ||
5027 | } | ||
5028 | |||
5029 | /** | ||
5030 | * Dispatch to a table of labels. | ||
5031 | */ | ||
5032 | private class OTStmtSwitch: OTStmt | ||
5033 | { | ||
5034 | private OTOpnd index; | ||
5035 | private OTLabel[] labels; | ||
5036 | |||
5037 | public OTStmtSwitch(OTOpnd index, OTLabel[] labels) | ||
5038 | { | ||
5039 | this.index = index; | ||
5040 | this.labels = labels; | ||
5041 | } | ||
5042 | |||
5043 | public override void CountRefs() | ||
5044 | { | ||
5045 | index.CountRefs(false); | ||
5046 | foreach(OTLabel label in labels) | ||
5047 | { | ||
5048 | label.lbljumps++; | ||
5049 | } | ||
5050 | } | ||
5051 | |||
5052 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
5053 | { | ||
5054 | bool rc = false; | ||
5055 | if(index != null) | ||
5056 | index = index.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
5057 | return rc; | ||
5058 | } | ||
5059 | |||
5060 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
5061 | { | ||
5062 | return this; | ||
5063 | } | ||
5064 | |||
5065 | public override void PrintStmt(TextWriter twout, string indent) | ||
5066 | { | ||
5067 | twout.Write("switch (" + index.PrintableString + ") {\n"); | ||
5068 | for(int i = 0; i < labels.Length; i++) | ||
5069 | { | ||
5070 | twout.Write(indent + INDENT + "case " + i + ": jump " + labels[i].name + ";\n"); | ||
5071 | } | ||
5072 | twout.Write(indent + '}'); | ||
5073 | } | ||
5074 | } | ||
5075 | |||
5076 | /** | ||
5077 | * Throw an exception. | ||
5078 | */ | ||
5079 | private class OTStmtThrow: OTStmt | ||
5080 | { | ||
5081 | private OTOpnd value; | ||
5082 | private OTDecompile decompile; | ||
5083 | |||
5084 | public OTStmtThrow(OTOpnd value, OTDecompile decompile) | ||
5085 | { | ||
5086 | this.value = value; | ||
5087 | this.decompile = decompile; | ||
5088 | } | ||
5089 | |||
5090 | public override void CountRefs() | ||
5091 | { | ||
5092 | value.CountRefs(false); | ||
5093 | } | ||
5094 | |||
5095 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
5096 | { | ||
5097 | return StripStuffForTerminal(link); | ||
5098 | } | ||
5099 | |||
5100 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
5101 | { | ||
5102 | bool rc = false; | ||
5103 | if(value != null) | ||
5104 | value = value.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
5105 | return rc; | ||
5106 | } | ||
5107 | |||
5108 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
5109 | { | ||
5110 | return this; | ||
5111 | } | ||
5112 | |||
5113 | public override void PrintStmt(TextWriter twout, string indent) | ||
5114 | { | ||
5115 | // throw newobj ScriptUndefinedStateException ("x") => state x | ||
5116 | if(value is OTOpndNewobj) | ||
5117 | { | ||
5118 | OTOpndNewobj valueno = (OTOpndNewobj)value; | ||
5119 | if((valueno.ctor.DeclaringType == typeof(ScriptUndefinedStateException)) && | ||
5120 | (valueno.args.Length == 1) && (valueno.args[0] is OTOpndString)) | ||
5121 | { | ||
5122 | OTOpndString arg0 = (OTOpndString)valueno.args[0]; | ||
5123 | twout.Write("state " + arg0.value + "; /* throws undefined state exception */"); | ||
5124 | return; | ||
5125 | } | ||
5126 | } | ||
5127 | |||
5128 | // throw newobj ScriptChangeStateException (n) => state n | ||
5129 | if(value is OTOpndNewobj) | ||
5130 | { | ||
5131 | OTOpndNewobj valueno = (OTOpndNewobj)value; | ||
5132 | if((valueno.ctor.DeclaringType == typeof(ScriptChangeStateException)) && | ||
5133 | (valueno.args.Length == 1) && (valueno.args[0] is OTOpndInt)) | ||
5134 | { | ||
5135 | OTOpndInt arg0 = (OTOpndInt)valueno.args[0]; | ||
5136 | twout.Write("state " + decompile.scriptObjCode.stateNames[arg0.value] + ';'); | ||
5137 | return; | ||
5138 | } | ||
5139 | } | ||
5140 | |||
5141 | // throwing something else, output as is | ||
5142 | twout.Write("throw " + value.PrintableString + ';'); | ||
5143 | } | ||
5144 | } | ||
5145 | |||
5146 | /** | ||
5147 | * Call with void return, or really anything that we discard the value of after computing it. | ||
5148 | */ | ||
5149 | private class OTStmtVoid: OTStmt | ||
5150 | { | ||
5151 | private OTOpnd value; | ||
5152 | |||
5153 | public static void AddLast(OTDecompile decompile, OTOpnd value) | ||
5154 | { | ||
5155 | OTStmt it = OTStmtVoid.Make(value); | ||
5156 | if(it != null) | ||
5157 | decompile.AddLastStmt(it); | ||
5158 | } | ||
5159 | |||
5160 | public static OTStmt Make(OTOpnd value) | ||
5161 | { | ||
5162 | if(!value.HasSideEffects) | ||
5163 | return null; | ||
5164 | OTStmtVoid it = new OTStmtVoid(); | ||
5165 | it.value = value; | ||
5166 | return it; | ||
5167 | } | ||
5168 | |||
5169 | private OTStmtVoid() | ||
5170 | { | ||
5171 | } | ||
5172 | |||
5173 | public override void CountRefs() | ||
5174 | { | ||
5175 | value.CountRefs(false); | ||
5176 | } | ||
5177 | |||
5178 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
5179 | { | ||
5180 | bool rc = false; | ||
5181 | value = value.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
5182 | return rc; | ||
5183 | } | ||
5184 | |||
5185 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
5186 | { | ||
5187 | // strip out calls to CheckRunQuick() and CheckRunStack() | ||
5188 | if(value is OTOpndCall) | ||
5189 | { | ||
5190 | OTOpndCall call = (OTOpndCall)value; | ||
5191 | MethodInfo method = call.method; | ||
5192 | if((method.Name == _checkRunQuick) || (method.Name == _checkRunStack)) | ||
5193 | { | ||
5194 | link.List.Remove(link); | ||
5195 | return true; | ||
5196 | } | ||
5197 | } | ||
5198 | |||
5199 | return false; | ||
5200 | } | ||
5201 | |||
5202 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
5203 | { | ||
5204 | return this; | ||
5205 | } | ||
5206 | |||
5207 | public override void PrintStmt(TextWriter twout, string indent) | ||
5208 | { | ||
5209 | twout.Write(value.PrintableString + ';'); | ||
5210 | } | ||
5211 | } | ||
5212 | |||
5213 | /***************************\ | ||
5214 | * Structured statements * | ||
5215 | \***************************/ | ||
5216 | |||
5217 | /** | ||
5218 | * Block of statements. | ||
5219 | */ | ||
5220 | private class OTStmtBlock: OTStmt | ||
5221 | { | ||
5222 | public LinkedList<OTStmt> blkstmts = new LinkedList<OTStmt>(); | ||
5223 | |||
5224 | public override void CountRefs() | ||
5225 | { | ||
5226 | foreach(OTStmt stmt in blkstmts) | ||
5227 | { | ||
5228 | stmt.CountRefs(); | ||
5229 | } | ||
5230 | } | ||
5231 | |||
5232 | /** | ||
5233 | * Scrub out all references to behind-the-scenes parts and simplify. | ||
5234 | */ | ||
5235 | public override bool StripStuff(LinkedListNode<OTStmt> link) | ||
5236 | { | ||
5237 | // loop through all sub-statements to strip out behind-the-scenes references | ||
5238 | bool rc = false; | ||
5239 | loop: | ||
5240 | for(LinkedListNode<OTStmt> stmtlink = blkstmts.First; stmtlink != null; stmtlink = stmtlink.Next) | ||
5241 | { | ||
5242 | if(stmtlink.Value.StripStuff(stmtlink)) | ||
5243 | { | ||
5244 | rc = true; | ||
5245 | goto loop; | ||
5246 | } | ||
5247 | } | ||
5248 | if(rc) | ||
5249 | return true; | ||
5250 | |||
5251 | // try to merge this block into outer block | ||
5252 | // change: | ||
5253 | // { | ||
5254 | // ... | ||
5255 | // { << link points here | ||
5256 | // ... | ||
5257 | // } | ||
5258 | // ... | ||
5259 | // } | ||
5260 | // to: | ||
5261 | // { | ||
5262 | // ... | ||
5263 | // ... | ||
5264 | // ... | ||
5265 | // } | ||
5266 | if(link != null) | ||
5267 | { | ||
5268 | LinkedListNode<OTStmt> nextlink; | ||
5269 | while((nextlink = blkstmts.Last) != null) | ||
5270 | { | ||
5271 | nextlink.List.Remove(nextlink); | ||
5272 | link.List.AddAfter(link, nextlink); | ||
5273 | } | ||
5274 | link.List.Remove(link); | ||
5275 | return true; | ||
5276 | } | ||
5277 | |||
5278 | return rc; | ||
5279 | } | ||
5280 | |||
5281 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
5282 | { | ||
5283 | bool rc = false; | ||
5284 | foreach(OTStmt stmt in blkstmts) | ||
5285 | { | ||
5286 | rc |= stmt.ReplaceOperand(oldopnd, newopnd); | ||
5287 | } | ||
5288 | return rc; | ||
5289 | } | ||
5290 | |||
5291 | /** | ||
5292 | * Check each statement in the block to see if it starts a do/for/if/while statement. | ||
5293 | */ | ||
5294 | public override bool DetectDoForIfWhile(LinkedListNode<OTStmt> link) | ||
5295 | { | ||
5296 | bool rc = false; | ||
5297 | loop: | ||
5298 | for(link = blkstmts.First; link != null; link = link.Next) | ||
5299 | { | ||
5300 | if(link.Value.DetectDoForIfWhile(link)) | ||
5301 | { | ||
5302 | rc = true; | ||
5303 | goto loop; | ||
5304 | } | ||
5305 | } | ||
5306 | return rc; | ||
5307 | } | ||
5308 | |||
5309 | /** | ||
5310 | * Assume we will never try to replace the block itself. | ||
5311 | * But go through all our sub-ordinates statements. | ||
5312 | */ | ||
5313 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
5314 | { | ||
5315 | for(LinkedListNode<OTStmt> childlink = blkstmts.First; childlink != null; childlink = childlink.Next) | ||
5316 | { | ||
5317 | childlink.Value = childlink.Value.ReplaceStatement(oldstmt, newstmt); | ||
5318 | } | ||
5319 | return this; | ||
5320 | } | ||
5321 | |||
5322 | /** | ||
5323 | * Print out the block including its enclosed statements. | ||
5324 | */ | ||
5325 | public override void PrintStmt(TextWriter twout, string indent) | ||
5326 | { | ||
5327 | switch(blkstmts.Count) | ||
5328 | { | ||
5329 | case 0: | ||
5330 | { | ||
5331 | twout.Write("{ }"); | ||
5332 | break; | ||
5333 | } | ||
5334 | ////case 1: { | ||
5335 | //// blkstmts.First.Value.PrintStmt (twout, indent); | ||
5336 | //// break; | ||
5337 | ////} | ||
5338 | default: | ||
5339 | { | ||
5340 | twout.Write('{'); | ||
5341 | PrintBodyAndEnd(twout, indent); | ||
5342 | break; | ||
5343 | } | ||
5344 | } | ||
5345 | } | ||
5346 | |||
5347 | public void PrintBodyAndEnd(TextWriter twout, string indent) | ||
5348 | { | ||
5349 | string newindent = indent + INDENT; | ||
5350 | foreach(OTStmt stmt in blkstmts) | ||
5351 | { | ||
5352 | twout.Write('\n' + indent); | ||
5353 | if(!(stmt is OTStmtLabel)) | ||
5354 | twout.Write(INDENT); | ||
5355 | else | ||
5356 | twout.Write(LABELINDENT); | ||
5357 | stmt.PrintStmt(twout, newindent); | ||
5358 | } | ||
5359 | twout.Write('\n' + indent + '}'); | ||
5360 | } | ||
5361 | } | ||
5362 | |||
5363 | /** | ||
5364 | * 'do' statement. | ||
5365 | */ | ||
5366 | private class OTStmtDo: OTStmt | ||
5367 | { | ||
5368 | private OTOpnd dotest; | ||
5369 | private OTStmtBlock dobody; | ||
5370 | |||
5371 | /** | ||
5372 | * See if we have a do loop... | ||
5373 | * @doloop_<suffix>; << link points here | ||
5374 | * ... <dobody> ... | ||
5375 | * [ if (dotest) ] jump doloop_<suffix>; | ||
5376 | */ | ||
5377 | public static bool Detect(LinkedListNode<OTStmt> link) | ||
5378 | { | ||
5379 | // see if we have label starting with 'doloop_' | ||
5380 | OTLabel looplabel = ((OTStmtLabel)link.Value).label; | ||
5381 | if(!looplabel.name.StartsWith(_doLoop)) | ||
5382 | return false; | ||
5383 | |||
5384 | // good chance we have a do loop | ||
5385 | OTStmtDo it = new OTStmtDo(); | ||
5386 | |||
5387 | // scan ahead looking for the terminating cond/jump loop | ||
5388 | // also gather up the statements for the do body block | ||
5389 | it.dobody = new OTStmtBlock(); | ||
5390 | LinkedListNode<OTStmt> nextlink; | ||
5391 | for(nextlink = link.Next; nextlink != null; nextlink = nextlink.Next) | ||
5392 | { | ||
5393 | OTStmt nextstmt = nextlink.Value; | ||
5394 | |||
5395 | // add statement to do body | ||
5396 | it.dobody.blkstmts.AddLast(nextlink.Value); | ||
5397 | |||
5398 | // check for something what jumps to loop label | ||
5399 | // that gives us the end of the loop | ||
5400 | OTStmt maybejump = nextstmt; | ||
5401 | if(nextstmt is OTStmtCond) | ||
5402 | { | ||
5403 | maybejump = ((OTStmtCond)nextstmt).stmt; | ||
5404 | } | ||
5405 | if((maybejump is OTStmtJump) && (((OTStmtJump)maybejump).label == looplabel)) | ||
5406 | { | ||
5407 | break; | ||
5408 | } | ||
5409 | } | ||
5410 | |||
5411 | // make sure we found the jump back to the loop label | ||
5412 | if(nextlink == null) | ||
5413 | return false; | ||
5414 | |||
5415 | // remove all statements from caller's block including the continue label if any | ||
5416 | // but leave the break label alone it will be removed later if unreferenced | ||
5417 | // and leave the initial loop label intact for now | ||
5418 | for(LinkedListNode<OTStmt> remlink = null; (remlink = link.Next) != null;) | ||
5419 | { | ||
5420 | link.List.Remove(remlink); | ||
5421 | if(remlink == nextlink) | ||
5422 | break; | ||
5423 | } | ||
5424 | |||
5425 | // take test condition from last statement of body | ||
5426 | // it should be an cond/jump or just a jump to the loop label | ||
5427 | LinkedListNode<OTStmt> lastlink = it.dobody.blkstmts.Last; | ||
5428 | OTStmt laststmt = lastlink.Value; | ||
5429 | if(laststmt is OTStmtCond) | ||
5430 | { | ||
5431 | it.dotest = ((OTStmtCond)laststmt).valu; | ||
5432 | } | ||
5433 | else | ||
5434 | { | ||
5435 | it.dotest = new OTOpndInt(1); | ||
5436 | } | ||
5437 | lastlink.List.Remove(lastlink); | ||
5438 | |||
5439 | // finally replace the loop label with the whole do statement | ||
5440 | link.Value = it; | ||
5441 | |||
5442 | // tell caller we made a change | ||
5443 | return true; | ||
5444 | } | ||
5445 | |||
5446 | public override void CountRefs() | ||
5447 | { | ||
5448 | if(dotest != null) | ||
5449 | dotest.CountRefs(false); | ||
5450 | if(dobody != null) | ||
5451 | dobody.CountRefs(); | ||
5452 | } | ||
5453 | |||
5454 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
5455 | { | ||
5456 | return dobody.ReplaceOperand(oldopnd, newopnd); | ||
5457 | } | ||
5458 | |||
5459 | public override bool DetectDoForIfWhile(LinkedListNode<OTStmt> link) | ||
5460 | { | ||
5461 | return dobody.DetectDoForIfWhile(link); | ||
5462 | } | ||
5463 | |||
5464 | /** | ||
5465 | * Assume we won't replace the do statement itself. | ||
5466 | * But search all our sub-ordinate statements. | ||
5467 | */ | ||
5468 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
5469 | { | ||
5470 | dobody = (OTStmtBlock)dobody.ReplaceStatement(oldstmt, newstmt); | ||
5471 | return this; | ||
5472 | } | ||
5473 | |||
5474 | public override void PrintStmt(TextWriter twout, string indent) | ||
5475 | { | ||
5476 | // output do body | ||
5477 | twout.Write("do "); | ||
5478 | dobody.PrintStmt(twout, indent); | ||
5479 | |||
5480 | // output while part | ||
5481 | twout.Write(" while (" + StripBrtrue(dotest).PrintableString + ");"); | ||
5482 | } | ||
5483 | } | ||
5484 | |||
5485 | /** | ||
5486 | * 'for' or 'while' statement. | ||
5487 | */ | ||
5488 | private class OTStmtFor: OTStmt | ||
5489 | { | ||
5490 | private bool iswhile; | ||
5491 | private OTOpnd fortest; | ||
5492 | private OTStmtBlock forbody; | ||
5493 | private OTStmt forinit; | ||
5494 | private OTStmt forstep; | ||
5495 | |||
5496 | /** | ||
5497 | * See if we have a for or while loop... | ||
5498 | * <forinit> | ||
5499 | * @forloop_<suffix>; << link points here | ||
5500 | * [ if (<fortest>) jump forbreak_<suffix>; ] | ||
5501 | * ... <forbody> ... | ||
5502 | * jump forloop_<suffix>; | ||
5503 | * [ @forbreak_<suffix>; ] | ||
5504 | */ | ||
5505 | public static bool Detect(LinkedListNode<OTStmt> link, bool iswhile) | ||
5506 | { | ||
5507 | string loopname = iswhile ? _whileLoop : _forLoop; | ||
5508 | string breakname = iswhile ? _whileBreak : _forBreak; | ||
5509 | |||
5510 | // see if we have label starting with 'forloop_' | ||
5511 | OTLabel looplabel = ((OTStmtLabel)link.Value).label; | ||
5512 | if(!looplabel.name.StartsWith(loopname)) | ||
5513 | return false; | ||
5514 | |||
5515 | // good chance we have a for loop | ||
5516 | OTStmtFor it = new OTStmtFor(); | ||
5517 | it.iswhile = iswhile; | ||
5518 | |||
5519 | // all labels end with this suffix | ||
5520 | string suffix = looplabel.name.Substring(loopname.Length); | ||
5521 | |||
5522 | // scan ahead looking for the 'jump forloop_<suffix>;' statement | ||
5523 | // also gather up the statements for the for body block | ||
5524 | it.forbody = new OTStmtBlock(); | ||
5525 | LinkedListNode<OTStmt> lastlink; | ||
5526 | for(lastlink = link; (lastlink = lastlink.Next) != null;) | ||
5527 | { | ||
5528 | |||
5529 | // check for jump forloop that tells us where loop ends | ||
5530 | if(lastlink.Value is OTStmtJump) | ||
5531 | { | ||
5532 | OTStmtJump lastjump = (OTStmtJump)lastlink.Value; | ||
5533 | if(lastjump.label == looplabel) | ||
5534 | break; | ||
5535 | } | ||
5536 | |||
5537 | // add to body block | ||
5538 | it.forbody.blkstmts.AddLast(lastlink.Value); | ||
5539 | } | ||
5540 | |||
5541 | // make sure we found the 'jump forloop' where the for loop ends | ||
5542 | if(lastlink == null) | ||
5543 | return false; | ||
5544 | |||
5545 | // remove all statements from caller's block including final jump | ||
5546 | // but leave the loop label in place | ||
5547 | for(LinkedListNode<OTStmt> nextlink = null; (nextlink = link.Next) != null;) | ||
5548 | { | ||
5549 | link.List.Remove(nextlink); | ||
5550 | if(nextlink == lastlink) | ||
5551 | break; | ||
5552 | } | ||
5553 | |||
5554 | // if statement before loop label is an assignment, use it for the init statement | ||
5555 | if(!iswhile && (link.Previous != null) && (link.Previous.Value is OTStmtStore)) | ||
5556 | { | ||
5557 | it.forinit = link.Previous.Value; | ||
5558 | link.List.Remove(link.Previous); | ||
5559 | } | ||
5560 | |||
5561 | // if first statement of for body is 'if (...) jump breaklabel' use it for the test value | ||
5562 | if((it.forbody.blkstmts.First != null) && (it.forbody.blkstmts.First.Value is OTStmtCond)) | ||
5563 | { | ||
5564 | OTStmtCond condstmt = (OTStmtCond)it.forbody.blkstmts.First.Value; | ||
5565 | if((condstmt.stmt is OTStmtJump) && (((OTStmtJump)condstmt.stmt).label.name == breakname + suffix)) | ||
5566 | { | ||
5567 | it.fortest = OTOpndUnOp.Make(MyOp.Brfalse, condstmt.valu); | ||
5568 | it.forbody.blkstmts.RemoveFirst(); | ||
5569 | } | ||
5570 | } | ||
5571 | |||
5572 | // if last statement of body is an assigment, | ||
5573 | // use the assignment as the step statement | ||
5574 | if(!iswhile && (it.forbody.blkstmts.Last != null) && | ||
5575 | (it.forbody.blkstmts.Last.Value is OTStmtStore)) | ||
5576 | { | ||
5577 | LinkedListNode<OTStmt> storelink = it.forbody.blkstmts.Last; | ||
5578 | storelink.List.Remove(storelink); | ||
5579 | it.forstep = storelink.Value; | ||
5580 | } | ||
5581 | |||
5582 | // finally replace the loop label with the whole for statement | ||
5583 | link.Value = it; | ||
5584 | |||
5585 | // tell caller we made a change | ||
5586 | return true; | ||
5587 | } | ||
5588 | |||
5589 | public override void CountRefs() | ||
5590 | { | ||
5591 | if(fortest != null) | ||
5592 | fortest.CountRefs(false); | ||
5593 | if(forbody != null) | ||
5594 | forbody.CountRefs(); | ||
5595 | if(forinit != null) | ||
5596 | forinit.CountRefs(); | ||
5597 | if(forstep != null) | ||
5598 | forstep.CountRefs(); | ||
5599 | } | ||
5600 | |||
5601 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
5602 | { | ||
5603 | return forbody.ReplaceOperand(oldopnd, newopnd) | | ||
5604 | ((forinit != null) && forinit.ReplaceOperand(oldopnd, newopnd)) | | ||
5605 | ((forstep != null) && forstep.ReplaceOperand(oldopnd, newopnd)); | ||
5606 | } | ||
5607 | |||
5608 | public override bool DetectDoForIfWhile(LinkedListNode<OTStmt> link) | ||
5609 | { | ||
5610 | return forbody.DetectDoForIfWhile(link) | | ||
5611 | ((forinit != null) && forinit.DetectDoForIfWhile(link)) | | ||
5612 | ((forstep != null) && forstep.DetectDoForIfWhile(link)); | ||
5613 | } | ||
5614 | |||
5615 | /** | ||
5616 | * Assume we won't replace the for statement itself. | ||
5617 | * But search all our sub-ordinate statements. | ||
5618 | */ | ||
5619 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
5620 | { | ||
5621 | forbody = (OTStmtBlock)forbody.ReplaceStatement(oldstmt, newstmt); | ||
5622 | if(forinit != null) | ||
5623 | forinit = forinit.ReplaceStatement(oldstmt, newstmt); | ||
5624 | if(forstep != null) | ||
5625 | forstep = forstep.ReplaceStatement(oldstmt, newstmt); | ||
5626 | return this; | ||
5627 | } | ||
5628 | |||
5629 | public override void PrintStmt(TextWriter twout, string indent) | ||
5630 | { | ||
5631 | if(iswhile) | ||
5632 | { | ||
5633 | twout.Write("while ("); | ||
5634 | if(fortest == null) | ||
5635 | { | ||
5636 | twout.Write("TRUE"); | ||
5637 | } | ||
5638 | else | ||
5639 | { | ||
5640 | twout.Write(StripBrtrue(fortest).PrintableString); | ||
5641 | } | ||
5642 | } | ||
5643 | else | ||
5644 | { | ||
5645 | twout.Write("for ("); | ||
5646 | if(forinit != null) | ||
5647 | { | ||
5648 | forinit.PrintStmt(twout, indent + INDENT); | ||
5649 | } | ||
5650 | else | ||
5651 | { | ||
5652 | twout.Write(';'); | ||
5653 | } | ||
5654 | if(fortest != null) | ||
5655 | { | ||
5656 | twout.Write(' ' + StripBrtrue(fortest).PrintableString); | ||
5657 | } | ||
5658 | twout.Write(';'); | ||
5659 | if(forstep != null) | ||
5660 | { | ||
5661 | StringWriter sw = new StringWriter(); | ||
5662 | sw.Write(' '); | ||
5663 | forstep.PrintStmt(sw, indent + INDENT); | ||
5664 | StringBuilder sb = sw.GetStringBuilder(); | ||
5665 | int sl = sb.Length; | ||
5666 | if((sl > 0) && (sb[sl - 1] == ';')) | ||
5667 | sb.Remove(--sl, 1); | ||
5668 | twout.Write(sb.ToString()); | ||
5669 | } | ||
5670 | } | ||
5671 | |||
5672 | twout.Write(") "); | ||
5673 | forbody.PrintStmt(twout, indent); | ||
5674 | } | ||
5675 | } | ||
5676 | |||
5677 | /** | ||
5678 | * if/then/else block. | ||
5679 | */ | ||
5680 | private class OTStmtIf: OTStmt | ||
5681 | { | ||
5682 | private OTOpnd testvalu; | ||
5683 | private OTStmt thenstmt; | ||
5684 | private OTStmt elsestmt; // might be null | ||
5685 | |||
5686 | /** | ||
5687 | * Try to detect a structured if statement. | ||
5688 | * | ||
5689 | * if (condition) jump ifdone_<suffix>; << link points here | ||
5690 | * ... then body ... | ||
5691 | * @ifdone_<suffix>; | ||
5692 | * | ||
5693 | * if (condition) jump ifelse_<suffix>; | ||
5694 | * ... then body ... | ||
5695 | * jump ifdone_<suffix>; << optional if true body doesn't fall through | ||
5696 | * @ifelse_<suffix>; | ||
5697 | * ... else body ... | ||
5698 | * @ifdone_<suffix>; | ||
5699 | */ | ||
5700 | public static bool Detect(LinkedListNode<OTStmt> link) | ||
5701 | { | ||
5702 | OTStmtCond condstmt = (OTStmtCond)link.Value; | ||
5703 | if(!(condstmt.stmt is OTStmtJump)) | ||
5704 | return false; | ||
5705 | |||
5706 | OTStmtJump jumpstmt = (OTStmtJump)condstmt.stmt; | ||
5707 | if(jumpstmt.label.name.StartsWith(_ifDone)) | ||
5708 | { | ||
5709 | |||
5710 | // then-only if | ||
5711 | |||
5712 | // skip forward to find the ifdone_<suffix> label | ||
5713 | // also save the intervening statements for the then body | ||
5714 | OTStmtBlock thenbody; | ||
5715 | LinkedListNode<OTStmt> donelink = ScanForLabel(link, jumpstmt.label, out thenbody); | ||
5716 | |||
5717 | // make sure we found matching label | ||
5718 | if(donelink == null) | ||
5719 | return false; | ||
5720 | |||
5721 | // replace the jump ifdone_<suffix> with the <then body> | ||
5722 | OTStmtIf it = new OTStmtIf(); | ||
5723 | it.thenstmt = thenbody; | ||
5724 | |||
5725 | // replace the test value with the opposite | ||
5726 | it.testvalu = OTOpndUnOp.Make(MyOp.Brfalse, condstmt.valu); | ||
5727 | condstmt.valu = null; | ||
5728 | |||
5729 | // strip out the true body statements from the main code including the ifdone_<suffix> label | ||
5730 | StripInterveningStatements(link, donelink); | ||
5731 | |||
5732 | // replace the simple conditional with the if/then/else block | ||
5733 | link.Value = it; | ||
5734 | |||
5735 | // tell caller we changed something | ||
5736 | return true; | ||
5737 | } | ||
5738 | |||
5739 | if(jumpstmt.label.name.StartsWith(_ifElse)) | ||
5740 | { | ||
5741 | string suffix = jumpstmt.label.name.Substring(_ifElse.Length); | ||
5742 | |||
5743 | // if/then/else | ||
5744 | OTStmtIf it = new OTStmtIf(); | ||
5745 | |||
5746 | // skip forward to find the ifelse_<suffix> label | ||
5747 | // also save the intervening statements for the true body | ||
5748 | OTStmtBlock thenbody; | ||
5749 | LinkedListNode<OTStmt> elselink = ScanForLabel(link, jumpstmt.label, out thenbody); | ||
5750 | |||
5751 | // make sure we found matching label | ||
5752 | if(elselink != null) | ||
5753 | { | ||
5754 | |||
5755 | // the last statement of the then body might be a jump ifdone_<suffix> | ||
5756 | LinkedListNode<OTStmt> lastthenlink = thenbody.blkstmts.Last; | ||
5757 | if((lastthenlink != null) && (lastthenlink.Value is OTStmtJump)) | ||
5758 | { | ||
5759 | OTStmtJump jumpifdone = (OTStmtJump)lastthenlink.Value; | ||
5760 | if(jumpifdone.label.name == _ifDone + suffix) | ||
5761 | { | ||
5762 | |||
5763 | lastthenlink.List.Remove(lastthenlink); | ||
5764 | |||
5765 | // skip forward to find the ifdone_<suffix> label | ||
5766 | // also save the intervening statements for the else body | ||
5767 | OTStmtBlock elsebody; | ||
5768 | LinkedListNode<OTStmt> donelink = ScanForLabel(elselink, jumpifdone.label, out elsebody); | ||
5769 | if(donelink != null) | ||
5770 | { | ||
5771 | |||
5772 | // replace the jump ifdone_<suffix> with the <true body> | ||
5773 | it.thenstmt = thenbody; | ||
5774 | |||
5775 | // save the else body as well | ||
5776 | it.elsestmt = elsebody; | ||
5777 | |||
5778 | // replace the test value with the opposite | ||
5779 | it.testvalu = OTOpndUnOp.Make(MyOp.Brfalse, condstmt.valu); | ||
5780 | condstmt.valu = null; | ||
5781 | |||
5782 | // strip out the true and else body statements from the main code including the ifdone_<suffix> label | ||
5783 | StripInterveningStatements(link, donelink); | ||
5784 | |||
5785 | // replace the simple conditional with the if/then/else block | ||
5786 | link.Value = it; | ||
5787 | |||
5788 | // tell caller we changed something | ||
5789 | return true; | ||
5790 | } | ||
5791 | } | ||
5792 | } | ||
5793 | |||
5794 | // missing the jump _ifDone_<suffix>, so make it a simple if/then | ||
5795 | // if (condition) jump ifelse_<suffix>; << link | ||
5796 | // ... then body ... << encapsulated in block thenbody | ||
5797 | // @ifelse_<suffix>; << elselink | ||
5798 | // ... else body ... << still inline and leave it there | ||
5799 | // @ifdone_<suffix>; << strip this out | ||
5800 | |||
5801 | // replace the jump ifelse_<suffix> with the <true body> | ||
5802 | it.thenstmt = thenbody; | ||
5803 | |||
5804 | // replace the test value with the opposite | ||
5805 | it.testvalu = OTOpndUnOp.Make(MyOp.Brfalse, condstmt.valu); | ||
5806 | condstmt.valu = null; | ||
5807 | |||
5808 | // strip out the then body statements from the main code including the ifelse_<suffix> label | ||
5809 | StripInterveningStatements(link, elselink); | ||
5810 | |||
5811 | // there's a dangling unused ifdone_<suffix> label ahead that has to be stripped | ||
5812 | for(LinkedListNode<OTStmt> donelink = link; (donelink = donelink.Next) != null;) | ||
5813 | { | ||
5814 | if((donelink.Value is OTStmtLabel) && (((OTStmtLabel)donelink.Value).label.name == _ifDone + suffix)) | ||
5815 | { | ||
5816 | donelink.List.Remove(donelink); | ||
5817 | break; | ||
5818 | } | ||
5819 | } | ||
5820 | |||
5821 | // replace the simple conditional with the if/then/else block | ||
5822 | link.Value = it; | ||
5823 | |||
5824 | // tell caller we changed something | ||
5825 | return true; | ||
5826 | } | ||
5827 | } | ||
5828 | |||
5829 | return false; | ||
5830 | } | ||
5831 | |||
5832 | private OTStmtIf() | ||
5833 | { | ||
5834 | } | ||
5835 | |||
5836 | public override void CountRefs() | ||
5837 | { | ||
5838 | if(testvalu != null) | ||
5839 | testvalu.CountRefs(false); | ||
5840 | if(thenstmt != null) | ||
5841 | thenstmt.CountRefs(); | ||
5842 | if(elsestmt != null) | ||
5843 | elsestmt.CountRefs(); | ||
5844 | } | ||
5845 | |||
5846 | public override bool ReplaceOperand(OTOpnd oldopnd, OTOpnd newopnd) | ||
5847 | { | ||
5848 | bool rc = thenstmt.ReplaceOperand(oldopnd, newopnd); | ||
5849 | testvalu = testvalu.ReplaceOperand(oldopnd, newopnd, ref rc); | ||
5850 | return rc; | ||
5851 | } | ||
5852 | |||
5853 | public override bool DetectDoForIfWhile(LinkedListNode<OTStmt> link) | ||
5854 | { | ||
5855 | return ((thenstmt != null) && thenstmt.DetectDoForIfWhile(link)) | | ||
5856 | ((elsestmt != null) && elsestmt.DetectDoForIfWhile(link)); | ||
5857 | } | ||
5858 | |||
5859 | /** | ||
5860 | * Assume we won't replace the if statement itself. | ||
5861 | * But search all our sub-ordinate statements. | ||
5862 | */ | ||
5863 | public override OTStmt ReplaceStatement(OTStmt oldstmt, OTStmt newstmt) | ||
5864 | { | ||
5865 | thenstmt = thenstmt.ReplaceStatement(oldstmt, newstmt); | ||
5866 | if(elsestmt != null) | ||
5867 | elsestmt = elsestmt.ReplaceStatement(oldstmt, newstmt); | ||
5868 | return this; | ||
5869 | } | ||
5870 | |||
5871 | public override void PrintStmt(TextWriter twout, string indent) | ||
5872 | { | ||
5873 | twout.Write("if (" + StripBrtrue(testvalu).PrintableString + ") "); | ||
5874 | OTStmt thenst = ReduceStmtBody(thenstmt, false); | ||
5875 | thenst.PrintStmt(twout, indent); | ||
5876 | if(elsestmt != null) | ||
5877 | { | ||
5878 | twout.Write('\n' + indent + "else "); | ||
5879 | OTStmt elsest = ReduceStmtBody(elsestmt, true); | ||
5880 | elsest.PrintStmt(twout, indent); | ||
5881 | } | ||
5882 | } | ||
5883 | |||
5884 | // strip block off a single jump so it prints inline instead of with braces around it | ||
5885 | // also, if this is part of else, strip block for ifs to make else if statement | ||
5886 | private static OTStmt ReduceStmtBody(OTStmt statement, bool stripif) | ||
5887 | { | ||
5888 | OTStmt onestmt = statement; | ||
5889 | if((onestmt is OTStmtBlock) && (((OTStmtBlock)onestmt).blkstmts.Count == 1)) | ||
5890 | { | ||
5891 | onestmt = ((OTStmtBlock)onestmt).blkstmts.First.Value; | ||
5892 | if((onestmt is OTStmtJump) || (stripif && (onestmt is OTStmtIf))) | ||
5893 | { | ||
5894 | return onestmt; | ||
5895 | } | ||
5896 | } | ||
5897 | return statement; | ||
5898 | } | ||
5899 | |||
5900 | /** | ||
5901 | * Scan forward for a given label definition. | ||
5902 | * Put intervening statements in a statement block. | ||
5903 | * @param link = start scanning after this statement | ||
5904 | * @param label = look for this label definition | ||
5905 | * @param block = where to return intervening statement block | ||
5906 | * @returns null: label definition not found | ||
5907 | * else: label definition statement | ||
5908 | */ | ||
5909 | private static LinkedListNode<OTStmt> ScanForLabel(LinkedListNode<OTStmt> link, | ||
5910 | OTLabel label, out OTStmtBlock block) | ||
5911 | { | ||
5912 | block = new OTStmtBlock(); | ||
5913 | while((link = link.Next) != null) | ||
5914 | { | ||
5915 | if(link.Value is OTStmtLabel) | ||
5916 | { | ||
5917 | if(((OTStmtLabel)link.Value).label == label) | ||
5918 | break; | ||
5919 | } | ||
5920 | block.blkstmts.AddLast(link.Value); | ||
5921 | } | ||
5922 | return link; | ||
5923 | } | ||
5924 | |||
5925 | /** | ||
5926 | * Strip statements after link up to and including donelink. | ||
5927 | */ | ||
5928 | private static void StripInterveningStatements(LinkedListNode<OTStmt> link, LinkedListNode<OTStmt> donelink) | ||
5929 | { | ||
5930 | LinkedListNode<OTStmt> striplink; | ||
5931 | do | ||
5932 | { | ||
5933 | striplink = link.Next; | ||
5934 | striplink.List.Remove(striplink); | ||
5935 | } while(striplink != donelink); | ||
5936 | } | ||
5937 | } | ||
5938 | |||
5939 | private class MyOp | ||
5940 | { | ||
5941 | public int index; | ||
5942 | public OpCode sysop; | ||
5943 | public string name; | ||
5944 | public string source; | ||
5945 | |||
5946 | private static Dictionary<string, MyOp> myopsbyname = new Dictionary<string, MyOp>(); | ||
5947 | private static int nextindex = 0; | ||
5948 | |||
5949 | public MyOp(OpCode sysop) | ||
5950 | { | ||
5951 | this.index = nextindex++; | ||
5952 | this.sysop = sysop; | ||
5953 | this.name = sysop.Name; | ||
5954 | myopsbyname.Add(name, this); | ||
5955 | } | ||
5956 | |||
5957 | public MyOp(OpCode sysop, string source) | ||
5958 | { | ||
5959 | this.index = nextindex++; | ||
5960 | this.sysop = sysop; | ||
5961 | this.name = sysop.Name; | ||
5962 | this.source = source; | ||
5963 | myopsbyname.Add(name, this); | ||
5964 | } | ||
5965 | |||
5966 | public MyOp(string name) | ||
5967 | { | ||
5968 | this.index = nextindex++; | ||
5969 | this.name = name; | ||
5970 | myopsbyname.Add(name, this); | ||
5971 | } | ||
5972 | |||
5973 | public MyOp(string name, string source) | ||
5974 | { | ||
5975 | this.index = nextindex++; | ||
5976 | this.name = name; | ||
5977 | this.source = source; | ||
5978 | myopsbyname.Add(name, this); | ||
5979 | } | ||
5980 | |||
5981 | public static MyOp GetByName(string name) | ||
5982 | { | ||
5983 | return myopsbyname[name]; | ||
5984 | } | ||
5985 | |||
5986 | public override string ToString() | ||
5987 | { | ||
5988 | return name; | ||
5989 | } | ||
5990 | |||
5991 | // these copied from OpCodes.cs | ||
5992 | public static readonly MyOp Nop = new MyOp(OpCodes.Nop); | ||
5993 | public static readonly MyOp Break = new MyOp(OpCodes.Break); | ||
5994 | public static readonly MyOp Ldarg_0 = new MyOp(OpCodes.Ldarg_0); | ||
5995 | public static readonly MyOp Ldarg_1 = new MyOp(OpCodes.Ldarg_1); | ||
5996 | public static readonly MyOp Ldarg_2 = new MyOp(OpCodes.Ldarg_2); | ||
5997 | public static readonly MyOp Ldarg_3 = new MyOp(OpCodes.Ldarg_3); | ||
5998 | public static readonly MyOp Ldloc_0 = new MyOp(OpCodes.Ldloc_0); | ||
5999 | public static readonly MyOp Ldloc_1 = new MyOp(OpCodes.Ldloc_1); | ||
6000 | public static readonly MyOp Ldloc_2 = new MyOp(OpCodes.Ldloc_2); | ||
6001 | public static readonly MyOp Ldloc_3 = new MyOp(OpCodes.Ldloc_3); | ||
6002 | public static readonly MyOp Stloc_0 = new MyOp(OpCodes.Stloc_0); | ||
6003 | public static readonly MyOp Stloc_1 = new MyOp(OpCodes.Stloc_1); | ||
6004 | public static readonly MyOp Stloc_2 = new MyOp(OpCodes.Stloc_2); | ||
6005 | public static readonly MyOp Stloc_3 = new MyOp(OpCodes.Stloc_3); | ||
6006 | public static readonly MyOp Ldarg_S = new MyOp(OpCodes.Ldarg_S); | ||
6007 | public static readonly MyOp Ldarga_S = new MyOp(OpCodes.Ldarga_S); | ||
6008 | public static readonly MyOp Starg_S = new MyOp(OpCodes.Starg_S); | ||
6009 | public static readonly MyOp Ldloc_S = new MyOp(OpCodes.Ldloc_S); | ||
6010 | public static readonly MyOp Ldloca_S = new MyOp(OpCodes.Ldloca_S); | ||
6011 | public static readonly MyOp Stloc_S = new MyOp(OpCodes.Stloc_S); | ||
6012 | public static readonly MyOp Ldnull = new MyOp(OpCodes.Ldnull); | ||
6013 | public static readonly MyOp Ldc_I4_M1 = new MyOp(OpCodes.Ldc_I4_M1); | ||
6014 | public static readonly MyOp Ldc_I4_0 = new MyOp(OpCodes.Ldc_I4_0); | ||
6015 | public static readonly MyOp Ldc_I4_1 = new MyOp(OpCodes.Ldc_I4_1); | ||
6016 | public static readonly MyOp Ldc_I4_2 = new MyOp(OpCodes.Ldc_I4_2); | ||
6017 | public static readonly MyOp Ldc_I4_3 = new MyOp(OpCodes.Ldc_I4_3); | ||
6018 | public static readonly MyOp Ldc_I4_4 = new MyOp(OpCodes.Ldc_I4_4); | ||
6019 | public static readonly MyOp Ldc_I4_5 = new MyOp(OpCodes.Ldc_I4_5); | ||
6020 | public static readonly MyOp Ldc_I4_6 = new MyOp(OpCodes.Ldc_I4_6); | ||
6021 | public static readonly MyOp Ldc_I4_7 = new MyOp(OpCodes.Ldc_I4_7); | ||
6022 | public static readonly MyOp Ldc_I4_8 = new MyOp(OpCodes.Ldc_I4_8); | ||
6023 | public static readonly MyOp Ldc_I4_S = new MyOp(OpCodes.Ldc_I4_S); | ||
6024 | public static readonly MyOp Ldc_I4 = new MyOp(OpCodes.Ldc_I4); | ||
6025 | public static readonly MyOp Ldc_I8 = new MyOp(OpCodes.Ldc_I8); | ||
6026 | public static readonly MyOp Ldc_R4 = new MyOp(OpCodes.Ldc_R4); | ||
6027 | public static readonly MyOp Ldc_R8 = new MyOp(OpCodes.Ldc_R8); | ||
6028 | public static readonly MyOp Dup = new MyOp(OpCodes.Dup); | ||
6029 | public static readonly MyOp Pop = new MyOp(OpCodes.Pop); | ||
6030 | public static readonly MyOp Jmp = new MyOp(OpCodes.Jmp); | ||
6031 | public static readonly MyOp Call = new MyOp(OpCodes.Call); | ||
6032 | public static readonly MyOp Calli = new MyOp(OpCodes.Calli); | ||
6033 | public static readonly MyOp Ret = new MyOp(OpCodes.Ret); | ||
6034 | public static readonly MyOp Br_S = new MyOp(OpCodes.Br_S); | ||
6035 | public static readonly MyOp Brfalse_S = new MyOp(OpCodes.Brfalse_S); | ||
6036 | public static readonly MyOp Brtrue_S = new MyOp(OpCodes.Brtrue_S); | ||
6037 | public static readonly MyOp Beq_S = new MyOp(OpCodes.Beq_S, "=="); | ||
6038 | public static readonly MyOp Bge_S = new MyOp(OpCodes.Bge_S, ">="); | ||
6039 | public static readonly MyOp Bgt_S = new MyOp(OpCodes.Bgt_S, ">"); | ||
6040 | public static readonly MyOp Ble_S = new MyOp(OpCodes.Ble_S, "<="); | ||
6041 | public static readonly MyOp Blt_S = new MyOp(OpCodes.Blt_S, "<"); | ||
6042 | public static readonly MyOp Bne_Un_S = new MyOp(OpCodes.Bne_Un_S, "!="); | ||
6043 | public static readonly MyOp Bge_Un_S = new MyOp(OpCodes.Bge_Un_S); | ||
6044 | public static readonly MyOp Bgt_Un_S = new MyOp(OpCodes.Bgt_Un_S); | ||
6045 | public static readonly MyOp Ble_Un_S = new MyOp(OpCodes.Ble_Un_S); | ||
6046 | public static readonly MyOp Blt_Un_S = new MyOp(OpCodes.Blt_Un_S); | ||
6047 | public static readonly MyOp Br = new MyOp(OpCodes.Br); | ||
6048 | public static readonly MyOp Brfalse = new MyOp(OpCodes.Brfalse, "!"); | ||
6049 | public static readonly MyOp Brtrue = new MyOp(OpCodes.Brtrue, "!!"); | ||
6050 | public static readonly MyOp Beq = new MyOp(OpCodes.Beq, "=="); | ||
6051 | public static readonly MyOp Bge = new MyOp(OpCodes.Bge, ">="); | ||
6052 | public static readonly MyOp Bgt = new MyOp(OpCodes.Bgt, ">"); | ||
6053 | public static readonly MyOp Ble = new MyOp(OpCodes.Ble, "<="); | ||
6054 | public static readonly MyOp Blt = new MyOp(OpCodes.Blt, "<"); | ||
6055 | public static readonly MyOp Bne_Un = new MyOp(OpCodes.Bne_Un, "!="); | ||
6056 | public static readonly MyOp Bge_Un = new MyOp(OpCodes.Bge_Un); | ||
6057 | public static readonly MyOp Bgt_Un = new MyOp(OpCodes.Bgt_Un); | ||
6058 | public static readonly MyOp Ble_Un = new MyOp(OpCodes.Ble_Un); | ||
6059 | public static readonly MyOp Blt_Un = new MyOp(OpCodes.Blt_Un); | ||
6060 | public static readonly MyOp Switch = new MyOp(OpCodes.Switch); | ||
6061 | public static readonly MyOp Ldind_I1 = new MyOp(OpCodes.Ldind_I1); | ||
6062 | public static readonly MyOp Ldind_U1 = new MyOp(OpCodes.Ldind_U1); | ||
6063 | public static readonly MyOp Ldind_I2 = new MyOp(OpCodes.Ldind_I2); | ||
6064 | public static readonly MyOp Ldind_U2 = new MyOp(OpCodes.Ldind_U2); | ||
6065 | public static readonly MyOp Ldind_I4 = new MyOp(OpCodes.Ldind_I4); | ||
6066 | public static readonly MyOp Ldind_U4 = new MyOp(OpCodes.Ldind_U4); | ||
6067 | public static readonly MyOp Ldind_I8 = new MyOp(OpCodes.Ldind_I8); | ||
6068 | public static readonly MyOp Ldind_I = new MyOp(OpCodes.Ldind_I); | ||
6069 | public static readonly MyOp Ldind_R4 = new MyOp(OpCodes.Ldind_R4); | ||
6070 | public static readonly MyOp Ldind_R8 = new MyOp(OpCodes.Ldind_R8); | ||
6071 | public static readonly MyOp Ldind_Ref = new MyOp(OpCodes.Ldind_Ref); | ||
6072 | public static readonly MyOp Stind_Ref = new MyOp(OpCodes.Stind_Ref); | ||
6073 | public static readonly MyOp Stind_I1 = new MyOp(OpCodes.Stind_I1); | ||
6074 | public static readonly MyOp Stind_I2 = new MyOp(OpCodes.Stind_I2); | ||
6075 | public static readonly MyOp Stind_I4 = new MyOp(OpCodes.Stind_I4); | ||
6076 | public static readonly MyOp Stind_I8 = new MyOp(OpCodes.Stind_I8); | ||
6077 | public static readonly MyOp Stind_R4 = new MyOp(OpCodes.Stind_R4); | ||
6078 | public static readonly MyOp Stind_R8 = new MyOp(OpCodes.Stind_R8); | ||
6079 | public static readonly MyOp Add = new MyOp(OpCodes.Add, "+"); | ||
6080 | public static readonly MyOp Sub = new MyOp(OpCodes.Sub, "-"); | ||
6081 | public static readonly MyOp Mul = new MyOp(OpCodes.Mul, "*"); | ||
6082 | public static readonly MyOp Div = new MyOp(OpCodes.Div, "/"); | ||
6083 | public static readonly MyOp Div_Un = new MyOp(OpCodes.Div_Un); | ||
6084 | public static readonly MyOp Rem = new MyOp(OpCodes.Rem, "%"); | ||
6085 | public static readonly MyOp Rem_Un = new MyOp(OpCodes.Rem_Un); | ||
6086 | public static readonly MyOp And = new MyOp(OpCodes.And, "&"); | ||
6087 | public static readonly MyOp Or = new MyOp(OpCodes.Or, "|"); | ||
6088 | public static readonly MyOp Xor = new MyOp(OpCodes.Xor, "^"); | ||
6089 | public static readonly MyOp Shl = new MyOp(OpCodes.Shl, "<<"); | ||
6090 | public static readonly MyOp Shr = new MyOp(OpCodes.Shr, ">>"); | ||
6091 | public static readonly MyOp Shr_Un = new MyOp(OpCodes.Shr_Un); | ||
6092 | public static readonly MyOp Neg = new MyOp(OpCodes.Neg, "-"); | ||
6093 | public static readonly MyOp Not = new MyOp(OpCodes.Not, "~"); | ||
6094 | public static readonly MyOp Conv_I1 = new MyOp(OpCodes.Conv_I1); | ||
6095 | public static readonly MyOp Conv_I2 = new MyOp(OpCodes.Conv_I2); | ||
6096 | public static readonly MyOp Conv_I4 = new MyOp(OpCodes.Conv_I4); | ||
6097 | public static readonly MyOp Conv_I8 = new MyOp(OpCodes.Conv_I8); | ||
6098 | public static readonly MyOp Conv_R4 = new MyOp(OpCodes.Conv_R4); | ||
6099 | public static readonly MyOp Conv_R8 = new MyOp(OpCodes.Conv_R8); | ||
6100 | public static readonly MyOp Conv_U4 = new MyOp(OpCodes.Conv_U4); | ||
6101 | public static readonly MyOp Conv_U8 = new MyOp(OpCodes.Conv_U8); | ||
6102 | public static readonly MyOp Callvirt = new MyOp(OpCodes.Callvirt); | ||
6103 | public static readonly MyOp Cpobj = new MyOp(OpCodes.Cpobj); | ||
6104 | public static readonly MyOp Ldobj = new MyOp(OpCodes.Ldobj); | ||
6105 | public static readonly MyOp Ldstr = new MyOp(OpCodes.Ldstr); | ||
6106 | public static readonly MyOp Newobj = new MyOp(OpCodes.Newobj); | ||
6107 | public static readonly MyOp Castclass = new MyOp(OpCodes.Castclass); | ||
6108 | public static readonly MyOp Isinst = new MyOp(OpCodes.Isinst); | ||
6109 | public static readonly MyOp Conv_R_Un = new MyOp(OpCodes.Conv_R_Un); | ||
6110 | public static readonly MyOp Unbox = new MyOp(OpCodes.Unbox); | ||
6111 | public static readonly MyOp Throw = new MyOp(OpCodes.Throw); | ||
6112 | public static readonly MyOp Ldfld = new MyOp(OpCodes.Ldfld); | ||
6113 | public static readonly MyOp Ldflda = new MyOp(OpCodes.Ldflda); | ||
6114 | public static readonly MyOp Stfld = new MyOp(OpCodes.Stfld); | ||
6115 | public static readonly MyOp Ldsfld = new MyOp(OpCodes.Ldsfld); | ||
6116 | public static readonly MyOp Ldsflda = new MyOp(OpCodes.Ldsflda); | ||
6117 | public static readonly MyOp Stsfld = new MyOp(OpCodes.Stsfld); | ||
6118 | public static readonly MyOp Stobj = new MyOp(OpCodes.Stobj); | ||
6119 | public static readonly MyOp Conv_Ovf_I1_Un = new MyOp(OpCodes.Conv_Ovf_I1_Un); | ||
6120 | public static readonly MyOp Conv_Ovf_I2_Un = new MyOp(OpCodes.Conv_Ovf_I2_Un); | ||
6121 | public static readonly MyOp Conv_Ovf_I4_Un = new MyOp(OpCodes.Conv_Ovf_I4_Un); | ||
6122 | public static readonly MyOp Conv_Ovf_I8_Un = new MyOp(OpCodes.Conv_Ovf_I8_Un); | ||
6123 | public static readonly MyOp Conv_Ovf_U1_Un = new MyOp(OpCodes.Conv_Ovf_U1_Un); | ||
6124 | public static readonly MyOp Conv_Ovf_U2_Un = new MyOp(OpCodes.Conv_Ovf_U2_Un); | ||
6125 | public static readonly MyOp Conv_Ovf_U4_Un = new MyOp(OpCodes.Conv_Ovf_U4_Un); | ||
6126 | public static readonly MyOp Conv_Ovf_U8_Un = new MyOp(OpCodes.Conv_Ovf_U8_Un); | ||
6127 | public static readonly MyOp Conv_Ovf_I_Un = new MyOp(OpCodes.Conv_Ovf_I_Un); | ||
6128 | public static readonly MyOp Conv_Ovf_U_Un = new MyOp(OpCodes.Conv_Ovf_U_Un); | ||
6129 | public static readonly MyOp Box = new MyOp(OpCodes.Box); | ||
6130 | public static readonly MyOp Newarr = new MyOp(OpCodes.Newarr); | ||
6131 | public static readonly MyOp Ldlen = new MyOp(OpCodes.Ldlen); | ||
6132 | public static readonly MyOp Ldelema = new MyOp(OpCodes.Ldelema); | ||
6133 | public static readonly MyOp Ldelem_I1 = new MyOp(OpCodes.Ldelem_I1); | ||
6134 | public static readonly MyOp Ldelem_U1 = new MyOp(OpCodes.Ldelem_U1); | ||
6135 | public static readonly MyOp Ldelem_I2 = new MyOp(OpCodes.Ldelem_I2); | ||
6136 | public static readonly MyOp Ldelem_U2 = new MyOp(OpCodes.Ldelem_U2); | ||
6137 | public static readonly MyOp Ldelem_I4 = new MyOp(OpCodes.Ldelem_I4); | ||
6138 | public static readonly MyOp Ldelem_U4 = new MyOp(OpCodes.Ldelem_U4); | ||
6139 | public static readonly MyOp Ldelem_I8 = new MyOp(OpCodes.Ldelem_I8); | ||
6140 | public static readonly MyOp Ldelem_I = new MyOp(OpCodes.Ldelem_I); | ||
6141 | public static readonly MyOp Ldelem_R4 = new MyOp(OpCodes.Ldelem_R4); | ||
6142 | public static readonly MyOp Ldelem_R8 = new MyOp(OpCodes.Ldelem_R8); | ||
6143 | public static readonly MyOp Ldelem_Ref = new MyOp(OpCodes.Ldelem_Ref); | ||
6144 | public static readonly MyOp Stelem_I = new MyOp(OpCodes.Stelem_I); | ||
6145 | public static readonly MyOp Stelem_I1 = new MyOp(OpCodes.Stelem_I1); | ||
6146 | public static readonly MyOp Stelem_I2 = new MyOp(OpCodes.Stelem_I2); | ||
6147 | public static readonly MyOp Stelem_I4 = new MyOp(OpCodes.Stelem_I4); | ||
6148 | public static readonly MyOp Stelem_I8 = new MyOp(OpCodes.Stelem_I8); | ||
6149 | public static readonly MyOp Stelem_R4 = new MyOp(OpCodes.Stelem_R4); | ||
6150 | public static readonly MyOp Stelem_R8 = new MyOp(OpCodes.Stelem_R8); | ||
6151 | public static readonly MyOp Stelem_Ref = new MyOp(OpCodes.Stelem_Ref); | ||
6152 | public static readonly MyOp Ldelem = new MyOp(OpCodes.Ldelem); | ||
6153 | public static readonly MyOp Stelem = new MyOp(OpCodes.Stelem); | ||
6154 | public static readonly MyOp Unbox_Any = new MyOp(OpCodes.Unbox_Any); | ||
6155 | public static readonly MyOp Conv_Ovf_I1 = new MyOp(OpCodes.Conv_Ovf_I1); | ||
6156 | public static readonly MyOp Conv_Ovf_U1 = new MyOp(OpCodes.Conv_Ovf_U1); | ||
6157 | public static readonly MyOp Conv_Ovf_I2 = new MyOp(OpCodes.Conv_Ovf_I2); | ||
6158 | public static readonly MyOp Conv_Ovf_U2 = new MyOp(OpCodes.Conv_Ovf_U2); | ||
6159 | public static readonly MyOp Conv_Ovf_I4 = new MyOp(OpCodes.Conv_Ovf_I4); | ||
6160 | public static readonly MyOp Conv_Ovf_U4 = new MyOp(OpCodes.Conv_Ovf_U4); | ||
6161 | public static readonly MyOp Conv_Ovf_I8 = new MyOp(OpCodes.Conv_Ovf_I8); | ||
6162 | public static readonly MyOp Conv_Ovf_U8 = new MyOp(OpCodes.Conv_Ovf_U8); | ||
6163 | public static readonly MyOp Refanyval = new MyOp(OpCodes.Refanyval); | ||
6164 | public static readonly MyOp Ckfinite = new MyOp(OpCodes.Ckfinite); | ||
6165 | public static readonly MyOp Mkrefany = new MyOp(OpCodes.Mkrefany); | ||
6166 | public static readonly MyOp Ldtoken = new MyOp(OpCodes.Ldtoken); | ||
6167 | public static readonly MyOp Conv_U2 = new MyOp(OpCodes.Conv_U2); | ||
6168 | public static readonly MyOp Conv_U1 = new MyOp(OpCodes.Conv_U1); | ||
6169 | public static readonly MyOp Conv_I = new MyOp(OpCodes.Conv_I); | ||
6170 | public static readonly MyOp Conv_Ovf_I = new MyOp(OpCodes.Conv_Ovf_I); | ||
6171 | public static readonly MyOp Conv_Ovf_U = new MyOp(OpCodes.Conv_Ovf_U); | ||
6172 | public static readonly MyOp Add_Ovf = new MyOp(OpCodes.Add_Ovf); | ||
6173 | public static readonly MyOp Add_Ovf_Un = new MyOp(OpCodes.Add_Ovf_Un); | ||
6174 | public static readonly MyOp Mul_Ovf = new MyOp(OpCodes.Mul_Ovf); | ||
6175 | public static readonly MyOp Mul_Ovf_Un = new MyOp(OpCodes.Mul_Ovf_Un); | ||
6176 | public static readonly MyOp Sub_Ovf = new MyOp(OpCodes.Sub_Ovf); | ||
6177 | public static readonly MyOp Sub_Ovf_Un = new MyOp(OpCodes.Sub_Ovf_Un); | ||
6178 | public static readonly MyOp Endfinally = new MyOp(OpCodes.Endfinally); | ||
6179 | public static readonly MyOp Leave = new MyOp(OpCodes.Leave); | ||
6180 | public static readonly MyOp Leave_S = new MyOp(OpCodes.Leave_S); | ||
6181 | public static readonly MyOp Stind_I = new MyOp(OpCodes.Stind_I); | ||
6182 | public static readonly MyOp Conv_U = new MyOp(OpCodes.Conv_U); | ||
6183 | public static readonly MyOp Prefix7 = new MyOp(OpCodes.Prefix7); | ||
6184 | public static readonly MyOp Prefix6 = new MyOp(OpCodes.Prefix6); | ||
6185 | public static readonly MyOp Prefix5 = new MyOp(OpCodes.Prefix5); | ||
6186 | public static readonly MyOp Prefix4 = new MyOp(OpCodes.Prefix4); | ||
6187 | public static readonly MyOp Prefix3 = new MyOp(OpCodes.Prefix3); | ||
6188 | public static readonly MyOp Prefix2 = new MyOp(OpCodes.Prefix2); | ||
6189 | public static readonly MyOp Prefix1 = new MyOp(OpCodes.Prefix1); | ||
6190 | public static readonly MyOp Prefixref = new MyOp(OpCodes.Prefixref); | ||
6191 | public static readonly MyOp Arglist = new MyOp(OpCodes.Arglist); | ||
6192 | public static readonly MyOp Ceq = new MyOp(OpCodes.Ceq, "=="); | ||
6193 | public static readonly MyOp Cgt = new MyOp(OpCodes.Cgt, ">"); | ||
6194 | public static readonly MyOp Cgt_Un = new MyOp(OpCodes.Cgt_Un); | ||
6195 | public static readonly MyOp Clt = new MyOp(OpCodes.Clt, "<"); | ||
6196 | public static readonly MyOp Clt_Un = new MyOp(OpCodes.Clt_Un); | ||
6197 | public static readonly MyOp Ldftn = new MyOp(OpCodes.Ldftn); | ||
6198 | public static readonly MyOp Ldvirtftn = new MyOp(OpCodes.Ldvirtftn); | ||
6199 | public static readonly MyOp Ldarg = new MyOp(OpCodes.Ldarg); | ||
6200 | public static readonly MyOp Ldarga = new MyOp(OpCodes.Ldarga); | ||
6201 | public static readonly MyOp Starg = new MyOp(OpCodes.Starg); | ||
6202 | public static readonly MyOp Ldloc = new MyOp(OpCodes.Ldloc); | ||
6203 | public static readonly MyOp Ldloca = new MyOp(OpCodes.Ldloca); | ||
6204 | public static readonly MyOp Stloc = new MyOp(OpCodes.Stloc); | ||
6205 | public static readonly MyOp Localloc = new MyOp(OpCodes.Localloc); | ||
6206 | public static readonly MyOp Endfilter = new MyOp(OpCodes.Endfilter); | ||
6207 | public static readonly MyOp Unaligned = new MyOp(OpCodes.Unaligned); | ||
6208 | public static readonly MyOp Volatile = new MyOp(OpCodes.Volatile); | ||
6209 | public static readonly MyOp Tailcall = new MyOp(OpCodes.Tailcall); | ||
6210 | public static readonly MyOp Initobj = new MyOp(OpCodes.Initobj); | ||
6211 | public static readonly MyOp Constrained = new MyOp(OpCodes.Constrained); | ||
6212 | public static readonly MyOp Cpblk = new MyOp(OpCodes.Cpblk); | ||
6213 | public static readonly MyOp Initblk = new MyOp(OpCodes.Initblk); | ||
6214 | public static readonly MyOp Rethrow = new MyOp(OpCodes.Rethrow); | ||
6215 | public static readonly MyOp Sizeof = new MyOp(OpCodes.Sizeof); | ||
6216 | public static readonly MyOp Refanytype = new MyOp(OpCodes.Refanytype); | ||
6217 | public static readonly MyOp Readonly = new MyOp(OpCodes.Readonly); | ||
6218 | |||
6219 | // used internally | ||
6220 | public static readonly MyOp Cge = new MyOp("cge", ">="); | ||
6221 | public static readonly MyOp Cle = new MyOp("cle", "<="); | ||
6222 | public static readonly MyOp Cne = new MyOp("cne", "!="); | ||
6223 | } | ||
6224 | } | ||
6225 | } | ||