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