Sign in to follow this  
Nalin

64-bit issues with a string class

Recommended Posts

Nalin    100
I've been using AngelScript 2.18.1 in my project and so far it has been great. I've been developing it on Windows just fine. My project will be cross-platform, so I started work on the Linux version when I hit a snag. You see, my Linux computer is 64-bit, so all the libraries, compilers, etc default to 64-bit. This is when I started to run into my 64-bit issues. I had been using the scriptstdstring addon to bind std::string to AngelScript. However, do to some odd set of circumstances, AngelScript was crashing when it was trying to destruct the std::string. It was somehow related to passing it by reference. When I passed it by value, it only sort of worked. My "Update" function, which is called every frame, would silently stop working once it came across the string, yet my "OnKeyPress" function would pass the string just fine. Thinking it was a 64-bit issue, I went back to my Windows machine and had Visual Studio compile my project as a 64-bit application. I hadn't bothered to compile boost in 64-bit on Windows, so I had been developing it in 32-bit mode, and it worked great. Once I got all my libraries compiled as 64-bit, I compiled and ran my program. AngelScript passed random garbage as the string. Thinking it was just an issue with using std::string, I bound a different string class to AngelScript. This time, AngelScript passed an empty string and crashed when it tried to destruct it. This is the script that I am executing: http://nalin.pastebin.com/m7ea1ae65 In "Update", once AngelScript hit "obj.Animation = "walk.gani";", it would silently stop executing the script. However, in OnKeyPress, it would behave like normal. Is there anything I can do to further discover exactly what is going wrong?

Share this post


Link to post
Share on other sites
WitchLord    4678
Can you show me how the SceneObject is registered? And especially the Animation property?


This does indeed look like a bug in AngelScript, and I will investigate this further.

Regards,
Andreas

Share this post


Link to post
Share on other sites
Nalin    100
Sure, here is some of the source code:
http://nalin.suckerfree.org/public/code/rha/

asEngine.h/cpp binds Engine to AngelScript, while asSceneObject.h/cpp binds the SceneObject (from CSceneObject.h.)

It is currently using Irrlicht's string class, as binding std::string via the scriptstdstring add-on wasn't working. asString.h/cpp is the code to bind it. irrString.h is the Irrlicht string class (just in case you need to see it.)

If you need anything else, just ask. I can give you the complete project if need be.

---

I just now tried removing the reference from "void set_Animation(const string &in)", so it would just be "const string", and it fixes the crash. But it goes back to one of my original problems where the script just stops executing once it hits that function.

Share this post


Link to post
Share on other sites
WitchLord    4678
I haven't been able to reproduce this problem.

I wrote the following code to test this:


class CMyObj
{
public:
CMyObj() { refCount = 1; }
void set_Text(const string &s)
{
assert( s == "Hello world!" );
}

void AddRef() { refCount++; }
void Release() { if( --refCount == 0 ) delete this; }

int refCount;
};

CMyObj *MyObj_factory()
{
return new CMyObj;
}

bool Test2()
{
bool fail = false;
COutStream out;
int r;

asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL);

RegisterStdString(engine);

engine->RegisterObjectType("CMyObj", 0, asOBJ_REF);
engine->RegisterObjectBehaviour("CMyObj", asBEHAVE_FACTORY, "CMyObj @f()", asFUNCTION(MyObj_factory), asCALL_CDECL);
engine->RegisterObjectBehaviour("CMyObj", asBEHAVE_ADDREF, "void f()", asMETHOD(CMyObj, AddRef), asCALL_THISCALL);
engine->RegisterObjectBehaviour("CMyObj", asBEHAVE_RELEASE, "void f()", asMETHOD(CMyObj, Release), asCALL_THISCALL);
engine->RegisterObjectMethod("CMyObj", "void set_Text(const string &in)", asMETHOD(CMyObj, set_Text), asCALL_THISCALL);

const char *string =
"void main() { \n"
" CMyObj @obj = @CMyObj(); \n"
" obj.Text = 'Hello world!'; \n"
"} \n";
asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE);
mod->AddScriptSection("string", string);
r = mod->Build();
if( r < 0 )
fail = true;

r = ExecuteString(engine, "main()", mod);
if( r != asEXECUTION_FINISHED )
fail = true;

engine->Release();

return fail;
}




The CMyObj::set_Text property accessor is called with a proper string object.

I'm using MSVC++ 2008 Express and Windows 7 64bit to do these tests. What compiler and operative system are you using?

Share this post


Link to post
Share on other sites
Nalin    100
On Windows, I am using Visual C++ 2010 Beta 2.
On Linux, I am using g++ version 4.4.1.

I'm currently not at home right now, so I don't have access to a 64-bit Windows computer. I've attempted to walk through my issue on 64-bit Linux via gdb in an attempt to figure out why it is failing, but didn't succeed as I am still not very familiar with all of the inner workings of AngelScript. The result of that is here:
http://nalin.pastebin.com/m1dfb1e30

Here is a bytecode dump from my Linux build:
http://suckerfreegames.com/as/test.raw.lin64

