Hm, actually, I think I may have fixed it! (Emphasis on the
may.)
After spending a while getting acquainted with the calling procedure and SQVM::Execute, I managed to get generator functions working with .call()/.acall()/etc. It's a relatively small code change, but I'm still not completely familiar with Squirrel's innards, so there may be some unintended problems caused by this code. It seems to be working for me, but I'd appreciate some feedback from those who know better. :-)
Basically, just change the entire ET_CALL case of the switch at the top of SQVM::Execute to the following:
case
ET_CALL:{
SQInteger last_top = _top;
if(!StartCall(_closure(closure),
_top - nargs, nargs, stackbase, false))
{
//call
the handler if there are no calls in the stack, if not relies on the
previous node
if(ci
== NULL) CallErrorHandler(_lasterror);
return
false;
}
//
generator.call() bugfix:
if(_funcproto(_closure(closure)->_function)->_bgenerator)
{
SQGenerator *gen = SQGenerator::Create(_ss(this),
_closure(closure));
_GUARD(gen->Yield(this));
POP_CALLINFO(this);
outres = SQObjectPtr(gen);
while
(last_top >= _top) _stack._vals[last_top--].Null();
return
true;
}
ci->_root = SQTrue;
}
break;
Then .call(), .acall(), .pcall(), and .pacall() should work correctly on generator functions. (At least, as far as I can tell...) Alternately, I've uploaded a
fixed copy of sqvm.cpp from Squirrel 2.2.2 stable if anybody wants to just drop it in and try it out. I also wrote up
a quick .nut script to test using .call() methods on generator functions. I haven't tried this in 3.0 alpha 1 yet, but I imagine it should work there also.
Also, I found another bug, possibly related to the first one. Initially, I figured that I could just emulate .call() on a generator by wrapping the generator call in another function and using .call() on that. Unfortunately, this also crashes Squirrel:
function gen() {
print(this)
yield
}
function wrapper() {
return gen()
}
local g = wrapper.call("mycontext") // immediate crash
Oddly enough, this
only seems to cause a crash when
directly returning the result of a generator function evaluation from a method invoked with .call(). If the method is invoked normally, or if you store the generator in a local variable before returning it, everything works perfectly. For example:
function gen() {
print(this)
yield
}
function wrapper() {
local g = gen()
return g
}
local g = wrapper.call("mycontext") // this works fine
Seems like an odd and somewhat obscure bug. Definitely seems to be a different cause than the first bug though, from a cursory investigation.