# Function parameter problem

## Recommended Posts

Heuristics    147
Hello!

I am new to angelscript and I have a problem where the first parameter to a registered object method is always the object itself. What can I do to fix this? Here is some example code:

[source lang="cpp"]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);
}
[/source]

[source lang="cpp"]class Person {
public:
Person() {}
void printTwoInts(int a, int b);
void ReleaseRef() {}
static Person *refFactory() {
return new Person();
};

};
Person::printTwoInts(int a, int b){
cout (int)this " " a " " b endl;
}
(had to remove left arrows for cout in the above code above)
[/source]

[source lang="cpp"]int main() {
asIScriptEngine *scriptEngine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
int r = scriptEngine->SetMessageCallback(asFUNCTION(MessageCallback_), 0, asCALL_CDECL);assert( r >= 0 );
r = scriptEngine->RegisterObjectType("Person", sizeof(Person), asOBJ_REF); assert( r >= 0 );
r = scriptEngine->RegisterObjectBehaviour("Person", asBEHAVE_FACTORY, "Person @f()", asFUNCTION(Person::refFactory), asCALL_CDECL); assert( r >= 0 );
r = scriptEngine->RegisterObjectBehaviour("Person", asBEHAVE_ADDREF, "void f()", asMETHOD(Person, AddRef), asCALL_THISCALL); assert( r >= 0 );
r = scriptEngine->RegisterObjectBehaviour("Person", asBEHAVE_RELEASE, "void f()", asMETHOD(Person, ReleaseRef), asCALL_THISCALL); assert( r >= 0 );

r = scriptEngine->RegisterObjectMethod("Person", "void printTwoInts(int a, int b)", asMETHOD(Person, printTwoInts), asCALL_THISCALL);assert( r >= 0 );

CScriptBuilder builder;
r = builder.StartNewModule(scriptEngine, "Test"); assert( r >= 0 );
r = builder.AddSectionFromFile("./Test.as"); assert( r >= 0 );
r = builder.BuildModule();if( r < 0 ); assert( r >= 0 );

asIScriptModule *mod = scriptEngine->GetModule("Test");
asIScriptFunction *func = mod->GetFunctionByDecl("void main()");
asIScriptContext *ctx = scriptEngine->CreateContext();
ctx->Prepare(func);
r = ctx->Execute();
return 0;
}[/source]

The Test.as script is as:

[source lang="cpp"]void main() {
Person p;
p.printTwoInts(999,55);
}[/source]

When run this outputs: "8097752 8097752 999" Meaning the first parameter is not an int but an object pointer. So the function calling is done wrong? But when I try to change asCALL_THISCALL to asCALL_CDECL, asCALL_STDCALL, asCALL_CDECL_OBJLAST, asCALL_CDECL_OBJFIRST or asCALL_GENERIC I get an error code: -24. -24 is asWRONG_CALLING_CONV but I don't know how to fix that. Do I need to use something else other then asMETHOD when registering the function? If so what?

I am using mingw 4.7.0.

Thanks. Edited by Andreas Jonsson

##### Share on other sites
WitchLord    4678
You did everything correctly.

I think this might be a problem with the native calling conventions support specifically for MinGW. Possibly just for the version of MinGW that you're using.

Until I've figured out what the problem is, I suggest you work around it by using the [url="http://www.angelcode.com/angelscript/sdk/docs/manual/doc_addon_autowrap.html"]auto wrappers[/url]. To do this, include the /sdk/add_on/autowrapper/aswrappedcall.h header, replace the asMETHOD macro with WRAP_MFN, and asCALL_THISCALL with asCALL_GENERIC.

Or switch to Microsoft Visual C++. The Express version is free, and can be downloaded from Microsoft's home page. Unless you have a very specific motive for using MinGW, I recommend looking into using MSVC even if you weren't facing the problem above.

##### Share on other sites
WitchLord    4678
It looks like MinGW may have changed the way they implement the thiscall calling convention in this version. Earlier versions of MinGW used to work like GNUC does on Linux, in that the object pointer is put in the ECX register and pushed on the stack. Probably the MinGW developers decided to change this to work like MSVC do, i.e. only move the object pointer into the ECX register. Presumably they did this to make MinGW generated code compatible with MSVC generated dlls.

