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