I think I found a memory leak when script objects get deleted. If there is a c++ class Test and an angel script class Foo which contains a reference to Test then when Foo goes out of scope Test is not released.
main.cpp:
#include <iostream>
#include "scriptstring.h"
#include "Test.h"
#include <assert.h>
void MessageCallback(const asSMessageInfo* msg, void* param);
int main()
{
asIScriptEngine* eng = asCreateScriptEngine( ANGELSCRIPT_VERSION);
int r;
r = eng->SetMessageCallback( asFUNCTION( MessageCallback), NULL, asCALL_CDECL); assert( r >= 0);
Test::Register( eng);
std::string script;
//Foo goes out of scope m_Test needs to be released
script += "class Foo\n";
script += "{\n";
script += " Test@ m_Test;\n";
script += "};\n";
script += "void main()\n";
script += "{\n";
script += " Foo foo;\n";
script += " Test t;\n";
script += " @foo.m_Test = @t;\n";
script += "}\n";
r = eng->AddScriptSection( NULL, NULL, script.c_str(), script.size()); assert( r >= 0);
r = eng->Build( 0); assert( r >= 0);
asIScriptContext* context = eng->CreateContext();
//this always outputs 0
std::cout << "Num before: " << Test::NumLeft() << "\n";
r = eng->ExecuteString( "", "main()", &context); assert( r >= 0);
//this should output 0 but outputs 1
std::cout << "Num after: " << Test::NumLeft() << "\n";
context->Release();
eng->Release();
return 0;
}
void MessageCallback( const asSMessageInfo* msg, void* param)
{
const char *type = "ERR ";
if( msg->type == asMSGTYPE_WARNING )
{
type = "WARN";
}
else if( msg->type == asMSGTYPE_INFORMATION )
{
type = "INFO";
}
printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
}
Test.h:
#include "angelscript.h"
#include <vector>
/**
* Simple script class.
* Provides basic reference counting.
*/
class Test
{
public:
Test();
Test( const Test& test);
virtual ~Test();
/**
* Increments the reference count.
*/
void add();
/**
* Deincrements the reference count.
* When the reference count reaches 0 this is no longer valid.
*/
void release();
/**
* Calls the destructor.
*/
virtual void destroy();
/**
* Check how many Test objects are in existance.
*
* @return Number of Test objects.
*/
static size_t NumLeft();
/**
* Register to angelscript.
*
* @param eng Engine to register to.
*/
static void Register( asIScriptEngine* eng);
private:
/**
* Holds all existing instances of Test.
* Used to make sure there are no memmory leaks.
*/
static std::vector<Test*> s_Test;
unsigned int m_RefCount; /**< Reference counter. */
};
Test.cpp:
#include "Test.h"
#include <algorithm>
#include <iostream>
#include <assert.h>
std::vector<Test*> Test::s_Test;
Test::Test()
{
std::cout << "new\n";
m_RefCount = 1;
s_Test.push_back( this);
}
Test::Test( const Test& test)
{
std::cout << "new2\n";
m_RefCount = 1;
s_Test.push_back( this);
}
Test::~Test()
{
std::vector<Test*>::iterator iter;
iter = std::find( s_Test.begin(), s_Test.end(), this);
if( iter == s_Test.end())
{
std::cout << "Deleted twice\n";
std::exit( 1);
} else {
s_Test.erase( iter);
}
}
void Test::add()
{
std::cout << "add\n";
m_RefCount++;
}
void Test::release()
{
std::cout << "release\n";
assert( m_RefCount > 0);
m_RefCount--;
if( m_RefCount <= 0)
{
delete this;
}
}
void Test::destroy()
{ this->~Test(); }
size_t Test::NumLeft()
{
return s_Test.size();
}
void Construct( Test* obj)
{ new (obj)Test(); }
Test& Copy( Test& obj, const Test& other)
{
return obj = other;
}
void Test::Register( asIScriptEngine* eng)
{
int r;
r = eng->RegisterObjectType( "Test", sizeof( Test), asOBJ_CLASS_CDA); assert( r >= 0);
r = eng->RegisterObjectBehaviour( "Test", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION( Construct), asCALL_CDECL_OBJFIRST); assert( r >= 0);
r = eng->RegisterObjectBehaviour( "Test", asBEHAVE_DESTRUCT, "void f()", asMETHOD( Test, destroy), asCALL_THISCALL); assert( r >= 0);
r = eng->RegisterObjectBehaviour( "Test", asBEHAVE_ASSIGNMENT, "Test& f(const Test&in)", asFUNCTION( Copy), asCALL_CDECL_OBJFIRST); assert( r >= 0);
r = eng->RegisterObjectBehaviour( "Test", asBEHAVE_ADDREF, "void f()", asMETHOD( Test, add), asCALL_THISCALL); assert( r >= 0);
r = eng->RegisterObjectBehaviour( "Test", asBEHAVE_RELEASE, "void f()", asMETHOD( Test, release), asCALL_THISCALL); assert( r >= 0);
}