If I'm right, I'll have to make some changes in AngelScript to verify the version of MinGW used and adapt the code accordingly.

##### Share on other sites
WitchLord    4678
[url="http://lists-archives.com/mingw-users/23557-mingw-gcc-4-7-0-released.html"]The release notification to the MinGW mailing lists, confirms that version 4.7.0 changed the C++ ABI for thiscall methods[/url]. So that explains why you're facing the problem that you do.

I believe the change to make AngelScript compatible with the new version of MinGW is simple, but unfortunately I will not have time to validate it myself right now, as I'm about to leave for vacation.

If you feel like giving it a try yourself the changes I believe are needed are to remove a few lines from the as_callfunc_x86.cpp file:

In function CallThisCallFunction, remove the lines:

1094: "pushl %%ecx \n" // push obj on the stack
1097: "addl $4, %%esp \n" // pop obj In function CallThisCallFunctionRetByRef, remove the lines: 1218: "pushl %%ecx \n" // push obj on the stack 1225: "addl$4, %%esp \n" // pop the object pointer

Removing these 4 lines should hopefully make things work again in MinGW 4.7.0.

Of course, before I check-in these changes to the SVN I need to confirm that they actually work, and the change also needs to made in such a way that only MinGW 4.7.0 is affected, otherwise it will break for earlier versions of MinGW and gcc. Edited by Andreas Jonsson

##### Share on other sites
Heuristics    147
[quote name='Andreas Jonsson' timestamp='1348403016' post='4982892']

Removing these 4 lines should hopefully make things work again in MinGW 4.7.0.
[/quote]

Thank you! I tested this (with my simple example above in mingw 4.7.0) and it appears to work. Hope you have a nice vacation

##### Share on other sites
WitchLord    4678
I've checked in the fix with revision 1428.

Thanks for the help in confirming the fix.

##### Share on other sites
Heuristics    147
[quote name='Andreas Jonsson' timestamp='1349630981' post='4987717']
I've checked in the fix with revision 1428.

Thanks for the help in confirming the fix.
[/quote]

Hello again. Thanks for doing these changes.

