Lofti Gheribi wrote:
> This is not the case for .Net. Microsoft's implementation needs more
> than a single pass for be able to compute the stack depth.
Multiple passes are not actually required. As you've shown in your
example, locations after an unconditional branch (or throw, etc) which
have a forward branch to them (like L4) can get their stack contents
from the branch in a single forward pass. They are not a concern.
The complication comes with locations after an unconditional branch
which have only backward references to them (like L2). For clarity/
brevity, let me give such kind of location a name. I'll call it a
"mystery point." The standard says to assume that the stack is empty
at a mystery point, which is what Cecil probably does. But the
standard is WRONG, or more clearly, it is overly restrictive. Why do I
say that?
(Not counting obfuscators) Mystery points occur only with one kind of
high-level language construct: the loop. A loop like this...
Before()
while (Condition())
Body()
After()
...turns into this IL
call Before
br COND
LOOP:
call Body
COND:
call Condition
brtrue LOOP
call After
The mystery point here is of course at label LOOP. What should the
stack be? With languages like C# and VB, a loop can only occur as a
statement, and a statement always begins with a clear stack. So
assuming the stack is clear works fine for those languages -- BUT NOT
FOR ALL.
There are (at least) two cases where a loop can occur with a non-empty
stack in the general case.
1) A loop construct which can return a value
2) The language supports inlining a method
With Case #1, such a loop may be used as a second method argument:
SomeMethod (15, SumOfRange(5,10))
Here, by SumOfRange, I'm referring to a language construct which loops
to add all numbers 5 to 10 without calling any method. This loop
occurs while the first argument (15) is sitting on the stack.
Case #2 means that not every statement begins with an empty stack.
Assume now SumOfRange is an ordinary method which loops between the
numbers given as arguments and returns their sum. But our language
allows demanding that the method's code is emitted inline.
SomeMethod (15, inline SumOfRange(5,10))
The net result is the same -- a loop with a nonempty stack -- just for
a different reason.
As it turns out, for these cases it is possible to determine the stack
with a single forward pass. The rule is simple: assume that the stack
contents at a mystery point is -- drum roll please -- exactly the same
as at the unconditional branch before it. In other words, the loop
only adds to the stack contents temporarily; by the time the loop
ends, the stack is as it was at the beginning. Certainly, it makes
sense that a loop should behave that way. This is what the standard
should say, and it appears to be what .NET is doing.
For C#/VB this rule reduces to "assume the stack is empty" since, as I
showed, their loops and (therefore) mystery points can only appear at
a place where the stack is empty. But not all languages work that way,
and this rule works in the general case.
BTW, this rule works for your example, although it's not a loop, which
I believe is why your example runs correctly on .NET.
My 2 cents,
-- Keith
--~--~---------~--~----~------------~-------~--~----~
--
mono-cecil
-~----------~----~----~----~------~----~------~--~---