Advertisement

Crash when copying obj_values

Started by May 23, 2012 07:09 PM
5 comments, last by Plaristote 12 years, 6 months ago
Greetings !
I would need a bit of help on fixing a problem : apparently I'm doing something wrong when implementing the opAssign operator.
Here is a short and easily reproducible piece of code that, for some reason, crashes :

struct test
{
test copy(test other) { std::cout << "test2" << std::endl; thing = other.thing; return (other); }

int thing;
};

int main(void)
{
asIScriptEngine* engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

const char* testClass = "Test";
engine->RegisterObjectType (testClass, sizeof(test), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS);
engine->RegisterObjectMethod (testClass, "Test opAssign(Test)", asMETHOD(test,copy), asCALL_THISCALL);

CScriptBuilder builder;

builder.StartNewModule(engine, "testModule");
builder.AddSectionFromFile("scripts/t.as");
builder.BuildModule();

asIScriptModule* module = engine->GetModule("testModule");
asIScriptFunction* func = module->GetFunctionByDecl("void testFunc()");
asIScriptContext* context = engine->CreateContext();

std::cout << "Prepare" << std::endl;
context->Prepare(func);
std::cout << "Execute" << std::endl;
context->Execute();
std::cout << "Done" << std::endl;

return (0);
}

And the AngelScript code :
void testFunc()
{
Test test;
Test test2;

test = test2;
}

It crahses at the line "test = test".
Can you please help me :) ?
You have a bit of a chicken-and-egg situation here.

Your opAssign method is taking the object by value, but in order to pass the object by value to the method the object needs to be copied, which in turn will call the opAssign method. :)

I'll have to add a check in the code to prevent the script engine to accept this as it obviously won't work.

To fix it in your code you need to change your opAssign() method to take the object by reference, the opAssign() method should also return a reference to the object, i.e:



struct test
{
test &copy(const test &other) { std::cout << "test2" << std::endl; thing = other.thing; return *this; }

int thing;
};

int main(void)
{
asIScriptEngine* engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

const char* testClass = "Test";
engine->RegisterObjectType (testClass, sizeof(test), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS);
engine->RegisterObjectMethod (testClass, "Test &opAssign(const Test &in)", asMETHOD(test,copy), asCALL_THISCALL);


Observe how the C++ signature is 'test &copy(const test &)' but the AngelScript signature is 'Test &opAssign(const Test &in)'. The 'in' keyword is important, as it tells AngelScript that this reference is meant to be an input value.

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

Advertisement
Aha, indeed it works much better that yay ! Thank you !

Now that we're at it, I've got another problem that looks like this one (it crashes in the same fctions) but with opIndex, and I didn't manage to solve it by using references. Problem is I didn't manage to write a short example to reproduce it.
This is the opIndex method I registered :


engine->RegisterObjectMethod (dataClass, "Data opIndex(string &in)", asMETHODPR(Data,operator[], (const std::string&), Data), asCALL_THISCALL);

The operator[] itself returns a copy and that's the whole point (Data is actually a wrapper for a tree, and using copies it allows you to dynamically add branches, or explore branches that don't exist yet). Thus I don't know if it is possible or wise to return Data& on the AS side ?

I tried using both these prototypes for AS : "Data opIndex(string)" and "Data& opIndex(string)" : the outcome is the same, but the causes differ. The only way to solve it is to ask AS not to collect garbage on these objects (and that's not really an alternative ^^).



If I use Data&, it looks like there's a buffer overload or something like that : because when AS calls my C++ method, it does so with a "this" that has been allocated by scriptstdstring.

This is the valgrind output that shows this :





==29662== Invalid read of size 4
==29662== at 0x80BFCED: std::list<DataBranch*, std::allocator<DataBranch*> >::begin() (stl_list.h:762)
==29662== by 0x80D8124: Data::operator[](std::string const&) (data.cpp:74)
==29662== by 0x4FC0EAA: CallThisCallFunction(void const*, unsigned long const*, int, void (*)()) (in /usr/lib/libangelscript-2.23.1.so)
==29662== by 0xBEB58D3F: ???
==29662== Address 0xddd94b4 is 0 bytes after a block of size 20 alloc'd
==29662== at 0x402AB64: operator new(unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==29662== by 0x51126B3: std::string::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.17)
==29662== by 0x5114939: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/libstdc++.so.6.0.17)
==29662== by 0x5114A11: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, unsigned int, std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.17)




And if I don't use a reference, I have a double-free problem :



