Jump to content
  • Advertisement
Sign in to follow this  
Myran

Crash in AddRefScriptObject

This topic is 1522 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I want to be able to instantiate different classes based on a string, to do that I use this C++ function:
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;
		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;
		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);

	auto ret = CScriptHandle(bObj, type);
	ctx->Release();

	*(CScriptHandle*)gen->GetAddressOfReturnLocation() = ret;
}
This has worked well for me for a while now, but now I'm getting a crash when trying to use it on some really simple classes. It seems like when the CScriptHandle does engine->AddRefScriptObject it will crash if the class doesn't contain an object handle.
 
So doing it on this class works:
class Explode : IAction
{
	IAction@ dummy;
	
	Explode(SValue& params)
	{
	}
	
	bool DoAction(Actor@ owner, vec2 pos, vec2 dir)
	{
		return true;
	}
	
	void Update(int dt, int cooldown)
	{
	}
}
But if I remove IAction@ dummy, I get a crash in "void asCScriptEngine::CallObjectMethod(void *obj, asSSystemFunctionInterface *i, asCScriptFunction *s) const".

Share this post


Link to post
Share on other sites
Advertisement

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

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!