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