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