Refcounting in opCast

Started by
12 comments, last by Miss 5 years, 10 months ago

I have an function declaration of "void opCast(?&out)" on a class with asOBJ_REF, and I'm running into an interesting problem.

In 1 very specific case, (I can't seem to reproduce it in a clean test script), there seems to be either an AddRef too many or a Release too little.

I can fix it by making a small change to my script. The following causes there to be 5 AddRef's, and 5 Releases, which is the expected behavior:


auto a = g_scene.Mobils[0];
auto b = cast<CControlBase>(a);
if (b !is null) {
  // ...
} else {
  // ...
}

But the following produces 5 AddRef's and 4 Releases:


auto b = cast<CControlBase>(g_scene.Mobils[0]);
if (b !is null) {
  // ...
} else {
  // ...
}

I checked if the typeId happens to be different in the opCast function (thought maybe const could be at play or so) but it seems like that's the same in both situations. I do an AddRef myself in the opCast function when returning the casted handle.

Advertisement

g_scene.Mobils is a CScriptArray? What is the type of the elements?

I'll try to reproduce the problem.

 

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

No, it's actually a custom class "MwBuffer". The interface is documented here: https://openplanet.nl/docs/class/MwBuffer

The code for opIndex looks like this:


static void ScriptBufferIndexer(asIScriptGeneric* gen)
{
	CFastBufferBase* self = (CFastBufferBase*)gen->GetObject();
	int dwIndex = gen->GetArgDWord(0);

	if (dwIndex >= self->m_Count) {
		asGetActiveContext()->SetException("Index out of range in buffer");
		return;
	}

	int returnTypeId = gen->GetReturnTypeId();
	asUINT typeSize = 4;
	if (!(returnTypeId & asTYPEID_OBJHANDLE)) {
		typeSize = GetTypeSize(gen->GetEngine(), gen->GetReturnTypeId());
	}

	void* pObj = (char*)self->m_elements + typeSize * dwIndex;
	gen->SetReturnAddress(pObj);
}

 

Can you show what the opCast implementation looks like too?

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

It's an asCALL_CDECL_OBJFIRST, implementation here: https://pastebin.com/ybuVSTHj

Thanks. 

I see nothing wrong in your code. I suspect it may be a bug related to how the compiler treats temporary variables. I'll investigate further and let you know.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Thank you!

I've managed to reproduce the problem. It is indeed a bug in the compiler. I even get an assert failure in the compiler when running in debug mode.

I'll have a fix available in the next few days.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I've fixed this in revision 2509.

Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game


external shared class A;
A@ a = cast<A>(GetA());

With the revision of rev 2509, when there is initialization of the global variable as described above, it outputs and stops as follows.


Assertion failed: tempVariables.Exists(offset), file j:\home\src\angelscript\sdk\angelscript\source\as_compiler.cpp, line 5333

The following is a fragment of the test code executed.


#include "../../../add_on/scriptany/scriptany.h"
...snip...

static void CScriptAny_Cast(void *ref, int refTypeId, void *x)
{
  if(!((CScriptAny*)x)->Retrieve(ref, refTypeId))
  {
    asIScriptContext *ctx = asGetActiveContext();
    if( ctx )
      ctx->SetException("Illegal conversion");
  }
}

static void *GetA()
{
  return nullptr;
}

bool Test()
{
...snip...

        RegisterScriptAny(engine);

        r = engine->RegisterObjectMethod("any", "void opCast(?&out)", asFUNCTION(CScriptAny_Cast), asCALL_CDECL_OBJLAST); assert( r >= 0 );

        r = engine->RegisterGlobalFunction(
                       "any@ GetA()"
                     , asFUNCTION(GetA)
                     , asCALL_CDECL
                     );
		if (r < 0)
			TEST_FAILED;

		mod = engine->GetModule("test", asGM_ALWAYS_CREATE);
		mod->AddScriptSection("test",
			"shared class A {"
            "};"
         );
		r = mod->Build();
		if (r < 0)
			TEST_FAILED;

		asIScriptModule *mod2 = engine->GetModule("test2", asGM_ALWAYS_CREATE);
		mod2->AddScriptSection("test",
			"external shared class A;"
            "A@ a = cast<A>(GetA());"
		);
		r = mod2->Build();
		if (r < 0)
			TEST_FAILED;

 

This topic is closed to new replies.

Advertisement