I made some short code to test it. This is the script part:
static const char* code =
"void main() {\n"
" array<MyObj@>@ the_array = testfunc();\n"
" console_log( string(the_array.length()) );\n"
" for (int i =0; i < the_array.length(); i++) {\n"
" console_log( \" \" + string(i) + \": refcount \" + string(the_array[i].getrefcount()) );\n"
" }\n"
"}";
It obtains an array created from C++ side function testfunc
, and print reference counts in its contents. After the script exit, the C++ destructor of MyObj
class is not called, the array object is not released, and the memory pool reports several unreleased chunks.
The array type I used is the one shipped with AngelScript source package. And this is the related C++ code:
struct MyObj : public juce::ReferenceCountedObject
{
MyObj() { juce::Logger::writeToLog( "MyObj " + juce::String::toHexString( size_t( this ) ) + " constructed" ); }
virtual ~MyObj() { juce::Logger::writeToLog( "MyObj " + juce::String::toHexString( size_t( this ) ) + " destructed" ); }
JUCE_LEAK_DETECTOR( MyObj );
};
void MyObj_ref( asIScriptGeneric* as )
{
auto* self = (MyObj*) as->GetObject();
self->incReferenceCount();
}
void MyObj_unref( asIScriptGeneric* as )
{
auto* self = (MyObj*) as->GetObject();
self->decReferenceCount();
}
void MyObj_getrefcount( asIScriptGeneric* as )
{
auto* self = (MyObj*) as->GetObject();
as->SetReturnDWord( self->getReferenceCount() );
}
static ashelper::CScriptArray* hackptr = nullptr;
void testfunc( asIScriptGeneric* as )
{
auto* tinfo = as->GetEngine()->GetTypeInfoByDecl( "array<MyObj@>" );
auto* re = ashelper::CScriptArray::Create( tinfo, 3 );
hackptr = re;
auto obj1 = new MyObj();
re->SetValue( 0, &obj1 );
auto obj2 = new MyObj();
re->SetValue( 1, &obj2 );
auto obj3 = new MyObj();
re->SetValue( 2, &obj3 );
as->SetReturnObject( re );
}
C++ type register part:
ashelper::registerRefObj( engine, "MyObj" );
ashelper::registerObjBehavior( engine, "MyObj", asBEHAVE_ADDREF, "void f()", MyObj_ref );
ashelper::registerObjBehavior( engine, "MyObj", asBEHAVE_RELEASE, "void f()", MyObj_unref );
ashelper::registerObjMethod( engine, "MyObj", "int getrefcount() const", MyObj_getrefcount );
ashelper::registerFunc( engine, "array<MyObj@>@ testfunc()", testfunc );
Script running part, the array object's holding status is printed in a hacky way:
// run script
asIScriptContext* ctx = engine->CreateContext();
auto* func = mod->GetFunctionByDecl( "void main()" );
ctx->Prepare( func );
if ( ctx->Execute() != 0 )
{
juce::Logger::writeToLog( juce::String( "exec failed: " ) + ctx->GetExceptionString() );
}
juce::Logger::writeToLog( "array object refcount after execution: " + juce::String( hackptr->GetRefCount() ) );
And console output:
MyObj 1f11cc5af50 constructed
MyObj 1f11cc5b310 constructed
MyObj 1f11cc5afb0 constructed
3
0: refcount 1
1: refcount 1
2: refcount 1
array object refcount after execution: 1
WARNING at line 0, col 0: There is an external reference to an object in module 'TheModule', preventing it from being deleted