On 04/11/2007, Rob Withers reefedjib@yahoo.com wrote:
----- Original Message ----- From: "Igor Stasenko" siguctua@gmail.com
I'm personally much more worrying about non-local returns.
As an example of the problem of non-local return, let's look at this simple method:
foo: bar
bar ifTrue: [^ 1]. self snafu. ^ 0
If bar is eventual, we don't know at the time of invocation whether the method will exit through the non-local return or through the return at the end of the method.
How to best deal with this?
My thought is that the context needs to become eventual, be sent as a lambda to the bar promise, and the context return a promise. The return will check to see if there is a resolver on the context and #resolve: it with the appropriate return value. The lambda needs to be all sequential code from the point where a message is sent to the promise that includes a block as an argument to the end of the context. So the lambda would be:
f := [:barVow | barVow ifTrue: [^1]. self snafu. ^ 0]
and we would send: ^ bar whenResolved: f.
So the challenge here would be in capturing the lambda and in effect rewriting the code for this method, on the fly. The lambda is just a one arg blockContext, where the ip points to the pushTemp: 0.
17 <10> pushTemp: 0 18 <99> jumpFalse: 21 19 <76> pushConstant: 1 20 <7C> returnTop 21 <70> self 22 <D0> send: snafu 23 <87> pop 24 <75> pushConstant: 0 25 <7C> returnTop
and the two return bytecodes, would each need to resolve the promise returned. This needs to become something like the following, where lines 28-36 is the original block with three rewrites: first, pushTemp: 1 instead of pushTemp: 0, since the block arg is a separate temp. second, the two returnTops needs to become a jumpTo: and a blockReturn, so we can resolve the promise.
21 <10> pushTemp: 0 22 <89> pushThisContext: 23 <76> pushConstant: 1 24 <C8> send: blockCopy: 25 <A4 0A> jumpTo: 37 27 <69> popIntoTemp: 1 28 <11> pushTemp: 1 29 <99> jumpFalse: 32 30 <76> pushConstant: 1 31 <93> jumpTo: 36 32 <70> self 33 <D1> send: snafu 34 <87> pop 35 <75> pushConstant: 0 36 <7D> blockReturn 37 <E0> send: whenResolved: 38 <7C> returnTop
Of course, we don't want to modify the original method, since another call to it may not involve a promise. So we are looking at creating a new MethodContext, which we rewrite to create a new BlockContext defined from lines 22-25 through 36 above. For every non-local return method when called with a promise.
What do you think?
Hmm.. i think, you given a bad example. It depends on 'knowledge' that #ifTrue: is optimized by compiler as an execution branch, but not a regular message send. For general case we should consider passing a block as a argument, like:
bar someMethod: [^1].
But this again, not includes cases, when blocks are assigned to temps/ivars within context and never passed as arguments to message(s).
I thought a bit about marking a method context containing blocks with non-local returns with special flag, which will force context to not activate until all free variables in method are resolved (agruments/temps). This means that before any activation (entering method and each send in method it will wait for resolving all promises it contains). But i'm still doubt if this gives us anything.
There is many methods which operate with blocks by sending #value.. to them. And these methods actually don't care if those blocks contain non-local returns or not. They simply do their job.