I have been looking in to this a bit more and I am starting to wonder if the addref/releaserefs are called correctly (I haven't yet checked this for when registering as value).

Here is the code example again: [url="http://pastebin.com/KjsSrJPP"]http://pastebin.com/KjsSrJPP[/url] this time in compilable form in case you want to test this yourself.

What happens when I run this with mingw 4.7.0 is that some of the output of the application is correct (the class constructor is called and the class executes its printTwoInts function correctly) but ReleaseRef does not get called, also ctx->Execute() crashes with eip having the value 0xababababa.

when compiling and running it with mingw 4.6.2 the ReleaseRef function gets called correctly and no crash (that is everything is fine). Edited by Heuristics

##### Share on other sites
WitchLord    4678
MinGW 4.7 probably has more changes to the ABI then.

Can you run the test_feature project with MinGW 4.7 and tell me which tests pass, and which don't? Especifically the tests focused on the native calling conventions (starting at line 324 in main.cpp). You'll find the code for the project in the SVN.

This will help me figure out what needs to be modified to add compatibility for MinGW 4.7.

##### Share on other sites
Heuristics    147
[quote name='Andreas Jonsson' timestamp='1349791382' post='4988349']
MinGW 4.7 probably has more changes to the ABI then.

Can you run the test_feature project with MinGW 4.7 and tell me which tests pass, and which don't? Especifically the tests focused on the native calling conventions (starting at line 324 in main.cpp). You'll find the code for the project in the SVN.

This will help me figure out what needs to be modified to add compatibility for MinGW 4.7.
[/quote]

I am unable to get that project to compile, I tried the codeblocks, cmake and mingw projects. The mingw one gives: 4.6.3/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible ./libangelscript.a when searching for -langelscript
The codeblocks project is unable to find 3 .o files and the cmake project cannot find the angelscript.h file

##### Share on other sites
WitchLord    4678
The project files needs adjusting. I only use the MSVC6, MSVC9, and gnuc projects myself so those are the only ones I can guarantee are working out-of-the-box.

Looks like the mingw project is trying to compile the test project with a different target architecture than the library was compiled with.

The codeblocks project obviously need to have the files updated. There has several additions and changes since that project was last updated.

The cmake appears to lack information about the search path. Possibly and environment variable needs to be set in order to use it.

##### Share on other sites
WitchLord    4678
I've updated the code::blocks project files in revision 1431.

I tried installing mingw too, but the installer from the mingw site appears to only install version 4.6, not 4.7. The mingw version that comes with code::blocks is even older.

Where do you get version 4.7 from? Is it necessary to download each file manually?

##### Share on other sites
Heuristics    147
I think I did something like getting this file: http://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/mingw-get-inst-20120426/
and during the installation I picked "download latest repository catalogues". From checking now that appears to download mingw 4.7

##### Share on other sites
WitchLord    4678
Thanks, I'll give that a try.

##### Share on other sites
WitchLord    4678
I'm making some progress.

However, there are more changes in MinGW 4.7 than I had expected. It will be a while longer before I can complete all the changes and tests needed to get this fully working.

##### Share on other sites
WitchLord    4678
It appears that I made a break-through. As of revision 1435, all tests pass successfully with MinGW32 4.7.0 and 4.6.2.

The MinGW developers made some strange choices with the new ABI. For the most part they seem to have wanted to more closely follow the MS ABI, most likely to make it easier to use the many dlls available, but in some cases they ended up with something that is neither like the MS ABI nor the GNUC ABI. This doesn't make any sense at all to me, and quite frankly I believe they may very well change it again with a future version.

##### Share on other sites
Heuristics    147
Great to hear!

I tested it as well with my own code and it does indeed appear to work. Thanks a lot, I very much appreciate the very quick fix to this issue. I now get to use fancy c++11 features with angelscript

Strange about the ABI though, but then again I know little about such things. But I do know that it is sometimes hard to understand how the GNU devs pick their priorities.

##### Share on other sites
Heuristics    147
Hello again.

I extended my little test code with using a class as value instead of as reference and then it crashes in mingw 4.7.0 but works fine in 4.6.0. Perhaps this is another problem? (or I might just be doing it wrong )

Here is the modified example: http://pastebin.com/27BzeQcy

##### Share on other sites
WitchLord    4678
That it works on 4.6, but not on 4.7 does seem to indicate the problem is related to the changes done in MinGW, but I'll need to investigate it to be sure.

I'll let know when I figure it out. Thanks.

##### Share on other sites
WitchLord    4678
The problem was not with MinGW after all.

You've implemented the constructor and destructor incorrectly. They should be global functions, or static members, and registered with asCALL_CDECL_OBJLAST instead of asCALL_THISCALL.

It didn't crash on MinGW 4.6.0 because before 4.7 the thiscall was practically identical to the cdecl calling convention.

[code]
class Person {
public:
Person() {cout<<(int)this<<endl;this->counter = 1;}
static void Constructor(void *memory) {new(memory) Person();}
static void Destructor(void *memory) {((Person*)memory)->~Person();}
void printTwoInts(int a, int b) { cout << (int)this << " " << a << " " << b << endl; }
void AddRef() {cout<<this<<" 1 "<<this->counter;}
void ReleaseRef() {cout<<this<<" 2 "<<this->counter;}
static Person *refFactory() {
return new Person();
};
int counter;
};

void main()
{
...
asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL);
r = engine->RegisterObjectType("Person", sizeof(Person), asOBJ_VALUE | asOBJ_POD); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("Person", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Person::Constructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("Person", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Person::Destructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );

r = engine->RegisterObjectMethod("Person", "void printTwoInts(int a, int b)", asMETHOD(Person, printTwoInts), asCALL_THISCALL);assert( r >= 0 );
}
[/code]

Regards,
Andreas

##### Share on other sites
Heuristics    147
Ah!

That makes sense, I feel a bit silly now (was mislead by it not crashing on 4.6.0). Sorry about that. But thank you very much for your help again!

## 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