It seems like AngelScript isn't storing/retrieving the string correctly, but I can't be too sure. When I get back home later on tonight, I'll work on getting you some code to reproduce it.

Share this post


Link to post
Share on other sites
Nalin    100
Well, I found the problem. It was related to this line:
r = engine->RegisterObjectMethod("GameEngine", "bool keydown(EKEY_CODE)",
asMETHOD(CGame, key_pressed), asCALL_THISCALL); assert(r >= 0);

Here is the code that defines EKEY_CODE:
http://sfg.pastebin.com/m47bc91f9

The full GameEngine bind can be found here:
http://nalin.suckerfree.org/public/code/rha/asEngine.cpp

This is the CGame::key_pressed function:
bool key_pressed(EKEY_CODE key)
{
if ((u32)key >= KEY_KEY_CODES_COUNT) return false;
return KeyState[(u32)key];
}

I would call "keydown" in my script like so:
if (Engine.keydown(KEY_UP))
{ move.Y += 1.0f; dir = 0; domove = true; }

For some reason, AngelScript was choking on this use of enum when compiled as 64-bit code. To fix it, I changed the AngelScript "keydown" function to take a uint instead of an EKEY_CODE. I then bound it to this function:
bool Engine_keydown(unsigned int key, CGame& obj)
{
return obj.key_pressed((EKEY_CODE)key);
}

AngelScript converts the enum to a uint and passes it to the function, which turns it back into an enum. It is now perfect and I don't crash anymore.

Share this post


Link to post
Share on other sites
Nalin    100
Quote:
Original post by slugear
just FYI, you are registering key_pressed ( not Engine_keydown ) as keydown

Yes, that is what I had BEFORE my fix.

Before fix:
r = engine->RegisterObjectMethod("GameEngine", "bool keydown(EKEY_CODE)",
asMETHOD(CGame, key_pressed), asCALL_THISCALL); assert(r >= 0);


After fix:
r = engine->RegisterObjectMethod("GameEngine", "bool keydown(uint)",
asFUNCTION(Engine_keydown), asCALL_CDECL_OBJLAST); assert(r >= 0);

...

bool Engine_keydown(unsigned int key, CGame& obj)
{
return obj.key_pressed((EKEY_CODE)key);
}

Share this post


Link to post
Share on other sites
WitchLord    4678
Sorry for taking so long to look into this. I made another attempt to reproduce this problem with your latest information, but unfortunately I'm still not able to. This is the code I wrote to test the scenario:


enum TEST_ENUM
{
ENUM1 = 1,
ENUM2 = ENUM1*10,
ENUM3,
ENUM4 = -1
};

class CTestObject
{
public:
bool TestEnum(TEST_ENUM e) { val = e; if( e == ENUM2 ) return true; else return false; }

TEST_ENUM val;
};

bool Test()
{
asIScriptEngine *engine;
CBufferedOutStream bout;
int r;
bool fail = false;

engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

bout.buffer = "";
r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL);

r = engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC);

// Register the enum value
r = engine->RegisterEnum("TEST_ENUM"); assert(r >= 0);
r = engine->RegisterEnumValue("TEST_ENUM", "ENUM1", ENUM1); assert(r >= 0);
r = engine->RegisterEnumValue("TEST_ENUM", "ENUM2", ENUM2); assert(r >= 0);
r = engine->RegisterEnumValue("TEST_ENUM", "ENUM3", ENUM3); assert(r >= 0);

// Test enum in param to class method
assert( sizeof(TEST_ENUM) == 4 );
r = engine->RegisterObjectType("Obj", 0, asOBJ_REF | asOBJ_NOHANDLE); assert( r >= 0 );
r = engine->RegisterObjectMethod("Obj", "bool TestEnum(TEST_ENUM)", asMETHOD(CTestObject, TestEnum), asCALL_THISCALL); assert( r >= 0 );

CTestObject obj;
obj.val = ENUM1;

r = engine->RegisterGlobalProperty("Obj obj", &obj); assert( r >= 0 );

bout.buffer = "";
r = ExecuteString(engine, "if( !obj.TestEnum(ENUM2) ) assert(false); ");
if( r != asEXECUTION_FINISHED )
fail = true;
if( bout.buffer != "" )
{
printf(bout.buffer.c_str());
fail = true;
}
if( obj.val != ENUM2 )
fail = true;

engine->Release();

return fail;
}



Share this post


Link to post
Share on other sites
Nalin    100
Here is an example I made on 64-bit Linux:
http://suckerfreegames.com/_as/as_bug.zip

I used the projects/gnuc/makefile to compile AngelScript. I then compiled my example with:
g++ main.cpp scriptstdstring.cpp -langelscript

The program will generate a segmentation fault and crash.

I added two versions of the problem function. If you switch the comment to the first line, you can see that the enum-to-int conversion workaround doesn't cause a crash:

r = engine->RegisterGlobalFunction("bool TestEnum(TEST_ENUM)", asFUNCTION(TestEnum), asCALL_CDECL); assert(r >= 0);
//r = engine->RegisterGlobalFunction("bool TestEnum(int)", asFUNCTION(TestEnum2), asCALL_CDECL); assert(r >= 0);



Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this