As I played with various AS concepts in a small program that I based on "asrun" sample I started getting segfaults, which I tracked to be caused by adding a simple destructor to a class that was instantiated, called some method that caused exception to be set and then exited.
Took me a while, but I think I found the problem and it's probably worth fixing. This example uses callbacks to request and return context from/to a global context pool.
#0 0x000000000044f666 in asCContext::SetObject(void*) ()
#1 0x00000000004950d8 in asCScriptObject::CallDestructor() ()
#2 0x0000000000494ef6 in asCScriptObject::Release() const ()
#3 0x000000000047ae27 in asCScriptEngine::CallObjectMethod(void*, asSSystemFunctionInterface*, asCScriptFunction*) const ()
#4 0x000000000047ad07 in asCScriptEngine::CallObjectMethod(void*, int) const ()
#5 0x000000000045b059 in asCContext::CleanStackFrame() ()
#6 0x0000000000459f8c in asCContext::CleanStack() ()
#7 0x000000000044edac in asCContext::Unprepare() ()
#8 0x000000000040779a in ReturnContextCallback(asIScriptEngine*, asIScriptContext*, void*) ()
#9 0x000000000046cf8b in asCScriptEngine::ReturnContext(asIScriptContext*) ()
#10 0x000000000044a21b in CContextMgr::DoneWithContext(asIScriptContext*) ()
#11 0x000000000040718d in ExecuteScript(asIScriptEngine*, char const*, bool) ()
Key thing probably is exception and destructor and how ReturnContextCallback function returns context to the pool. Here is the function:
void ReturnContextCallback(asIScriptEngine *engine, asIScriptContext *ctx, void * /*param*/)
{
g_ctxPool.push_back(ctx);
// Unprepare the context to free any objects it may still hold (e.g. return value)
ctx->Unprepare();
}
Seems like the context is first returned to the pool as "ready to use", and only then unprepared. In my situation (after exception thrown) this causes context to be cleared of objects, which causes that destructor to be called. To call destructor, it has to fetch context, which it does through callback "ReturnContextCallback" and it yields the very same context as the one that's being unprepared.
I think I fixed this by just moving ctx->Unprepare() above the line that pushes the context back to the pool.
The script I used to reproduce this is simple, it just has to throw exception at some point, while also having a destructor.
class Player
{
~Player()
{
print("destructor\n");
}
void foo()
{
int x = 0;
int y = 1 / x;
}
}
int main()
{
Player player();
player.foo();
return 1;
}
Btw, is this correct place to fill some possible bugs/requests or there is some system for this?