==30274== Invalid free() / delete / delete[] / realloc()
==30274== at 0x402A21C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30274== by 0x5021D4D: asCScriptEngine::CallFree(void*) const (in /usr/lib/libangelscript-2.23.1.so)
==30274== by 0x4FC066B: CallSystemFunction(int, asCContext*, void*) (in /usr/lib/libangelscript-2.23.1.so)
==30274== by 0x4FFAC34: asCContext::ExecuteNext() (in /usr/lib/libangelscript-2.23.1.so)
==30274== by 0x4FFB9B7: asCContext::Execute() (in /usr/lib/libangelscript-2.23.1.so)
==30274== by 0x80D5575: Npc::Run(float) (npc.cpp:43)
==30274== by 0x80DACAD: _ZZN5Level7do_taskEvENKUlP10ObjectNodeE_clES1_ (level.cpp:146)
==30274== by 0x80DBB4D: _ZSt8for_eachISt14_List_iteratorIP10ObjectNodeEZN5Level7do_taskEvEUlS2_E_ET0_T_S7_S6_ (stl_algo.h:4442)
==30274== by 0x80DB0E8: Level::do_task() (level.cpp:147)
==30274== by 0x466FC87: AsyncTask::unlock_and_do_task() (in /usr/lib/panda3d/libpanda.so.1.8)
==30274== by 0x10A: ???
==30274== Address 0x7c993c8 is 4,064 bytes inside a block of size 4,096 alloc'd
==30274== at 0x402B018: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30274== by 0x4FFBE19: asCContext::Prepare(asIScriptFunction*) (in /usr/lib/libangelscript-2.23.1.so)
==30274== by 0x80DACAD: _ZZN5Level7do_taskEvENKUlP10ObjectNodeE_clES1_ (level.cpp:146)
==30274== by 0x80DBB4D: _ZSt8for_eachISt14_List_iteratorIP10ObjectNodeEZN5Level7do_taskEvEUlS2_E_ET0_T_S7_S6_ (stl_algo.h:4442)
==30274== by 0x80DB0E8: Level::do_task() (level.cpp:147)
==30274== by 0x466FC87: AsyncTask::unlock_and_do_task() (in /usr/lib/panda3d/libpanda.so.1.8)
==30274== by 0x10A: ???




I have good hope that this is the last problem I'll ever have with AS (I already exposed all the interesting parts of my engine's API without issue, that's the last of it : the DataEngine).

This one looks a bit trickier than the chicken/egg issue, but I hope therre's enough indication to see where I did wrong ! Can I have help pretty please :) ?: because when AS calls my C%2
I'll need a bit more information in order to figure out whats wrong here.

How has the 'Data' type been registered with the engine? I especifically need to see the RegisterObjectType call, and any RegisterObjectBehaviour calls, as well as know how you registered the 'opAssign' method (if you have registered it).

Also, as it might be a bug in the compiler with regards to the opIndex behaviour, can you make a a test by calling the opIndex method explicitly? i.e. '[font=courier new,courier,monospace]ret = var.opIndex('test');[/font]' instead of '[font=courier new,courier,monospace]ret = var['index'];[/font]'?

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

Here they go !

engine->RegisterObjectType (dataClass, sizeof(Data), asOBJ_VALUE | asOBJ_APP_CLASS);
engine->RegisterObjectBehaviour(dataClass, asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(asData::Constructor), asCALL_CDECL_OBJLAST);
engine->RegisterObjectBehaviour(dataClass, asBEHAVE_DESTRUCT, "void f()", asFUNCTION(asData::Destructor), asCALL_CDECL_OBJLAST);
engine->RegisterObjectMethod (dataClass, "const Data& opAssign(const Data &in)", asMETHODPR(Data,operator=, (const Data&), const Data&), asCALL_THISCALL);
engine->RegisterObjectMethod (dataClass, "Data& opIndex(string &in)", asMETHODPR(Data,operator[], (const std::string&), Data), asCALL_THISCALL);


I tried the explicit call to opIndex and it behaves the same.
I see two possible causes for problems you're facing.

1. I suspect that the C++ object Data has a constructor and possibly a destructor too. I know it has an assignment operator. But you have told AngelScript that it has neither of these with the flag asOBJ_APP_CLASS. The correct use of the asOBJ_APP_CLASS_... flag is very important when functions return value types by value or take them by value. If this flag is not correctly informed to AngelScript then you may experience crashes, and other less obvious errors as AngelScript will not pass the type exactly how C++ expects it.

2. Your C++ operator[] method is returning the type by value, but you have registered the method as if it is returning it by reference. This obviously won't work. I suspect this is likely a mistake as you've been experimenting with different ways to get around the problem caused by the incorrect asOBJ_APP_CLASS flag.

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

Advertisement
Sweet lord you're right ! I had completly forgotten about those flags (I pretty much went over that part of the documentation because at first I thought only asOBJ_APP_CLASS would be usefull to me). I apologize for my lack of attention.

In any way, thank you ! I'm really glad I fell on this AngelScript ! It's a life saver !

This topic is closed to new replies.

Advertisement