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