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