aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs2637
1 files changed, 2637 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs
new file mode 100644
index 0000000..9c0d621
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCollector.cs
@@ -0,0 +1,2637 @@
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
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using System;
30using System.Collections.Generic;
31using System.IO;
32using System.Reflection;
33using System.Reflection.Emit;
34using System.Text;
35
36
37/**
38 * @brief Wrapper class for ScriptMyILGen to do simple optimizations.
39 * The main one is to figure out which locals are active at the labels
40 * so the stack capture/restore code doesn't have to do everything.
41 * Second is it removes unnecessary back-to-back stloc/ldloc's.
42 */
43
44namespace OpenSim.Region.ScriptEngine.XMREngine
45{
46 /**
47 * @brief This is a list that keeps track of types pushed on the evaluation stack.
48 */
49 public class StackDepth : List<Type> {
50 public List<bool> isBoxeds = new List<bool> ();
51
52 /**
53 * @brief Clear both stacks.
54 */
55 public new void Clear ()
56 {
57 base.Clear ();
58 isBoxeds.Clear ();
59 }
60
61 /**
62 * @brief Pop call parameters and validate the types.
63 */
64 public void Pop (ParameterInfo[] pis)
65 {
66 int n = pis.Length;
67 int c = this.Count;
68 if (n > c) throw new Exception ("stack going negative");
69 for (int i = n; -- i >= 0;) {
70 -- c;
71 ExpectedVsOnStack (pis[i].ParameterType, this[c], isBoxeds[c]);
72 }
73 Pop (n);
74 }
75
76 /**
77 * @brief Pop values and validate the types.
78 */
79 public void Pop (Type[] ts)
80 {
81 int n = ts.Length;
82 int c = this.Count;
83 if (n > c) throw new Exception ("stack going negative");
84 for (int i = ts.Length; -- i >= 0;) {
85 -- c;
86 ExpectedVsOnStack (ts[i], this[c], isBoxeds[c]);
87 }
88 Pop (n);
89 }
90
91 /**
92 * @brief Pop a single value and validate the type.
93 */
94 public void Pop (Type t)
95 {
96 int c = this.Count;
97 if (c < 1) throw new Exception ("stack going negative");
98 ExpectedVsOnStack (t, this[c-1], isBoxeds[c-1]);
99 Pop (1);
100 }
101
102 /**
103 * @brief Pop a single value and validate that it is a numeric type.
104 */
105 public Type PopNumVal ()
106 {
107 int c = this.Count;
108 if (c < 1) throw new Exception ("stack going negative");
109 Type st = this[--c];
110 if (st == null) {
111 throw new Exception ("stack has null, expecting a numeric");
112 }
113 if (isBoxeds[c]) {
114 throw new Exception ("stack is boxed " + st.Name + ", expecting a numeric");
115 }
116 if ((st != typeof (bool)) && (st != typeof (char)) && (st != typeof (int)) &&
117 (st != typeof (long)) && (st != typeof (float)) && (st != typeof (double))) {
118 throw new Exception ("stack has " + st.Name + ", expecting a numeric");
119 }
120 return Pop (1);
121 }
122
123 /**
124 * @brief Pop a single value and validate that it is a reference type
125 */
126 public Type PopRef ()
127 {
128 int c = this.Count;
129 if (c < 1) throw new Exception ("stack going negative");
130 Type st = this[--c];
131 if ((st != null) && !isBoxeds[c] && st.IsValueType) {
132 throw new Exception ("stack has " + st.Name + ", expecting a ref type");
133 }
134 return Pop (1);
135 }
136
137 /**
138 * @brief Pop a single value and validate that it is a value type
139 */
140 public Type PopValue ()
141 {
142 int c = this.Count;
143 if (c < 1) throw new Exception ("stack going negative");
144 Type st = this[--c];
145 if (st == null) {
146 throw new Exception ("stack has null, expecting a value type");
147 }
148 if (!st.IsValueType) {
149 throw new Exception ("stack has " + st.Name + ", expecting a value type");
150 }
151 if (isBoxeds[c]) {
152 throw new Exception ("stack has boxed " + st.Name + ", expecting an unboxed value type");
153 }
154 return Pop (1);
155 }
156
157 // ex = what is expected to be on stack
158 // st = what is actually on stack (null for ldnull)
159 // stBoxed = stack value is boxed
160 public static void ExpectedVsOnStack (Type ex, Type st, bool stBoxed)
161 {
162 // ldnull pushed on stack can go into any pointer type
163 if (st == null) {
164 if (ex.IsByRef || ex.IsPointer || ex.IsClass || ex.IsInterface) return;
165 throw new Exception ("stack has null, expect " + ex.Name);
166 }
167
168 // simple case of expecting an object
169 // ...so the stack can have object,string, etc
170 // but we cant allow int = boxed int here
171 if (ex.IsAssignableFrom (st) && !stBoxed) return;
172
173 // case of expecting an enum on the stack
174 // but all the CIL code knows about are ints etc
175 // so convert the Enum type to integer or whatever
176 // and that should be assignable from what's on stack
177 if (ex.IsEnum && typeof (int).IsAssignableFrom (st)) return;
178
179 // bool, char, int are interchangeable on the stack
180 if ((ex == typeof (bool) || ex == typeof (char) || ex == typeof (int)) &&
181 (st == typeof (bool) || st == typeof (char) || st == typeof (int))) return;
182
183 // float and double are interchangeable on the stack
184 if ((ex == typeof (float) || ex == typeof (double)) &&
185 (st == typeof (float) || st == typeof (double))) return;
186
187 // object can accept any boxed type
188 if ((ex == typeof (object)) && stBoxed) return;
189
190 // otherwise, it is disallowed
191 throw new Exception ("stack has " + StackTypeString (st, stBoxed) + ", expect " + ex.Name);
192 }
193
194 /**
195 * @brief Pop values without any validation.
196 */
197 public Type Pop (int n)
198 {
199 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
200 Type lastPopped = null;
201 int c = this.Count;
202 if (n > c) throw new Exception ("stack going negative");
203 if (n > 0) {
204 lastPopped = this[c-n];
205 this.RemoveRange (c - n, n);
206 isBoxeds.RemoveRange (c - n, n);
207 }
208 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
209 return lastPopped;
210 }
211
212 /**
213 * @brief Peek at the n'th stack value.
214 * n = 0 : top of stack
215 * 1 : next to top
216 * ...
217 */
218 public Type Peek (int n)
219 {
220 int c = this.Count;
221 if (n > c - 1) throw new Exception ("stack going negative");
222 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
223 return this[c-n-1];
224 }
225 public bool PeekBoxed (int n)
226 {
227 int c = isBoxeds.Count;
228 if (n > c - 1) throw new Exception ("stack going negative");
229 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
230 return isBoxeds[c-n-1];
231 }
232
233 /**
234 * @brief Push a single value of the given type.
235 */
236 public void Push (Type t)
237 {
238 Push (t, false);
239 }
240 public void Push (Type t, bool isBoxed)
241 {
242 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
243 this.Add (t);
244 isBoxeds.Add (isBoxed);
245 }
246
247 /**
248 * @brief See if the types at a given label exactly match those on the stack.
249 * We should have the stack types be the same no matter how we branched
250 * or fell through to a particular label.
251 */
252 public void Matches (ScriptMyLabel label)
253 {
254 Type[] ts = label.stackDepth;
255 bool[] tsBoxeds = label.stackBoxeds;
256 int i;
257
258 if (this.Count != isBoxeds.Count) throw new Exception ("isBoxeds count bad");
259
260 if (ts == null) {
261 label.stackDepth = this.ToArray ();
262 label.stackBoxeds = isBoxeds.ToArray ();
263 } else if (ts.Length != this.Count) {
264 throw new Exception ("stack depth mismatch");
265 } else {
266 for (i = this.Count; -- i >= 0;) {
267 if (tsBoxeds[i] != this.isBoxeds[i]) goto mismatch;
268 if (ts[i] == this[i]) continue;
269 if ((ts[i] == typeof (bool) || ts[i] == typeof (char) || ts[i] == typeof (int)) &&
270 (this[i] == typeof (bool) || this[i] == typeof (char) || this[i] == typeof (int))) continue;
271 if ((ts[i] == typeof (double) || ts[i] == typeof (float)) &&
272 (this[i] == typeof (double) || this[i] == typeof (float))) continue;
273 goto mismatch;
274 }
275 }
276 return;
277 mismatch:
278 throw new Exception ("stack type mismatch: " + StackTypeString (ts[i], tsBoxeds[i]) + " vs " + StackTypeString (this[i], this.isBoxeds[i]));
279 }
280
281 private static string StackTypeString (Type ts, bool isBoxed)
282 {
283 if (!isBoxed) return ts.Name;
284 return "[" + ts.Name + "]";
285 }
286 }
287
288 /**
289 * @brief One of these per opcode and label in the function plus other misc markers.
290 * They form the CIL instruction stream of the function.
291 */
292 public abstract class GraphNode {
293 private static readonly bool DEBUG = false;
294
295 public const int OPINDENT = 4;
296 public const int OPDEBLEN = 12;
297
298 public ScriptCollector coll;
299 public GraphNodeBeginExceptionBlock tryBlock; // start of enclosing try block
300 // valid in the try section
301 // null in the catch/finally sections
302 // null outside of try block
303 // for the try node itself, links to outer try block
304 public GraphNodeBeginExceptionBlock excBlock; // start of enclosing try block
305 // valid in the try/catch/finally sections
306 // null outside of try/catch/finally block
307 // for the try node itself, links to outer try block
308
309 /*
310 * List of nodes in order as originally given.
311 */
312 public GraphNode nextLin, prevLin;
313 public int linSeqNo;
314
315 /**
316 * @brief Save pointer to collector.
317 */
318 public GraphNode (ScriptCollector coll)
319 {
320 this.coll = coll;
321 }
322
323 /**
324 * @brief Chain graph node to end of linear list.
325 */
326 public virtual void ChainLin ()
327 {
328 coll.lastLin.nextLin = this;
329 this.prevLin = coll.lastLin;
330 coll.lastLin = this;
331 this.tryBlock = coll.curTryBlock;
332 this.excBlock = coll.curExcBlock;
333
334 if (DEBUG) {
335 StringBuilder sb = new StringBuilder ("ChainLin*:");
336 sb.Append (coll.stackDepth.Count.ToString("D2"));
337 sb.Append (' ');
338 this.DebString (sb);
339 Console.WriteLine (sb.ToString ());
340 }
341 }
342
343 /**
344 * @brief Append full info to debugging string for printing out the instruction.
345 */
346 public void DebStringExt (StringBuilder sb)
347 {
348 int x = sb.Length;
349 sb.Append (this.linSeqNo.ToString ().PadLeft (5));
350 sb.Append (": ");
351 this.DebString (sb);
352
353 if (this.ReadsLocal () != null) ScriptCollector.PadToLength (sb, x + 60, " [read]");
354 if (this.WritesLocal () != null) ScriptCollector.PadToLength (sb, x + 68, " [write]");
355 ScriptCollector.PadToLength (sb, x + 72, " ->");
356 bool first = true;
357 foreach (GraphNode nn in this.NextNodes) {
358 if (first) {
359 sb.Append (nn.linSeqNo.ToString ().PadLeft (5));
360 first = false;
361 } else {
362 sb.Append (',');
363 sb.Append (nn.linSeqNo);
364 }
365 }
366 }
367
368 /**
369 * @brief See if it's possible for it to fall through to the next inline (nextLin) instruction.
370 */
371 public virtual bool CanFallThrough ()
372 {
373 return true;
374 }
375
376 /**
377 * @brief Append to debugging string for printing out the instruction.
378 */
379 public abstract void DebString (StringBuilder sb);
380 public override string ToString ()
381 {
382 StringBuilder sb = new StringBuilder ();
383 this.DebString (sb);
384 return sb.ToString ();
385 }
386
387 /**
388 * @brief See if this instruction reads a local variable.
389 */
390 public virtual ScriptMyLocal ReadsLocal () { return null; }
391
392 /**
393 * @brief See if this instruction writes a local variable.
394 */
395 public virtual ScriptMyLocal WritesLocal () { return null; }
396
397 /**
398 * @brief Write this instruction out to the wrapped object file.
399 */
400 public abstract void WriteOutOne (ScriptMyILGen ilGen);
401
402 /**
403 * @brief Iterate through all the possible next nodes, including the next inline node, if any.
404 * The next inline code is excluded if the instruction never falls through, eg, return, unconditional branch.
405 * It includes a possible conditional branch to the beginning of the corresponding catch/finally of every
406 * instruction in a try section.
407 */
408 private System.Collections.Generic.IEnumerable<GraphNode> nextNodes, nextNodesCatchFinally;
409 public System.Collections.Generic.IEnumerable<GraphNode> NextNodes
410 { get {
411 if (nextNodes == null) {
412 nextNodes = GetNNEnumerable ();
413 nextNodesCatchFinally = new NNEnumerableCatchFinally (this);
414 }
415 return nextNodesCatchFinally;
416 } }
417
418 /**
419 * @brief This acts as a wrapper around all the other NNEnumerable's below.
420 * It assumes every instruction in a try { } can throw an exception so it
421 * says that every instruction in a try { } can conditionally branch to
422 * the beginning of the corresponding catch { } or finally { }.
423 */
424 private class NNEnumerableCatchFinally : System.Collections.Generic.IEnumerable<GraphNode> {
425 private GraphNode gn;
426 public NNEnumerableCatchFinally (GraphNode gn)
427 {
428 this.gn = gn;
429 }
430 System.Collections.Generic.IEnumerator<GraphNode> System.Collections.Generic.IEnumerable<GraphNode>.GetEnumerator ()
431 {
432 return new NNEnumeratorCatchFinally (gn);
433 }
434 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
435 {
436 return new NNEnumeratorCatchFinally (gn);
437 }
438 }
439 private class NNEnumeratorCatchFinally : NNEnumeratorBase {
440 private GraphNode gn;
441 private int index = 0;
442 private System.Collections.Generic.IEnumerator<GraphNode> realEnumerator;
443 public NNEnumeratorCatchFinally (GraphNode gn)
444 {
445 this.gn = gn;
446 this.realEnumerator = gn.nextNodes.GetEnumerator ();
447 }
448 public override bool MoveNext ()
449 {
450 /*
451 * First off, return any targets the instruction can come up with.
452 */
453 if (realEnumerator.MoveNext ()) {
454 nn = realEnumerator.Current;
455 return true;
456 }
457
458 /*
459 * Then if this instruction is in a try section, say this instruction
460 * can potentially branch to the beginning of the corresponding
461 * catch/finally.
462 */
463 if ((index == 0) && (gn.tryBlock != null)) {
464 index ++;
465 nn = gn.tryBlock.catchFinallyBlock;
466 return true;
467 }
468
469 /*
470 * That's all we can do.
471 */
472 nn = null;
473 return false;
474 }
475 public override void Reset ()
476 {
477 realEnumerator.Reset ();
478 index = 0;
479 nn = null;
480 }
481 }
482
483 /**
484 * @brief This default iterator always returns the next inline node as the one-and-only next node.
485 * Other instructions need to override it if they can possibly do other than that.
486 */
487
488 /**
489 * @brief GetNNEnumerable() gets the nextnode enumerable part of a GraphNode,
490 * which in turn gives the list of nodes that can possibly be next in
491 * a flow-control sense. It simply instantiates the NNEnumerator sub-
492 * class which does the actual enumeration.
493 */
494 protected virtual System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
495 {
496 return new NNEnumerable (this, typeof (NNEnumerator));
497 }
498
499 private class NNEnumerator : NNEnumeratorBase {
500 private GraphNode gn;
501 private int index;
502 public NNEnumerator (GraphNode gn)
503 {
504 this.gn = gn;
505 }
506 public override bool MoveNext ()
507 {
508 switch (index) {
509 case 0: {
510 index ++;
511 nn = gn.nextLin;
512 return nn != null;
513 }
514 case 1: {
515 nn = null;
516 return false;
517 }
518 }
519 throw new Exception ();
520 }
521 public override void Reset ()
522 {
523 index = 0;
524 nn = null;
525 }
526 }
527 }
528
529 /**
530 * @brief Things that derive from this are the beginning of a block.
531 * A block of code is that which begins with a label or is the beginning of all code
532 * and it contains no labels, ie, it can't be jumped into other than at its beginning.
533 */
534 public abstract class GraphNodeBlock : GraphNode {
535 public List<ScriptMyLocal> localsWrittenBeforeRead = new List<ScriptMyLocal> ();
536 public List<ScriptMyLocal> localsReadBeforeWritten = new List<ScriptMyLocal> ();
537 public int hasBeenResolved;
538 public GraphNodeBlock (ScriptCollector coll) : base (coll) { }
539 }
540
541 /**
542 * @brief This placeholder is at the beginning of the code so the first few instructions
543 * belong to some block.
544 */
545 public class GraphNodeBegin : GraphNodeBlock {
546 public GraphNodeBegin (ScriptCollector coll) : base (coll) { }
547 public override void DebString (StringBuilder sb) { sb.Append ("begin"); }
548 public override void WriteOutOne (ScriptMyILGen ilGen) { }
549 }
550
551 /**
552 * @brief Beginning of try block.
553 */
554 public class GraphNodeBeginExceptionBlock : GraphNodeBlock {
555 public GraphNodeBeginExceptionBlock outerTryBlock; // next outer try opcode or null
556 public GraphNodeCatchFinallyBlock catchFinallyBlock; // start of associated catch or finally
557 public GraphNodeEndExceptionBlock endExcBlock; // end of associated catch or finally
558 public int excBlkSeqNo; // debugging
559
560 public GraphNodeBeginExceptionBlock (ScriptCollector coll) : base (coll)
561 { }
562
563 public override void ChainLin ()
564 {
565 base.ChainLin ();
566
567 // we should always start try blocks with nothing on stack
568 // ...as CLI wipes stack for various conditions
569 if (coll.stackDepth.Count != 0) {
570 throw new Exception ("stack depth " + coll.stackDepth.Count);
571 }
572 }
573
574 public override void DebString (StringBuilder sb)
575 {
576 sb.Append (" beginexceptionblock_");
577 sb.Append (excBlkSeqNo);
578 }
579
580 public override void WriteOutOne (ScriptMyILGen ilGen)
581 {
582 ilGen.BeginExceptionBlock ();
583 }
584 }
585
586 /**
587 * @brief Beginning of catch or finally block.
588 */
589 public abstract class GraphNodeCatchFinallyBlock : GraphNodeBlock {
590 public GraphNodeCatchFinallyBlock (ScriptCollector coll) : base (coll)
591 { }
592
593 public override void ChainLin ()
594 {
595 base.ChainLin ();
596
597 // we should always start catch/finally blocks with nothing on stack
598 // ...as CLI wipes stack for various conditions
599 if (coll.stackDepth.Count != 0) {
600 throw new Exception ("stack depth " + coll.stackDepth.Count);
601 }
602 }
603 }
604
605 /**
606 * @brief Beginning of catch block.
607 */
608 public class GraphNodeBeginCatchBlock : GraphNodeCatchFinallyBlock {
609 public Type excType;
610
611 public GraphNodeBeginCatchBlock (ScriptCollector coll, Type excType) : base (coll)
612 {
613 this.excType = excType;
614 }
615
616 public override void ChainLin ()
617 {
618 base.ChainLin ();
619
620 // catch block always enters with one value on stack
621 if (coll.stackDepth.Count != 0) {
622 throw new Exception ("stack depth " + coll.stackDepth.Count);
623 }
624 coll.stackDepth.Push (excType);
625 }
626
627 public override void DebString (StringBuilder sb)
628 {
629 sb.Append (" begincatchblock_");
630 sb.Append (excBlock.excBlkSeqNo);
631 }
632
633 public override void WriteOutOne (ScriptMyILGen ilGen)
634 {
635 ilGen.BeginCatchBlock (excType);
636 }
637
638 /**
639 * @brief The beginning of every catch { } conditinally branches to the beginning
640 * of all outer catch { }s up to and including the next outer finally { }.
641 */
642 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
643 {
644 return new NNEnumerable (this, typeof (NNEnumerator));
645 }
646
647 private class NNEnumerator : NNEnumeratorBase {
648 private GraphNodeBeginCatchBlock gn;
649 private int index;
650 public NNEnumerator (GraphNodeBeginCatchBlock gn)
651 {
652 this.gn = gn;
653 }
654 public override bool MoveNext ()
655 {
656 while (true) {
657 switch (index) {
658 case 0: {
659 // start with the fallthru
660 nn = gn.nextLin;
661 index ++;
662 return true;
663 }
664
665 case 1: {
666 // get the first outer catch { } or finally { }
667 // pretend we last returned beginning of this catch { }
668 // then loop back to get next outer catch { } or finally { }
669 nn = gn;
670 break;
671 }
672
673 case 2: {
674 // nn points to a catch { } previously returned
675 // get the corresponding try { }
676 GraphNodeBeginExceptionBlock nntry = nn.excBlock;
677
678 // step out to next outer try { }
679 nntry = nntry.excBlock;
680 if (nntry == null) break;
681
682 // return corresponding catch { } or finally { }
683 nn = nntry.catchFinallyBlock;
684
685 // if it's a finally { } we don't do anything after that
686 if (nn is GraphNodeBeginFinallyBlock) index ++;
687 return true;
688 }
689
690 case 3: {
691 // we've returned the fallthru, catches and one finally
692 // so there's nothing more to say
693 nn = null;
694 return false;
695 }
696
697 default: throw new Exception ();
698 }
699 index ++;
700 }
701 }
702 public override void Reset ()
703 {
704 index = 0;
705 nn = null;
706 }
707 }
708 }
709
710 /**
711 * @brief Beginning of finally block.
712 */
713 public class GraphNodeBeginFinallyBlock : GraphNodeCatchFinallyBlock {
714
715 // leaveTargets has a list of all the targets of any contained
716 // leave instructions, ie, where an endfinally can possibly jump.
717 // But only those targets within the next outer finally { }, we
718 // don't contain any targets outside of that, those targets are
719 // stored in the actual finally that will jump to the target.
720 // The endfinally enumerator assumes that it is always possible
721 // for it to jump to the next outer finally (as would happen for
722 // an uncaught exception), so no need to do anything special.
723 public List<GraphNodeBlock> leaveTargets = new List<GraphNodeBlock> ();
724
725 public GraphNodeBeginFinallyBlock (ScriptCollector coll) : base (coll)
726 { }
727
728 public override void DebString (StringBuilder sb)
729 {
730 sb.Append (" beginfinallyblock_");
731 sb.Append (excBlock.excBlkSeqNo);
732 }
733
734 public override void WriteOutOne (ScriptMyILGen ilGen)
735 {
736 ilGen.BeginFinallyBlock ();
737 }
738 }
739
740 /**
741 * @brief End of try/catch/finally block.
742 */
743 public class GraphNodeEndExceptionBlock : GraphNode {
744 public GraphNodeEndExceptionBlock (ScriptCollector coll) : base (coll)
745 { }
746
747 public override void ChainLin ()
748 {
749 base.ChainLin ();
750
751 // we should always end exception blocks with nothing on stack
752 // ...as CLI wipes stack for various conditions
753 if (coll.stackDepth.Count != 0) {
754 throw new Exception ("stack depth " + coll.stackDepth.Count);
755 }
756 }
757
758 public override void DebString (StringBuilder sb)
759 {
760 sb.Append (" endexceptionblock_");
761 sb.Append (excBlock.excBlkSeqNo);
762 }
763
764 public override void WriteOutOne (ScriptMyILGen ilGen)
765 {
766 ilGen.EndExceptionBlock ();
767 }
768 }
769
770 /**
771 * @brief Actual instruction emits...
772 */
773 public abstract class GraphNodeEmit : GraphNode {
774 public OpCode opcode;
775 public Token errorAt;
776
777 public GraphNodeEmit (ScriptCollector coll, Token errorAt, OpCode opcode) : base (coll)
778 {
779 this.opcode = opcode;
780 this.errorAt = errorAt;
781 }
782
783 public override void ChainLin ()
784 {
785 base.ChainLin ();
786
787 // compute resultant stack depth
788 int stack = coll.stackDepth.Count;
789
790 if ((stack != 0) && ((opcode == OpCodes.Endfinally) || (opcode == OpCodes.Leave) || (opcode == OpCodes.Rethrow))) {
791 throw new Exception (opcode + " stack depth " + stack);
792 }
793 if ((stack != 1) && (opcode == OpCodes.Throw)) {
794 throw new Exception (opcode + " stack depth " + stack);
795 }
796 }
797
798 /**
799 * @brief See if it's possible for it to fall through to the next inline (nextLin) instruction.
800 */
801 public override bool CanFallThrough ()
802 {
803 switch (opcode.FlowControl) {
804 case FlowControl.Branch: return false; // unconditional branch
805 case FlowControl.Break: return true; // break
806 case FlowControl.Call: return true; // call
807 case FlowControl.Cond_Branch: return true; // conditional branch
808 case FlowControl.Next: return true; // falls through to next instruction
809 case FlowControl.Return: return false; // return
810 case FlowControl.Throw: return false; // throw
811 default: {
812 string op = opcode.ToString ();
813 if (op == "volatile.") return true;
814 throw new Exception ("unknown flow control " + opcode.FlowControl + " for " + op);
815 }
816 }
817 }
818
819 // if followed by OpCodes.Pop, it can be discarded
820 public bool isPoppable
821 { get {
822 return
823 ((opcode.StackBehaviourPop == StackBehaviour.Pop0) && // ldarg,ldloc,ldsfld
824 (opcode.StackBehaviourPush == StackBehaviour.Push1)) ||
825 ((opcode.StackBehaviourPop == StackBehaviour.Pop0) && // ldarga,ldloca,ldc,ldsflda,...
826 (opcode.StackBehaviourPush == StackBehaviour.Pushi)) ||
827 (opcode == OpCodes.Ldnull) ||
828 (opcode == OpCodes.Ldc_R4) ||
829 (opcode == OpCodes.Ldc_R8) ||
830 (opcode == OpCodes.Ldstr) ||
831 (opcode == OpCodes.Ldc_I8) ||
832 (opcode == OpCodes.Dup);
833 } }
834
835 public override void DebString (StringBuilder sb)
836 {
837 sb.Append ("".PadRight (OPINDENT));
838 sb.Append (opcode.ToString ().PadRight (OPDEBLEN));
839 }
840
841 /**
842 * @brief If instruction is terminating, we say there is nothing following (eg, return).
843 * Otherwise, say the one-and-only next instruction is the next instruction inline.
844 */
845 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
846 {
847 return new NNEnumerable (this, typeof (NNEnumerator));
848 }
849
850 private class NNEnumerator : NNEnumeratorBase {
851 private GraphNodeEmit gn;
852 private int index;
853 public NNEnumerator (GraphNodeEmit gn)
854 {
855 this.gn = gn;
856 }
857 public override bool MoveNext ()
858 {
859 switch (index) {
860 case 0: {
861 if (gn.CanFallThrough ()) {
862 index ++;
863 nn = gn.nextLin;
864 return nn != null;
865 }
866 return false;
867 }
868 case 1: {
869 nn = null;
870 return false;
871 }
872 }
873 throw new Exception ();
874 }
875 public override void Reset ()
876 {
877 index = 0;
878 nn = null;
879 }
880 }
881 }
882
883 public class GraphNodeEmitNull : GraphNodeEmit {
884 public GraphNodeEmitNull (ScriptCollector coll, Token errorAt, OpCode opcode) : base (coll, errorAt, opcode)
885 { }
886
887 public override void ChainLin ()
888 {
889 base.ChainLin ();
890
891 switch (opcode.ToString ()) {
892 case "nop": break;
893 case "break": break;
894 case "volatile.": break;
895 case "ldarg.0": coll.stackDepth.Push (coll.wrapped.argTypes[0]); break;
896 case "ldarg.1": coll.stackDepth.Push (coll.wrapped.argTypes[1]); break;
897 case "ldarg.2": coll.stackDepth.Push (coll.wrapped.argTypes[2]); break;
898 case "ldarg.3": coll.stackDepth.Push (coll.wrapped.argTypes[3]); break;
899 case "ldnull": coll.stackDepth.Push (null); break;
900 case "ldc.i4.m1":
901 case "ldc.i4.0":
902 case "ldc.i4.1":
903 case "ldc.i4.2":
904 case "ldc.i4.3":
905 case "ldc.i4.4":
906 case "ldc.i4.5":
907 case "ldc.i4.6":
908 case "ldc.i4.7":
909 case "ldc.i4.8": {
910 coll.stackDepth.Push (typeof (int));
911 break;
912 }
913 case "dup": {
914 Type t = coll.stackDepth.Peek (0);
915 bool b = coll.stackDepth.PeekBoxed (0);
916 coll.stackDepth.Push (t, b);
917 break;
918 }
919 case "pop": {
920 coll.stackDepth.Pop (1);
921 break;
922 }
923 case "ret": {
924 int sd = (coll.wrapped.retType != typeof (void)) ? 1 : 0;
925 if (coll.stackDepth.Count != sd) throw new Exception ("bad stack depth");
926 if (sd > 0) {
927 coll.stackDepth.Pop (coll.wrapped.retType);
928 }
929 break;
930 }
931 case "add":
932 case "sub":
933 case "mul":
934 case "div":
935 case "div.un":
936 case "rem":
937 case "rem.un":
938 case "and":
939 case "or":
940 case "xor":
941 case "shl":
942 case "shr":
943 case "shr.un":
944 case "add.ovf":
945 case "add.ovf.un":
946 case "mul.ovf":
947 case "mul.ovf.un":
948 case "sub.ovf":
949 case "sub.ovf.un": {
950 coll.stackDepth.PopNumVal ();
951 Type t = coll.stackDepth.PopNumVal ();
952 coll.stackDepth.Push (t);
953 break;
954 }
955 case "neg":
956 case "not": {
957 Type t = coll.stackDepth.PopNumVal ();
958 coll.stackDepth.Push (t);
959 break;
960 }
961 case "conv.i1":
962 case "conv.i2":
963 case "conv.i4":
964 case "conv.i8":
965 case "conv.r4":
966 case "conv.r8":
967 case "conv.u4":
968 case "conv.u8":
969 case "conv.r.un":
970 case "conv.ovf.i1.un":
971 case "conv.ovf.i2.un":
972 case "conv.ovf.i4.un":
973 case "conv.ovf.i8.un":
974 case "conv.ovf.u1.un":
975 case "conv.ovf.u2.un":
976 case "conv.ovf.u4.un":
977 case "conv.ovf.u8.un":
978 case "conv.ovf.i.un":
979 case "conv.ovf.u.un":
980 case "conv.ovf.i1":
981 case "conv.ovf.u1":
982 case "conv.ovf.i2":
983 case "conv.ovf.u2":
984 case "conv.ovf.i4":
985 case "conv.ovf.u4":
986 case "conv.ovf.i8":
987 case "conv.ovf.u8":
988 case "conv.u2":
989 case "conv.u1":
990 case "conv.i":
991 case "conv.ovf.i":
992 case "conv.ovf.u":
993 case "conv.u": {
994 coll.stackDepth.PopNumVal ();
995 coll.stackDepth.Push (ConvToType (opcode));
996 break;
997 }
998 case "throw": {
999 if (coll.stackDepth.Count != 1) throw new Exception ("bad stack depth " + coll.stackDepth.Count);
1000 coll.stackDepth.PopRef ();
1001 break;
1002 }
1003 case "ldlen": {
1004 coll.stackDepth.Pop (typeof (string));
1005 coll.stackDepth.Push (typeof (int));
1006 break;
1007 }
1008 case "ldelem.i1":
1009 case "ldelem.u1":
1010 case "ldelem.i2":
1011 case "ldelem.u2":
1012 case "ldelem.i4":
1013 case "ldelem.u4":
1014 case "ldelem.i8":
1015 case "ldelem.i":
1016 case "ldelem.r4":
1017 case "ldelem.r8":
1018 case "ldelem.ref": {
1019 Type t = coll.stackDepth.Peek (1).GetElementType ();
1020 coll.stackDepth.Pop (typeof (int));
1021 coll.stackDepth.Pop (t.MakeArrayType ());
1022 coll.stackDepth.Push (t);
1023 break;
1024 }
1025 case "stelem.i":
1026 case "stelem.i1":
1027 case "stelem.i2":
1028 case "stelem.i4":
1029 case "stelem.i8":
1030 case "stelem.r4":
1031 case "stelem.r8":
1032 case "stelem.ref": {
1033 Type t = coll.stackDepth.Peek (2).GetElementType ();
1034 coll.stackDepth.Pop (t);
1035 coll.stackDepth.Pop (typeof (int));
1036 coll.stackDepth.Pop (t.MakeArrayType ());
1037 break;
1038 }
1039 case "endfinally":
1040 case "rethrow": {
1041 if (coll.stackDepth.Count != 0) throw new Exception ("bad stack depth " + coll.stackDepth.Count);
1042 break;
1043 }
1044 case "ceq": {
1045 Type t = coll.stackDepth.Pop (1);
1046 if (t == null) {
1047 coll.stackDepth.PopRef ();
1048 } else {
1049 coll.stackDepth.Pop (t);
1050 }
1051 coll.stackDepth.Push (typeof (int));
1052 break;
1053 }
1054 case "cgt":
1055 case "cgt.un":
1056 case "clt":
1057 case "clt.un": {
1058 coll.stackDepth.PopNumVal ();
1059 coll.stackDepth.PopNumVal ();
1060 coll.stackDepth.Push (typeof (int));
1061 break;
1062 }
1063 case "ldind.i4": {
1064 coll.stackDepth.Pop (typeof (int).MakeByRefType ());
1065 coll.stackDepth.Push (typeof (int));
1066 break;
1067 }
1068 case "stind.i4": {
1069 coll.stackDepth.Pop (typeof (int));
1070 coll.stackDepth.Pop (typeof (int).MakeByRefType ());
1071 break;
1072 }
1073 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1074 }
1075 }
1076
1077 private static Type ConvToType (OpCode opcode)
1078 {
1079 string s = opcode.ToString ();
1080 s = s.Substring (5); // strip off "conv."
1081 if (s.StartsWith ("ovf.")) s = s.Substring (4);
1082 if (s.EndsWith (".un")) s = s.Substring (0, s.Length - 3);
1083
1084 switch (s) {
1085 case "i": return typeof (IntPtr);
1086 case "i1": return typeof (sbyte);
1087 case "i2": return typeof (short);
1088 case "i4": return typeof (int);
1089 case "i8": return typeof (long);
1090 case "r":
1091 case "r4": return typeof (float);
1092 case "r8": return typeof (double);
1093 case "u1": return typeof (byte);
1094 case "u2": return typeof (ushort);
1095 case "u4": return typeof (uint);
1096 case "u8": return typeof (ulong);
1097 case "u": return typeof (UIntPtr);
1098 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1099 }
1100 }
1101
1102 public override void WriteOutOne (ScriptMyILGen ilGen)
1103 {
1104 ilGen.Emit (errorAt, opcode);
1105 }
1106 }
1107
1108 public class GraphNodeEmitNullEndfinally : GraphNodeEmitNull {
1109 public GraphNodeEmitNullEndfinally (ScriptCollector coll, Token errorAt) : base (coll, errorAt, OpCodes.Endfinally)
1110 { }
1111
1112 /**
1113 * @brief Endfinally can branch to:
1114 * 1) the corresponding EndExceptionBlock
1115 * 2) any of the corresponding BeginFinallyBlock's leaveTargets
1116 * 3) the next outer BeginFinallyBlock
1117 */
1118 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
1119 {
1120 return new NNEnumerable (this, typeof (NNEnumerator));
1121 }
1122
1123 private class NNEnumerator : NNEnumeratorBase {
1124 private GraphNodeEmitNullEndfinally gn;
1125 private IEnumerator<GraphNodeBlock> leaveTargetEnumerator;
1126 private int index;
1127 public NNEnumerator (GraphNodeEmitNullEndfinally gn)
1128 {
1129 this.gn = gn;
1130
1131 // endfinally instruction must be within some try/catch/finally mess
1132 GraphNodeBeginExceptionBlock thistry = gn.excBlock;
1133
1134 // endfinally instruction must be within some finally { } mess
1135 GraphNodeBeginFinallyBlock thisfin = (GraphNodeBeginFinallyBlock)thistry.catchFinallyBlock;
1136
1137 // get the list of the finally { } leave instruction targets
1138 this.leaveTargetEnumerator = thisfin.leaveTargets.GetEnumerator ();
1139 }
1140 public override bool MoveNext ()
1141 {
1142 while (true) {
1143 switch (index) {
1144
1145 // to start, return end of our finally { }
1146 case 0: {
1147 GraphNodeBeginExceptionBlock thistry = gn.excBlock;
1148 nn = thistry.endExcBlock;
1149 if (nn == null) throw new NullReferenceException ("thistry.endExcBlock");
1150 index ++;
1151 return true;
1152 }
1153
1154 // return next one of our finally { }'s leave targets
1155 // ie, where any leave instructions in the try { } want
1156 // the finally { } to go to when it finishes
1157 case 1: {
1158 if (this.leaveTargetEnumerator.MoveNext ()) {
1159 nn = this.leaveTargetEnumerator.Current;
1160 if (nn == null) throw new NullReferenceException ("this.leaveTargetEnumerator.Current");
1161 return true;
1162 }
1163 break;
1164 }
1165
1166 // return beginning of next outer finally { }
1167 case 2: {
1168 GraphNodeBeginExceptionBlock nntry = gn.excBlock;
1169 while ((nntry = nntry.excBlock) != null) {
1170 if (nntry.catchFinallyBlock is GraphNodeBeginFinallyBlock) {
1171 nn = nntry.catchFinallyBlock;
1172 if (nn == null) throw new NullReferenceException ("nntry.catchFinallyBlock");
1173 index ++;
1174 return true;
1175 }
1176 }
1177 break;
1178 }
1179
1180 // got nothing more
1181 case 3: {
1182 return false;
1183 }
1184
1185 default: throw new Exception ();
1186 }
1187 index ++;
1188 }
1189 }
1190 public override void Reset ()
1191 {
1192 leaveTargetEnumerator.Reset ();
1193 index = 0;
1194 nn = null;
1195 }
1196 }
1197 }
1198
1199 public class GraphNodeEmitField : GraphNodeEmit {
1200 public FieldInfo field;
1201
1202 public GraphNodeEmitField (ScriptCollector coll, Token errorAt, OpCode opcode, FieldInfo field) : base (coll, errorAt, opcode)
1203 {
1204 this.field = field;
1205 }
1206
1207 public override void ChainLin ()
1208 {
1209 base.ChainLin ();
1210
1211 switch (opcode.ToString ()) {
1212 case "ldfld": PopPointer (); coll.stackDepth.Push (field.FieldType); break;
1213 case "ldflda": PopPointer (); coll.stackDepth.Push (field.FieldType.MakeByRefType ()); break;
1214 case "stfld": coll.stackDepth.Pop (field.FieldType); PopPointer (); break;
1215 case "ldsfld": coll.stackDepth.Push (field.FieldType); break;
1216 case "ldsflda": coll.stackDepth.Push (field.FieldType.MakeByRefType ()); break;
1217 case "stsfld": coll.stackDepth.Pop (field.FieldType); break;
1218 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1219 }
1220 }
1221 private void PopPointer ()
1222 {
1223 Type t = field.DeclaringType; // get class/field type
1224 if (t.IsValueType) {
1225 Type brt = t.MakeByRefType (); // if value type, eg Vector, it can be pushed by reference or by value
1226 int c = coll.stackDepth.Count;
1227 if ((c > 0) && (coll.stackDepth[c-1] == brt)) t = brt;
1228 }
1229 coll.stackDepth.Pop (t); // type of what should be on the stack pointing to object or struct
1230 }
1231
1232 public override void DebString (StringBuilder sb)
1233 {
1234 base.DebString (sb);
1235 sb.Append (field.Name);
1236 }
1237
1238 public override void WriteOutOne (ScriptMyILGen ilGen)
1239 {
1240 ilGen.Emit (errorAt, opcode, field);
1241 }
1242 }
1243
1244 public class GraphNodeEmitLocal : GraphNodeEmit {
1245 public ScriptMyLocal myLocal;
1246
1247 public GraphNodeEmitLocal (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptMyLocal myLocal) : base (coll, errorAt, opcode)
1248 {
1249 this.myLocal = myLocal;
1250 }
1251
1252 public override void ChainLin ()
1253 {
1254 base.ChainLin ();
1255
1256 switch (opcode.ToString ()) {
1257 case "ldloc": coll.stackDepth.Push (myLocal.type); break;
1258 case "ldloca": coll.stackDepth.Push (myLocal.type.MakeByRefType ()); break;
1259 case "stloc": coll.stackDepth.Pop (myLocal.type); break;
1260 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1261 }
1262 }
1263
1264 public override void DebString (StringBuilder sb)
1265 {
1266 base.DebString (sb);
1267 sb.Append (myLocal.name);
1268 }
1269
1270 public override ScriptMyLocal ReadsLocal ()
1271 {
1272 if (opcode == OpCodes.Ldloc) return myLocal;
1273 if (opcode == OpCodes.Ldloca) return myLocal;
1274 if (opcode == OpCodes.Stloc) return null;
1275 throw new Exception ("unknown opcode " + opcode);
1276 }
1277 public override ScriptMyLocal WritesLocal ()
1278 {
1279 if (opcode == OpCodes.Ldloc) return null;
1280 if (opcode == OpCodes.Ldloca) return myLocal;
1281 if (opcode == OpCodes.Stloc) return myLocal;
1282 throw new Exception ("unknown opcode " + opcode);
1283 }
1284
1285 public override void WriteOutOne (ScriptMyILGen ilGen)
1286 {
1287 ilGen.Emit (errorAt, opcode, myLocal);
1288 }
1289 }
1290
1291 public class GraphNodeEmitType : GraphNodeEmit {
1292 public Type type;
1293
1294 public GraphNodeEmitType (ScriptCollector coll, Token errorAt, OpCode opcode, Type type) : base (coll, errorAt, opcode)
1295 {
1296 this.type = type;
1297 }
1298
1299 public override void ChainLin ()
1300 {
1301 base.ChainLin ();
1302
1303 switch (opcode.ToString ()) {
1304 case "castclass":
1305 case "isinst": {
1306 coll.stackDepth.PopRef ();
1307 coll.stackDepth.Push (type, type.IsValueType);
1308 break;
1309 }
1310 case "box": {
1311 if (!type.IsValueType) throw new Exception ("can't box a non-value type");
1312 coll.stackDepth.Pop (type);
1313 coll.stackDepth.Push (type, true);
1314 break;
1315 }
1316 case "unbox":
1317 case "unbox.any": {
1318 if (!type.IsValueType) throw new Exception ("can't unbox to a non-value type");
1319 coll.stackDepth.PopRef ();
1320 coll.stackDepth.Push (type);
1321 break;
1322 }
1323 case "newarr": {
1324 coll.stackDepth.Pop (typeof (int));
1325 coll.stackDepth.Push (type.MakeArrayType ());
1326 break;
1327 }
1328 case "sizeof": {
1329 coll.stackDepth.Pop (1);
1330 coll.stackDepth.Push (typeof (int));
1331 break;
1332 }
1333 case "ldelem": {
1334 coll.stackDepth.Pop (typeof (int));
1335 coll.stackDepth.Pop (type.MakeArrayType ());
1336 coll.stackDepth.Push (type);
1337 break;
1338 }
1339 case "ldelema": {
1340 coll.stackDepth.Pop (typeof (int));
1341 coll.stackDepth.Pop (type.MakeArrayType ());
1342 coll.stackDepth.Push (type.MakeByRefType ());
1343 break;
1344 }
1345 case "stelem": {
1346 coll.stackDepth.Pop (type);
1347 coll.stackDepth.Pop (typeof (int));
1348 coll.stackDepth.Pop (type.MakeArrayType ());
1349 break;
1350 }
1351 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1352 }
1353 }
1354
1355 public override void DebString (StringBuilder sb)
1356 {
1357 base.DebString (sb);
1358 sb.Append (type.Name);
1359 }
1360
1361 public override void WriteOutOne (ScriptMyILGen ilGen)
1362 {
1363 ilGen.Emit (errorAt, opcode, type);
1364 }
1365 }
1366
1367 public class GraphNodeEmitLabel : GraphNodeEmit {
1368 public ScriptMyLabel myLabel;
1369
1370 public GraphNodeEmitLabel (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptMyLabel myLabel) : base (coll, errorAt, opcode)
1371 {
1372 this.myLabel = myLabel;
1373 }
1374
1375 public override void ChainLin ()
1376 {
1377 base.ChainLin ();
1378
1379 switch (opcode.ToString ()) {
1380 case "brfalse.s":
1381 case "brtrue.s":
1382 case "brfalse":
1383 case "brtrue": {
1384 coll.stackDepth.Pop (1);
1385 break;
1386 }
1387 case "beq.s":
1388 case "bge.s":
1389 case "bgt.s":
1390 case "ble.s":
1391 case "blt.s":
1392 case "bne.un.s":
1393 case "bge.un.s":
1394 case "bgt.un.s":
1395 case "ble.un.s":
1396 case "blt.un.s":
1397 case "beq":
1398 case "bge":
1399 case "bgt":
1400 case "ble":
1401 case "blt":
1402 case "bne.un":
1403 case "bge.un":
1404 case "bgt.un":
1405 case "ble.un":
1406 case "blt.un": {
1407 coll.stackDepth.PopNumVal ();
1408 coll.stackDepth.PopNumVal ();
1409 break;
1410 }
1411 case "br":
1412 case "br.s": break;
1413 case "leave": {
1414 if (coll.stackDepth.Count != 0) throw new Exception ("bad stack depth " + coll.stackDepth.Count);
1415 break;
1416 }
1417 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1418 }
1419
1420 // if a target doesn't have a depth yet, set its depth to the depth after instruction executes
1421 // otherwise, make sure it matches all other branches to that target and what fell through to it
1422 coll.stackDepth.Matches (myLabel);
1423 }
1424
1425 public override void DebString (StringBuilder sb)
1426 {
1427 base.DebString (sb);
1428 sb.Append (myLabel.name);
1429 }
1430
1431 public override void WriteOutOne (ScriptMyILGen ilGen)
1432 {
1433 ilGen.Emit (errorAt, opcode, myLabel);
1434 }
1435
1436 /**
1437 * @brief Conditional branches return the next inline followed by the branch target
1438 * Unconditional branches return only the branch target
1439 * But if the target is outside our scope (eg __retlbl), omit it from the list
1440 */
1441 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
1442 {
1443 return new NNEnumerable (this, typeof (NNEnumerator));
1444 }
1445
1446 private class NNEnumerator : NNEnumeratorBase {
1447 private GraphNodeEmitLabel gn;
1448 private int index;
1449 public NNEnumerator (GraphNodeEmitLabel gn)
1450 {
1451 this.gn = gn;
1452 }
1453 public override bool MoveNext ()
1454 {
1455 switch (gn.opcode.FlowControl) {
1456 case FlowControl.Branch: {
1457 // unconditional branch just goes to target and nothing else
1458 switch (index) {
1459 case 0: {
1460 nn = gn.myLabel.whereAmI;
1461 index ++;
1462 return nn != null;
1463 }
1464 case 1: {
1465 return false;
1466 }
1467 }
1468 throw new Exception ();
1469 }
1470 case FlowControl.Cond_Branch: {
1471 // conditional branch goes inline and to target
1472 switch (index) {
1473 case 0: {
1474 nn = gn.nextLin;
1475 index ++;
1476 return true;
1477 }
1478 case 1: {
1479 nn = gn.myLabel.whereAmI;
1480 index ++;
1481 return nn != null;
1482 }
1483 case 2: {
1484 return false;
1485 }
1486 }
1487 throw new Exception ();
1488 }
1489 default: throw new Exception ("unknown flow control " + gn.opcode.FlowControl.ToString () +
1490 " of " + gn.opcode.ToString ());
1491 }
1492 }
1493 public override void Reset ()
1494 {
1495 index = 0;
1496 nn = null;
1497 }
1498 }
1499 }
1500
1501 public class GraphNodeEmitLabelLeave : GraphNodeEmitLabel {
1502 public GraphNodeBlock unwindTo; // if unwinding, innermost finally block being unwound
1503 // else, same as myTarget.whereAmI
1504 // null if unwinding completely out of scope, eg, __retlbl
1505
1506 public GraphNodeEmitLabelLeave (ScriptCollector coll, Token errorAt, ScriptMyLabel myLabel) : base (coll, errorAt, OpCodes.Leave, myLabel)
1507 { }
1508
1509 /**
1510 * @brief Leave instructions have exactly one unconditional next node.
1511 * Either the given target if within the same try block
1512 * or the beginning of the intervening finally block.
1513 */
1514 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
1515 {
1516 return new NNEnumerable (this, typeof (NNEnumerator));
1517 }
1518
1519 private class NNEnumerator : NNEnumeratorBase {
1520 private GraphNodeEmitLabelLeave gn;
1521 private int index;
1522 public NNEnumerator (GraphNodeEmitLabelLeave gn)
1523 {
1524 this.gn = gn;
1525 }
1526 public override bool MoveNext ()
1527 {
1528 if (index == 0) {
1529 nn = gn.unwindTo;
1530 index ++;
1531 return nn != null;
1532 }
1533 nn = null;
1534 return false;
1535 }
1536 public override void Reset ()
1537 {
1538 index = 0;
1539 nn = null;
1540 }
1541 }
1542 }
1543
1544 public class GraphNodeEmitLabels : GraphNodeEmit {
1545 public ScriptMyLabel[] myLabels;
1546
1547 public GraphNodeEmitLabels (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels) : base (coll, errorAt, opcode)
1548 {
1549 this.myLabels = myLabels;
1550 }
1551
1552 public override void ChainLin ()
1553 {
1554 base.ChainLin ();
1555
1556 switch (opcode.ToString ()) {
1557 case "switch": {
1558 coll.stackDepth.Pop (typeof (int));
1559 break;
1560 }
1561 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1562 }
1563
1564 // if a target doesn't have a depth yet, set its depth to the depth after instruction executes
1565 // otherwise, make sure it matches all other branches to that target and what fell through to it
1566 foreach (ScriptMyLabel myLabel in myLabels) {
1567 coll.stackDepth.Matches (myLabel);
1568 }
1569 }
1570
1571 public override void DebString (StringBuilder sb)
1572 {
1573 base.DebString (sb);
1574 bool first = true;
1575 foreach (ScriptMyLabel lbl in myLabels) {
1576 if (!first) sb.Append (',');
1577 sb.Append (lbl.name);
1578 first = false;
1579 }
1580 }
1581
1582 public override void WriteOutOne (ScriptMyILGen ilGen)
1583 {
1584 ilGen.Emit (errorAt, opcode, myLabels);
1585 }
1586
1587 /**
1588 * @brief Return list of all labels followed by the next linear instruction
1589 * But if the target is outside our scope (eg __retlbl), omit it from the list
1590 */
1591 protected override System.Collections.Generic.IEnumerable<GraphNode> GetNNEnumerable ()
1592 {
1593 return new NNEnumerable (this, typeof (NNEnumerator));
1594 }
1595
1596 private class NNEnumerator : NNEnumeratorBase {
1597 private GraphNodeEmitLabels gn;
1598 private int index;
1599 public NNEnumerator (GraphNodeEmitLabels gn)
1600 {
1601 this.gn = gn;
1602 }
1603 public override bool MoveNext ()
1604 {
1605 /*
1606 * Return next from list of switch case labels.
1607 */
1608 while (index < gn.myLabels.Length) {
1609 nn = gn.myLabels[index++].whereAmI;
1610 if (nn != null) return true;
1611 }
1612
1613 /*
1614 * If all ran out, the switch instruction falls through.
1615 */
1616 if (index == gn.myLabels.Length) {
1617 index ++;
1618 nn = gn.nextLin;
1619 return true;
1620 }
1621
1622 /*
1623 * Even ran out of that, say there's nothing more.
1624 */
1625 nn = null;
1626 return false;
1627 }
1628 public override void Reset ()
1629 {
1630 index = 0;
1631 nn = null;
1632 }
1633 }
1634 }
1635
1636 public class GraphNodeEmitIntMeth : GraphNodeEmit {
1637 public ScriptObjWriter method;
1638
1639 public GraphNodeEmitIntMeth (ScriptCollector coll, Token errorAt, OpCode opcode, ScriptObjWriter method) : base (coll, errorAt, opcode)
1640 {
1641 this.method = method;
1642 }
1643
1644 public override void ChainLin ()
1645 {
1646 base.ChainLin ();
1647
1648 switch (opcode.ToString ()) {
1649 case "call": {
1650
1651 // calls have Varpop so pop the number of arguments
1652 // they are all static so there is no separate 'this' parameter
1653 coll.stackDepth.Pop (this.method.argTypes);
1654
1655 // calls are also Varpush so they push a return value iff non-void
1656 if (this.method.retType != typeof (void)) coll.stackDepth.Push (this.method.retType);
1657 break;
1658 }
1659
1660 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1661 }
1662 }
1663
1664 public override void DebString (StringBuilder sb)
1665 {
1666 base.DebString (sb);
1667 sb.Append (method.methName);
1668 }
1669
1670 public override void WriteOutOne (ScriptMyILGen ilGen)
1671 {
1672 ilGen.Emit (errorAt, opcode, method);
1673 }
1674 }
1675
1676 public class GraphNodeEmitExtMeth : GraphNodeEmit {
1677 public MethodInfo method;
1678
1679 public GraphNodeEmitExtMeth (ScriptCollector coll, Token errorAt, OpCode opcode, MethodInfo method) : base (coll, errorAt, opcode)
1680 {
1681 this.method = method;
1682 }
1683
1684 public override void ChainLin ()
1685 {
1686 base.ChainLin ();
1687
1688 switch (opcode.ToString ()) {
1689 case "call":
1690 case "callvirt": {
1691
1692 // calls have Varpop so pop the number of arguments
1693 coll.stackDepth.Pop (this.method.GetParameters ());
1694 if ((this.method.CallingConvention & CallingConventions.HasThis) != 0) {
1695 coll.stackDepth.Pop (method.DeclaringType);
1696 }
1697
1698 // calls are also Varpush so they push a return value iff non-void
1699 if (this.method.ReturnType != typeof (void)) coll.stackDepth.Push (this.method.ReturnType);
1700 break;
1701 }
1702
1703 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1704 }
1705 }
1706
1707 public override void DebString (StringBuilder sb)
1708 {
1709 base.DebString (sb);
1710 sb.Append (method.Name);
1711 }
1712
1713 public override void WriteOutOne (ScriptMyILGen ilGen)
1714 {
1715 ilGen.Emit (errorAt, opcode, method);
1716 }
1717 }
1718
1719 public class GraphNodeEmitCtor : GraphNodeEmit {
1720 public ConstructorInfo ctor;
1721
1722 public GraphNodeEmitCtor (ScriptCollector coll, Token errorAt, OpCode opcode, ConstructorInfo ctor) : base (coll, errorAt, opcode)
1723 {
1724 this.ctor = ctor;
1725 }
1726
1727 public override void ChainLin ()
1728 {
1729 base.ChainLin ();
1730
1731 switch (opcode.ToString ()) {
1732 case "newobj": {
1733 coll.stackDepth.Pop (ctor.GetParameters ());
1734 coll.stackDepth.Push (ctor.DeclaringType);
1735 break;
1736 }
1737
1738 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1739 }
1740 }
1741
1742 public override void DebString (StringBuilder sb)
1743 {
1744 base.DebString (sb);
1745 sb.Append (ctor.ReflectedType.Name);
1746 }
1747
1748 public override void WriteOutOne (ScriptMyILGen ilGen)
1749 {
1750 ilGen.Emit (errorAt, opcode, ctor);
1751 }
1752 }
1753
1754 public class GraphNodeEmitDouble : GraphNodeEmit {
1755 public double value;
1756
1757 public GraphNodeEmitDouble (ScriptCollector coll, Token errorAt, OpCode opcode, double value) : base (coll, errorAt, opcode)
1758 {
1759 this.value = value;
1760 }
1761
1762 public override void ChainLin ()
1763 {
1764 base.ChainLin ();
1765
1766 switch (opcode.ToString ()) {
1767 case "ldc.r8": coll.stackDepth.Push (typeof (double)); break;
1768 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1769 }
1770 }
1771
1772 public override void DebString (StringBuilder sb)
1773 {
1774 base.DebString (sb);
1775 sb.Append (value);
1776 }
1777
1778 public override void WriteOutOne (ScriptMyILGen ilGen)
1779 {
1780 ilGen.Emit (errorAt, opcode, value);
1781 }
1782 }
1783
1784 public class GraphNodeEmitFloat : GraphNodeEmit {
1785 public float value;
1786
1787 public GraphNodeEmitFloat (ScriptCollector coll, Token errorAt, OpCode opcode, float value) : base (coll, errorAt, opcode)
1788 {
1789 this.value = value;
1790 }
1791
1792 public override void ChainLin ()
1793 {
1794 base.ChainLin ();
1795
1796 switch (opcode.ToString ()) {
1797 case "ldc.r4": coll.stackDepth.Push (typeof (float)); break;
1798 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1799 }
1800 }
1801
1802 public override void DebString (StringBuilder sb)
1803 {
1804 base.DebString (sb);
1805 sb.Append (value);
1806 }
1807
1808 public override void WriteOutOne (ScriptMyILGen ilGen)
1809 {
1810 ilGen.Emit (errorAt, opcode, value);
1811 }
1812 }
1813
1814 public class GraphNodeEmitInt : GraphNodeEmit {
1815 public int value;
1816
1817 public GraphNodeEmitInt (ScriptCollector coll, Token errorAt, OpCode opcode, int value) : base (coll, errorAt, opcode)
1818 {
1819 this.value = value;
1820 }
1821
1822 public override void ChainLin ()
1823 {
1824 base.ChainLin ();
1825
1826 switch (opcode.ToString ()) {
1827 case "ldarg":
1828 case "ldarg.s": coll.stackDepth.Push (coll.wrapped.argTypes[value]); break;
1829 case "ldarga":
1830 case "ldarga.s": coll.stackDepth.Push (coll.wrapped.argTypes[value].MakeByRefType ()); break;
1831 case "starg":
1832 case "starg.s": coll.stackDepth.Pop (coll.wrapped.argTypes[value]); break;
1833 case "ldc.i4":
1834 case "ldc.i4.s": coll.stackDepth.Push (typeof (int)); break;
1835 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1836 }
1837 }
1838
1839 public override void DebString (StringBuilder sb)
1840 {
1841 base.DebString (sb);
1842 sb.Append (value);
1843 }
1844
1845 public override void WriteOutOne (ScriptMyILGen ilGen)
1846 {
1847 ilGen.Emit (errorAt, opcode, value);
1848 }
1849 }
1850
1851 public class GraphNodeEmitString : GraphNodeEmit {
1852 public string value;
1853
1854 public GraphNodeEmitString (ScriptCollector coll, Token errorAt, OpCode opcode, string value) : base (coll, errorAt, opcode)
1855 {
1856 this.value = value;
1857 }
1858
1859 public override void ChainLin ()
1860 {
1861 base.ChainLin ();
1862
1863 switch (opcode.ToString ()) {
1864 case "ldstr": coll.stackDepth.Push (typeof (string)); break;
1865 default: throw new Exception ("unknown opcode " + opcode.ToString ());
1866 }
1867 }
1868
1869 public override void DebString (StringBuilder sb)
1870 {
1871 base.DebString (sb);
1872 sb.Append ("\"");
1873 sb.Append (value);
1874 sb.Append ("\"");
1875 }
1876
1877 public override void WriteOutOne (ScriptMyILGen ilGen)
1878 {
1879 ilGen.Emit (errorAt, opcode, value);
1880 }
1881 }
1882
1883 public class GraphNodeMarkLabel : GraphNodeBlock {
1884 public ScriptMyLabel myLabel;
1885
1886 public GraphNodeMarkLabel (ScriptCollector coll, ScriptMyLabel myLabel) : base (coll)
1887 {
1888 this.myLabel = myLabel;
1889 }
1890
1891 public override void ChainLin ()
1892 {
1893 base.ChainLin ();
1894
1895 // if previous instruction can fall through to this label,
1896 // if the label doesn't yet have a stack depth, mark it with current stack depth
1897 // else, the label's stack depth from forward branches and current stack depth must match
1898 // else,
1899 // label must have had a forward branch to it so we can know stack depth
1900 // set the current stack depth to the label's stack depth as of that forward branch
1901 if (myLabel.whereAmI.prevLin.CanFallThrough ()) {
1902 coll.stackDepth.Matches (myLabel);
1903 } else {
1904 if (myLabel.stackDepth == null) {
1905 throw new Exception ("stack depth unknown at " + myLabel.name);
1906 }
1907 coll.stackDepth.Clear ();
1908 int n = myLabel.stackDepth.Length;
1909 for (int i = 0; i < n; i ++) {
1910 coll.stackDepth.Push (myLabel.stackDepth[i], myLabel.stackBoxeds[i]);
1911 }
1912 }
1913 }
1914
1915 public override void DebString (StringBuilder sb)
1916 {
1917 sb.Append (myLabel.name);
1918 sb.Append (':');
1919 if (myLabel.stackDepth != null) {
1920 sb.Append (" [");
1921 sb.Append (myLabel.stackDepth.Length);
1922 sb.Append (']');
1923 }
1924 }
1925
1926 public override void WriteOutOne (ScriptMyILGen ilGen)
1927 {
1928 ilGen.MarkLabel (myLabel);
1929 }
1930 }
1931
1932
1933 /**
1934 * @brief Generates enumerator that steps through list of nodes that can
1935 * possibly be next in a flow-control sense.
1936 */
1937 public class NNEnumerable : System.Collections.Generic.IEnumerable<GraphNode> {
1938 private object[] cps;
1939 private ConstructorInfo ci;
1940
1941 public NNEnumerable (GraphNode gn, Type nnEnumeratorType)
1942 {
1943 this.cps = new object[] { gn };
1944 this.ci = nnEnumeratorType.GetConstructor (new Type[] { gn.GetType () });
1945 }
1946 System.Collections.Generic.IEnumerator<GraphNode> System.Collections.Generic.IEnumerable<GraphNode>.GetEnumerator ()
1947 {
1948 return (System.Collections.Generic.IEnumerator<GraphNode>) ci.Invoke (cps);
1949 }
1950 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
1951 {
1952 return (System.Collections.IEnumerator) ci.Invoke (cps);
1953 }
1954 }
1955
1956
1957 /**
1958 * @brief Steps through list of nodes that can possible be next in a flow-control sense.
1959 */
1960 public abstract class NNEnumeratorBase : System.Collections.Generic.IEnumerator<GraphNode> {
1961 protected GraphNode nn;
1962
1963 public abstract bool MoveNext ();
1964 public abstract void Reset ();
1965
1966 GraphNode System.Collections.Generic.IEnumerator<GraphNode>.Current {
1967 get { return this.nn; }
1968 }
1969 object System.Collections.IEnumerator.Current {
1970 get { return this.nn; }
1971 }
1972 void System.IDisposable.Dispose() { }
1973 }
1974
1975
1976 public class ScriptCollector : ScriptMyILGen {
1977 public static readonly bool DEBUG = false;
1978
1979 public ScriptObjWriter wrapped;
1980 public GraphNode firstLin, lastLin;
1981 private bool resolvedSomething;
1982 private int resolveSequence;
1983 private int excBlkSeqNos;
1984 public StackDepth stackDepth = new StackDepth ();
1985
1986 public GraphNodeBeginExceptionBlock curTryBlock = null; // pushed at beginning of try
1987 // popped at BEGINNING of catch/finally
1988 public GraphNodeBeginExceptionBlock curExcBlock = null; // pushed at beginning of try
1989 // popped at END of catch/finally
1990
1991 private List<ScriptMyLocal> declaredLocals = new List<ScriptMyLocal> ();
1992 private List<ScriptMyLabel> definedLabels = new List<ScriptMyLabel> ();
1993
1994 public string methName { get { return wrapped.methName; } }
1995
1996 /**
1997 * @brief Wrap the optimizer around the ScriptObjWriter to collect the instruction stream.
1998 * All stream-writing calls get saved to our graph nodes instead of being written to object file.
1999 */
2000 public ScriptCollector (ScriptObjWriter wrapped)
2001 {
2002 this.wrapped = wrapped;
2003 GraphNodeBegin gnb = new GraphNodeBegin (this);
2004 this.firstLin = gnb;
2005 this.lastLin = gnb;
2006 }
2007
2008 public ScriptMyLocal DeclareLocal (Type type, string name)
2009 {
2010 ScriptMyLocal loc = new ScriptMyLocal ();
2011 loc.name = name;
2012 loc.type = type;
2013 loc.number = wrapped.localNumber ++;
2014 declaredLocals.Add (loc);
2015 return loc;
2016 }
2017
2018 public ScriptMyLabel DefineLabel (string name)
2019 {
2020 ScriptMyLabel lbl = new ScriptMyLabel ();
2021 lbl.name = name;
2022 lbl.number = wrapped.labelNumber ++;
2023 definedLabels.Add (lbl);
2024 return lbl;
2025 }
2026
2027 public void BeginExceptionBlock ()
2028 {
2029 GraphNodeBeginExceptionBlock tryBlock = new GraphNodeBeginExceptionBlock (this);
2030 tryBlock.ChainLin ();
2031 tryBlock.excBlkSeqNo = ++ this.excBlkSeqNos;
2032 this.curExcBlock = tryBlock;
2033 this.curTryBlock = tryBlock;
2034 }
2035
2036 public void BeginCatchBlock (Type excType)
2037 {
2038 GraphNodeBeginCatchBlock catchBlock = new GraphNodeBeginCatchBlock (this, excType);
2039 catchBlock.ChainLin ();
2040 if (curExcBlock.catchFinallyBlock != null) throw new Exception ("only one catch/finally allowed per try");
2041 curExcBlock.catchFinallyBlock = catchBlock;
2042 curTryBlock = curExcBlock.tryBlock;
2043 }
2044
2045 public void BeginFinallyBlock ()
2046 {
2047 GraphNodeBeginFinallyBlock finallyBlock = new GraphNodeBeginFinallyBlock (this);
2048 finallyBlock.ChainLin ();
2049 if (curExcBlock.catchFinallyBlock != null) throw new Exception ("only one catch/finally allowed per try");
2050 curExcBlock.catchFinallyBlock = finallyBlock;
2051 curTryBlock = curExcBlock.tryBlock;
2052 }
2053
2054 public void EndExceptionBlock ()
2055 {
2056 GraphNodeEndExceptionBlock endExcBlock = new GraphNodeEndExceptionBlock (this);
2057 endExcBlock.ChainLin ();
2058 curExcBlock.endExcBlock = endExcBlock;
2059 curTryBlock = curExcBlock.tryBlock;
2060 curExcBlock = curExcBlock.excBlock;
2061 }
2062
2063 public void Emit (Token errorAt, OpCode opcode)
2064 {
2065 if (opcode == OpCodes.Endfinally) {
2066 new GraphNodeEmitNullEndfinally (this, errorAt).ChainLin ();
2067 } else {
2068 new GraphNodeEmitNull (this, errorAt, opcode).ChainLin ();
2069 }
2070 }
2071
2072 public void Emit (Token errorAt, OpCode opcode, FieldInfo field)
2073 {
2074 if (field == null) throw new ArgumentNullException ("field");
2075 new GraphNodeEmitField (this, errorAt, opcode, field).ChainLin ();
2076 }
2077
2078 public void Emit (Token errorAt, OpCode opcode, ScriptMyLocal myLocal)
2079 {
2080 new GraphNodeEmitLocal (this, errorAt, opcode, myLocal).ChainLin ();
2081 }
2082
2083 public void Emit (Token errorAt, OpCode opcode, Type type)
2084 {
2085 new GraphNodeEmitType (this, errorAt, opcode, type).ChainLin ();
2086 }
2087
2088 public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel myLabel)
2089 {
2090 if (opcode == OpCodes.Leave) {
2091 new GraphNodeEmitLabelLeave (this, errorAt, myLabel).ChainLin ();
2092 } else {
2093 new GraphNodeEmitLabel (this, errorAt, opcode, myLabel).ChainLin ();
2094 }
2095 }
2096
2097 public void Emit (Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels)
2098 {
2099 new GraphNodeEmitLabels (this, errorAt, opcode, myLabels).ChainLin ();
2100 }
2101
2102 public void Emit (Token errorAt, OpCode opcode, ScriptObjWriter method)
2103 {
2104 if (method == null) throw new ArgumentNullException ("method");
2105 new GraphNodeEmitIntMeth (this, errorAt, opcode, method).ChainLin ();
2106 }
2107
2108 public void Emit (Token errorAt, OpCode opcode, MethodInfo method)
2109 {
2110 if (method == null) throw new ArgumentNullException ("method");
2111 new GraphNodeEmitExtMeth (this, errorAt, opcode, method).ChainLin ();
2112 }
2113
2114 public void Emit (Token errorAt, OpCode opcode, ConstructorInfo ctor)
2115 {
2116 if (ctor == null) throw new ArgumentNullException ("ctor");
2117 new GraphNodeEmitCtor (this, errorAt, opcode, ctor).ChainLin ();
2118 }
2119
2120 public void Emit (Token errorAt, OpCode opcode, double value)
2121 {
2122 new GraphNodeEmitDouble (this, errorAt, opcode, value).ChainLin ();
2123 }
2124
2125 public void Emit (Token errorAt, OpCode opcode, float value)
2126 {
2127 new GraphNodeEmitFloat (this, errorAt, opcode, value).ChainLin ();
2128 }
2129
2130 public void Emit (Token errorAt, OpCode opcode, int value)
2131 {
2132 new GraphNodeEmitInt (this, errorAt, opcode, value).ChainLin ();
2133 }
2134
2135 public void Emit (Token errorAt, OpCode opcode, string value)
2136 {
2137 new GraphNodeEmitString (this, errorAt, opcode, value).ChainLin ();
2138 }
2139
2140 public void MarkLabel (ScriptMyLabel myLabel)
2141 {
2142 myLabel.whereAmI = new GraphNodeMarkLabel (this, myLabel);
2143 myLabel.whereAmI.ChainLin ();
2144 }
2145
2146 /**
2147 * @brief Write the whole graph out to the object file.
2148 */
2149 public ScriptMyILGen WriteOutAll ()
2150 {
2151 foreach (ScriptMyLocal loc in declaredLocals) {
2152 if (loc.isReferenced) wrapped.DeclareLocal (loc);
2153 }
2154 foreach (ScriptMyLabel lbl in definedLabels) {
2155 wrapped.DefineLabel (lbl);
2156 }
2157 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2158 gn.WriteOutOne (wrapped);
2159 }
2160 return wrapped;
2161 }
2162
2163 /**
2164 * @brief Perform optimizations.
2165 */
2166 public void Optimize ()
2167 {
2168 if (curExcBlock != null) throw new Exception ("exception block still open");
2169
2170 /*
2171 * If an instruction says it doesn't fall through, remove all instructions to
2172 * the end of the block.
2173 */
2174 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2175 if (!gn.CanFallThrough ()) {
2176 GraphNode nn;
2177 while (((nn = gn.nextLin) != null) && !(nn is GraphNodeBlock) &&
2178 !(nn is GraphNodeEndExceptionBlock)) {
2179 if ((gn.nextLin = nn.nextLin) != null) {
2180 nn.nextLin.prevLin = gn;
2181 }
2182 }
2183 }
2184 }
2185
2186 /*
2187 * Scan for OpCodes.Leave instructions.
2188 * For each found, its target for flow analysis purposes is the beginning of the corresponding
2189 * finally block. And the end of the finally block gets a conditional branch target of the
2190 * leave instruction's target. A leave instruction can unwind zero or more finally blocks.
2191 */
2192 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2193 if (gn is GraphNodeEmitLabelLeave) {
2194 GraphNodeEmitLabelLeave leaveInstr = (GraphNodeEmitLabelLeave)gn; // the leave instruction
2195 GraphNodeMarkLabel leaveTarget = leaveInstr.myLabel.whereAmI; // label being targeted by leave
2196 GraphNodeBeginExceptionBlock leaveTargetsTryBlock = // try block directly enclosing leave target
2197 (leaveTarget == null) ? null : leaveTarget.tryBlock; // ...it must not be unwound
2198
2199 /*
2200 * Step through try { }s from the leave instruction towards its target looking for try { }s with finally { }s.
2201 * The leave instruction unconditionally branches to the beginning of the innermost one found.
2202 * The end of the last one found conditionally branches to the leave instruction's target.
2203 * If none found, the leave is a simple unconditional branch to its target.
2204 */
2205 GraphNodeBeginFinallyBlock innerFinallyBlock = null;
2206 for (GraphNodeBeginExceptionBlock tryBlock = leaveInstr.tryBlock;
2207 tryBlock != leaveTargetsTryBlock;
2208 tryBlock = tryBlock.tryBlock) {
2209 if (tryBlock == null) throw new Exception ("leave target not at or outer to leave instruction");
2210 GraphNodeCatchFinallyBlock cfb = tryBlock.catchFinallyBlock;
2211 if (cfb is GraphNodeBeginFinallyBlock) {
2212 if (innerFinallyBlock == null) {
2213 leaveInstr.unwindTo = cfb;
2214 }
2215 innerFinallyBlock = (GraphNodeBeginFinallyBlock)cfb;
2216 }
2217 }
2218
2219 /*
2220 * The end of the outermost finally being unwound can conditionally jump to the target of the leave instruction.
2221 * In the case of no finallies being unwound, the leave is just a simple unconditional branch.
2222 */
2223 if (innerFinallyBlock == null) {
2224 leaveInstr.unwindTo = leaveTarget;
2225 } else if (!innerFinallyBlock.leaveTargets.Contains (leaveTarget)) {
2226 innerFinallyBlock.leaveTargets.Add (leaveTarget);
2227 }
2228 }
2229 }
2230
2231 /*
2232 * See which variables a particular block reads before writing.
2233 * This just considers the block itself and nothing that it branches to or fallsthru to.
2234 */
2235 GraphNodeBlock currentBlock = null;
2236 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2237 if (gn is GraphNodeBlock) currentBlock = (GraphNodeBlock)gn;
2238 ScriptMyLocal rdlcl = gn.ReadsLocal ();
2239 if ((rdlcl != null) &&
2240 !currentBlock.localsWrittenBeforeRead.Contains (rdlcl) &&
2241 !currentBlock.localsReadBeforeWritten.Contains (rdlcl)) {
2242 currentBlock.localsReadBeforeWritten.Add (rdlcl);
2243 }
2244 ScriptMyLocal wrlcl = gn.WritesLocal ();
2245 if ((wrlcl != null) &&
2246 !currentBlock.localsWrittenBeforeRead.Contains (wrlcl) &&
2247 !currentBlock.localsReadBeforeWritten.Contains (wrlcl)) {
2248 currentBlock.localsWrittenBeforeRead.Add (wrlcl);
2249 }
2250 }
2251
2252 /*
2253 * For every block we branch to, add that blocks readables to our list of readables,
2254 * because we need to have those values valid on entry to our block. But if we write the
2255 * variable before we can possibly branch to that block, then we don't need to have it valid
2256 * on entry to our block. So basically it looks like the branch instruction is reading
2257 * everything required by any blocks it can branch to.
2258 */
2259 do {
2260 this.resolvedSomething = false;
2261 this.resolveSequence ++;
2262 this.ResolveBlock ((GraphNodeBlock)firstLin);
2263 } while (this.resolvedSomething);
2264
2265 /*
2266 * Repeat the cutting loops as long as we keep finding stuff.
2267 */
2268 bool didSomething;
2269 do {
2270 didSomething = false;
2271
2272 /*
2273 * Strip out ldc.i4.1/xor/ldc.i4.1/xor
2274 */
2275 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2276 if (!(gn is GraphNodeEmit)) continue;
2277 GraphNodeEmit xor2 = (GraphNodeEmit)gn;
2278 if (xor2.opcode != OpCodes.Xor) continue;
2279 if (!(xor2.prevLin is GraphNodeEmit)) continue;
2280 GraphNodeEmit ld12 = (GraphNodeEmit)xor2.prevLin;
2281 if (ld12.opcode != OpCodes.Ldc_I4_1) continue;
2282 if (!(ld12.prevLin is GraphNodeEmit)) continue;
2283 GraphNodeEmit xor1 = (GraphNodeEmit)ld12.prevLin;
2284 if (xor1.opcode != OpCodes.Xor) continue;
2285 if (!(xor2.prevLin is GraphNodeEmit)) continue;
2286 GraphNodeEmit ld11 = (GraphNodeEmit)xor1.prevLin;
2287 if (ld11.opcode != OpCodes.Ldc_I4_1) continue;
2288 ld11.prevLin.nextLin = xor2.nextLin;
2289 xor2.nextLin.prevLin = ld11.prevLin;
2290 didSomething = true;
2291 }
2292
2293 /*
2294 * Replace c{cond}/ldc.i4.1/xor/br{false,true} -> c{cond}/br{true,false}
2295 */
2296 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2297 if (!(gn is GraphNodeEmit)) continue;
2298 GraphNodeEmit brft = (GraphNodeEmit)gn;
2299 if ((brft.opcode != OpCodes.Brfalse) && (brft.opcode != OpCodes.Brtrue)) continue;
2300 if (!(brft.prevLin is GraphNodeEmit)) continue;
2301 GraphNodeEmit xor = (GraphNodeEmit)brft.prevLin;
2302 if (xor.opcode != OpCodes.Xor) continue;
2303 if (!(xor.prevLin is GraphNodeEmit)) continue;
2304 GraphNodeEmit ldc = (GraphNodeEmit)xor.prevLin;
2305 if (ldc.opcode != OpCodes.Ldc_I4_1) continue;
2306 if (!(ldc.prevLin is GraphNodeEmit)) continue;
2307 GraphNodeEmit cmp = (GraphNodeEmit)ldc.prevLin;
2308 if (cmp.opcode.StackBehaviourPop != StackBehaviour.Pop1_pop1) continue;
2309 if (cmp.opcode.StackBehaviourPush != StackBehaviour.Pushi) continue;
2310 cmp.nextLin = brft;
2311 brft.prevLin = cmp;
2312 brft.opcode = (brft.opcode == OpCodes.Brfalse) ? OpCodes.Brtrue : OpCodes.Brfalse;
2313 didSomething = true;
2314 }
2315
2316 /*
2317 * Replace c{cond}/br{false,true} -> b{!,}{cond}
2318 */
2319 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2320 if (!(gn is GraphNodeEmit)) continue;
2321 GraphNodeEmit brft = (GraphNodeEmit)gn;
2322 if ((brft.opcode != OpCodes.Brfalse) && (brft.opcode != OpCodes.Brtrue)) continue;
2323 if (!(brft.prevLin is GraphNodeEmit)) continue;
2324 GraphNodeEmit cmp = (GraphNodeEmit)brft.prevLin;
2325 if (cmp.opcode.StackBehaviourPop != StackBehaviour.Pop1_pop1) continue;
2326 if (cmp.opcode.StackBehaviourPush != StackBehaviour.Pushi) continue;
2327 cmp.prevLin.nextLin = brft;
2328 brft.prevLin = cmp.prevLin;
2329 bool brtru = (brft.opcode == OpCodes.Brtrue);
2330 if (cmp.opcode == OpCodes.Ceq) brft.opcode = brtru ? OpCodes.Beq : OpCodes.Bne_Un;
2331 else if (cmp.opcode == OpCodes.Cgt) brft.opcode = brtru ? OpCodes.Bgt : OpCodes.Ble;
2332 else if (cmp.opcode == OpCodes.Cgt_Un) brft.opcode = brtru ? OpCodes.Bgt_Un : OpCodes.Ble_Un;
2333 else if (cmp.opcode == OpCodes.Clt) brft.opcode = brtru ? OpCodes.Blt : OpCodes.Bge;
2334 else if (cmp.opcode == OpCodes.Clt_Un) brft.opcode = brtru ? OpCodes.Blt_Un : OpCodes.Bge_Un;
2335 else throw new Exception ();
2336 didSomething = true;
2337 }
2338
2339 /*
2340 * Replace ld{c.i4.0,null}/br{ne.un,eq} -> br{true,false}
2341 */
2342 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2343 if (!(gn is GraphNodeEmit)) continue;
2344 GraphNodeEmit brcc = (GraphNodeEmit)gn;
2345 if ((brcc.opcode != OpCodes.Bne_Un) && (brcc.opcode != OpCodes.Beq)) continue;
2346 if (!(brcc.prevLin is GraphNodeEmit)) continue;
2347 GraphNodeEmit ldc0 = (GraphNodeEmit)brcc.prevLin;
2348 if ((ldc0.opcode != OpCodes.Ldc_I4_0) && (ldc0.opcode != OpCodes.Ldnull)) continue;
2349 ldc0.prevLin.nextLin = brcc;
2350 brcc.prevLin = ldc0.prevLin;
2351 brcc.opcode = (brcc.opcode == OpCodes.Bne_Un) ? OpCodes.Brtrue : OpCodes.Brfalse;
2352 didSomething = true;
2353 }
2354
2355 /*
2356 * Replace:
2357 * ldloc v1
2358 * stloc v2
2359 * ld<anything> except ld<anything> v2
2360 * ldloc v2
2361 * ...v2 unreferenced hereafter
2362 * With:
2363 * ld<anything> except ld<anything> v2
2364 * ldloc v1
2365 */
2366 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2367
2368 // check for 'ldloc v1' instruction
2369 if (!(gn is GraphNodeEmitLocal)) continue;
2370 GraphNodeEmitLocal ldlv1 = (GraphNodeEmitLocal)gn;
2371 if (ldlv1.opcode != OpCodes.Ldloc) continue;
2372
2373 // check for 'stloc v2' instruction
2374 if (!(ldlv1.nextLin is GraphNodeEmitLocal)) continue;
2375 GraphNodeEmitLocal stlv2 = (GraphNodeEmitLocal)ldlv1.nextLin;
2376 if (stlv2.opcode != OpCodes.Stloc) continue;
2377
2378 // check for 'ld<anything> except ld<anything> v2' instruction
2379 if (!(stlv2.nextLin is GraphNodeEmit)) continue;
2380 GraphNodeEmit ldany = (GraphNodeEmit)stlv2.nextLin;
2381 if (!ldany.opcode.ToString ().StartsWith ("ld")) continue;
2382 if ((ldany is GraphNodeEmitLocal) &&
2383 ((GraphNodeEmitLocal)ldany).myLocal == stlv2.myLocal) continue;
2384
2385 // check for 'ldloc v2' instruction
2386 if (!(ldany.nextLin is GraphNodeEmitLocal)) continue;
2387 GraphNodeEmitLocal ldlv2 = (GraphNodeEmitLocal)ldany.nextLin;
2388 if (ldlv2.opcode != OpCodes.Ldloc) continue;
2389 if (ldlv2.myLocal != stlv2.myLocal) continue;
2390
2391 // check that v2 is not needed after this at all
2392 if (IsLocalNeededAfterThis (ldlv2, ldlv2.myLocal)) continue;
2393
2394 // make 'ld<anything>...' the first instruction
2395 ldany.prevLin = ldlv1.prevLin;
2396 ldany.prevLin.nextLin = ldany;
2397
2398 // make 'ldloc v1' the second instruction
2399 ldany.nextLin = ldlv1;
2400 ldlv1.prevLin = ldany;
2401
2402 // and make 'ldloc v1' the last instruction
2403 ldlv1.nextLin = ldlv2.nextLin;
2404 ldlv1.nextLin.prevLin = ldlv1;
2405
2406 didSomething = true;
2407 }
2408
2409 /*
2410 * Remove all the stloc/ldloc that are back-to-back without the local
2411 * being needed afterwards. If it is needed afterwards, replace the
2412 * stloc/ldloc with dup/stloc.
2413 */
2414 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2415 if ((gn is GraphNodeEmitLocal) &&
2416 (gn.prevLin is GraphNodeEmitLocal)) {
2417 GraphNodeEmitLocal stloc = (GraphNodeEmitLocal)gn.prevLin;
2418 GraphNodeEmitLocal ldloc = (GraphNodeEmitLocal)gn;
2419 if ((stloc.opcode == OpCodes.Stloc) &&
2420 (ldloc.opcode == OpCodes.Ldloc) &&
2421 (stloc.myLocal == ldloc.myLocal)) {
2422 if (IsLocalNeededAfterThis (ldloc, ldloc.myLocal)) {
2423 GraphNodeEmitNull dup = new GraphNodeEmitNull (this, stloc.errorAt, OpCodes.Dup);
2424 dup.nextLin = stloc;
2425 dup.prevLin = stloc.prevLin;
2426 stloc.nextLin = ldloc.nextLin;
2427 stloc.prevLin = dup;
2428 dup.prevLin.nextLin = dup;
2429 stloc.nextLin.prevLin = stloc;
2430 gn = stloc;
2431 } else {
2432 stloc.prevLin.nextLin = ldloc.nextLin;
2433 ldloc.nextLin.prevLin = stloc.prevLin;
2434 gn = stloc.prevLin;
2435 }
2436 didSomething = true;
2437 }
2438 }
2439 }
2440
2441 /*
2442 * Remove all write-only local variables, ie, those with no ldloc[a] references.
2443 * Replace any stloc instructions with pops.
2444 */
2445 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2446 ScriptMyLocal rdlcl = gn.ReadsLocal ();
2447 if (rdlcl != null) rdlcl.isReferenced = true;
2448 }
2449 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2450 ScriptMyLocal wrlcl = gn.WritesLocal ();
2451 if ((wrlcl != null) && !wrlcl.isReferenced) {
2452 if (!(gn is GraphNodeEmitLocal) || (((GraphNodeEmitLocal)gn).opcode != OpCodes.Stloc)) {
2453 throw new Exception ("expecting stloc");
2454 }
2455 GraphNodeEmitNull pop = new GraphNodeEmitNull (this, ((GraphNodeEmit)gn).errorAt, OpCodes.Pop);
2456 pop.nextLin = gn.nextLin;
2457 pop.prevLin = gn.prevLin;
2458 gn.nextLin.prevLin = pop;
2459 gn.prevLin.nextLin = pop;
2460 gn = pop;
2461 didSomething = true;
2462 }
2463 }
2464
2465 /*
2466 * Remove any Ld<const>/Dup,Pop.
2467 */
2468 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2469 if ((gn is GraphNodeEmit) &&
2470 (gn.nextLin is GraphNodeEmit)) {
2471 GraphNodeEmit gne = (GraphNodeEmit)gn;
2472 GraphNodeEmit nne = (GraphNodeEmit)gn.nextLin;
2473 if (gne.isPoppable && (nne.opcode == OpCodes.Pop)) {
2474 gne.prevLin.nextLin = nne.nextLin;
2475 nne.nextLin.prevLin = gne.prevLin;
2476 gn = gne.prevLin;
2477 didSomething = true;
2478 }
2479 }
2480 }
2481 } while (didSomething);
2482
2483 /*
2484 * Dump out the results.
2485 */
2486 if (DEBUG) {
2487 Console.WriteLine ("");
2488 Console.WriteLine (methName);
2489 Console.WriteLine (" resolveSequence=" + this.resolveSequence);
2490
2491 Console.WriteLine (" Locals:");
2492 foreach (ScriptMyLocal loc in declaredLocals) {
2493 Console.WriteLine (" " + loc.type.Name + " " + loc.name);
2494 }
2495
2496 Console.WriteLine (" Labels:");
2497 foreach (ScriptMyLabel lbl in definedLabels) {
2498 Console.WriteLine (" " + lbl.name);
2499 }
2500
2501 Console.WriteLine (" Code:");
2502 DumpCode ();
2503 }
2504 }
2505
2506 private void DumpCode ()
2507 {
2508 int linSeqNos = 0;
2509 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2510 gn.linSeqNo = ++ linSeqNos;
2511 }
2512 for (GraphNode gn = firstLin; gn != null; gn = gn.nextLin) {
2513 StringBuilder sb = new StringBuilder ();
2514 gn.DebStringExt (sb);
2515 Console.WriteLine (sb.ToString ());
2516 if (gn is GraphNodeBlock) {
2517 GraphNodeBlock gnb = (GraphNodeBlock)gn;
2518 foreach (ScriptMyLocal lcl in gnb.localsReadBeforeWritten) {
2519 Console.WriteLine (" reads " + lcl.name);
2520 }
2521 }
2522 }
2523 }
2524
2525 /**
2526 * @brief Scan the given block for branches to other blocks.
2527 * For any locals read by those blocks, mark them as being read by this block,
2528 * provided this block has not written them by that point. This makes it look
2529 * as though the branch instruction is reading all the locals needed by any
2530 * target blocks.
2531 */
2532 private void ResolveBlock (GraphNodeBlock currentBlock)
2533 {
2534 if (currentBlock.hasBeenResolved == this.resolveSequence) return;
2535
2536 /*
2537 * So we don't recurse forever on a backward branch.
2538 */
2539 currentBlock.hasBeenResolved = this.resolveSequence;
2540
2541 /*
2542 * Assume we haven't written any locals yet.
2543 */
2544 List<ScriptMyLocal> localsWrittenSoFar = new List<ScriptMyLocal> ();
2545
2546 /*
2547 * Scan through the instructions in this block.
2548 */
2549 for (GraphNode gn = currentBlock; gn != null;) {
2550
2551 /*
2552 * See if the instruction writes a local we don't know about yet.
2553 */
2554 ScriptMyLocal wrlcl = gn.WritesLocal ();
2555 if ((wrlcl != null) && !localsWrittenSoFar.Contains (wrlcl)) {
2556 localsWrittenSoFar.Add (wrlcl);
2557 }
2558
2559 /*
2560 * Scan through all the possible next instructions after this.
2561 * Note that if we are in the first part of a try/catch/finally block,
2562 * every instruction conditionally branches to the beginning of the
2563 * second part (the catch/finally block).
2564 */
2565 GraphNode nextFallthruNode = null;
2566 foreach (GraphNode nn in gn.NextNodes) {
2567 if (nn is GraphNodeBlock) {
2568
2569 /*
2570 * Start of a block, go through all locals needed by that block on entry.
2571 */
2572 GraphNodeBlock nextBlock = (GraphNodeBlock)nn;
2573 ResolveBlock (nextBlock);
2574 foreach (ScriptMyLocal readByNextBlock in nextBlock.localsReadBeforeWritten) {
2575
2576 /*
2577 * If this block hasn't written it by now and this block doesn't already
2578 * require it on entry, say this block requires it on entry.
2579 */
2580 if (!localsWrittenSoFar.Contains (readByNextBlock) &&
2581 !currentBlock.localsReadBeforeWritten.Contains (readByNextBlock)) {
2582 currentBlock.localsReadBeforeWritten.Add (readByNextBlock);
2583 this.resolvedSomething = true;
2584 }
2585 }
2586 } else {
2587
2588 /*
2589 * Not start of a block, should be normal fallthru instruction.
2590 */
2591 if (nextFallthruNode != null) throw new Exception ("more than one fallthru from " + gn.ToString ());
2592 nextFallthruNode = nn;
2593 }
2594 }
2595
2596 /*
2597 * Process next instruction if it isn't the start of a block.
2598 */
2599 if (nextFallthruNode == gn) throw new Exception ("can't fallthru to self");
2600 gn = nextFallthruNode;
2601 }
2602 }
2603
2604 /**
2605 * @brief Figure out whether the value in a local var is needed after the given instruction.
2606 * True if we reach the end of the program on all branches before reading it
2607 * True if we write the local var on all branches before reading it
2608 * False otherwise
2609 */
2610 private bool IsLocalNeededAfterThis (GraphNode node, ScriptMyLocal local)
2611 {
2612 do {
2613 GraphNode nextFallthruNode = null;
2614 foreach (GraphNode nn in node.NextNodes) {
2615 if (nn is GraphNodeBlock) {
2616 if (((GraphNodeBlock)nn).localsReadBeforeWritten.Contains (local)) {
2617 return true;
2618 }
2619 } else {
2620 nextFallthruNode = nn;
2621 }
2622 }
2623 node = nextFallthruNode;
2624 if (node == null) return false;
2625 if (node.ReadsLocal () == local) return true;
2626 } while (node.WritesLocal () != local);
2627 return false;
2628 }
2629
2630 public static void PadToLength (StringBuilder sb, int len, string str)
2631 {
2632 int pad = len - sb.Length;
2633 if (pad < 0) pad = 0;
2634 sb.Append (str.PadLeft (pad));
2635 }
2636 }
2637}