I see a couple of errors in your code. It is probably by luck that it hasn't given you trouble before.
void InstantiateClass(asIScriptGeneric *gen)
{
auto name = (std::string*)gen->GetArgObject(0);
auto params = (SValue*)gen->GetArgAddress(1);
auto mod = gen->GetEngine()->GetModule("Scripts");
auto type = mod->GetObjectTypeByName(name->c_str());
if (!type)
{
printf("%s '%s'\n", "Couldn't find class", name->c_str());
//auto ret = CScriptHandle();
//*(CScriptHandle*)gen->GetAddressOfReturnLocation() = ret; // <-- The memory is not yet initialized, so you cannot do an assignment on it
new( gen->GetAddressOfReturnLocation() ) CScriptHandle(); // <-- You must initialize the memory using the placement new() operator
return;
}
auto tName = std::string(type->GetName());
auto factory = type->GetFactoryByDecl((tName + "@ " + tName + "(SValue& params)").c_str());
if (!factory)
{
printf("%s '%s' %s\n", "Couldn't find class", name->c_str(), "factory function");
//auto ret = CScriptHandle();
//*(CScriptHandle*)gen->GetAddressOfReturnLocation() = ret; // <-- The memory is not yet initialized, so you cannot do an assignment on it
new( gen->GetAddressOfReturnLocation() ) CScriptHandle(); // <-- You must initialize the memory using the placement new() operator
return;
}
auto ctx = gen->GetEngine()->CreateContext();
ScriptEngine::PrepareContext(ctx, factory);
int r = ctx->SetArgObject(0, params); assert(r >= 0);
ScriptEngine::VerifyScriptExecution(ctx, ctx->Execute());
auto bObj = *((asIScriptObject**)ctx->GetAddressOfReturnValue());
// ScriptEngine::UnprepareContext(ctx); // <-- This releases the reference to the object held by the context, thus destroying it before you give it to the CScriptHandle
// auto ret = CScriptHandle(bObj, type); // <-- The script handle will increment the ref count to take ownership of the object
// ctx->Release(); // <-- Destroying the context automatically unprepares it, so there is no need to explicitly call Unprepare() unless you're using context pooling
// *(CScriptHandle*)gen->GetAddressOfReturnLocation() = ret; // <-- The memory is not yet initialized so this assignment is incorrect
new( gen->GetAddressOfReturnLocation() ) CScriptHandle(bObj, type); // <-- Use placement new to initalize the memory
ctx->Release(); // <-- Release the context after initializing the script handle to avoid it being destroyed too early
}
The fact that it doesn't crash when the script class has a handle as member is because that member causes the class to be garbage collected as it can potentially form circular references. This in turn makes the garbage collector hold on to a reference to the object in order to properly control the life time, which is how the object was kept alive long enough for the CScriptHandle to call AddRefScriptObject on it. When the class doesn't have the handle, the script object was destroyed immediately upon ctx->Unprepare() thus turning the bObj pointer invalid even before you could pass it to the CScriptHandle instance.
Regards,
Andreas