compiler: mingw 4.7.2
os: Windows 7
angelscript: official 2.26.1
I have a problem that I cannot solve, I am hoping that I am just registering something wrong.
The short story of the symptoms:
When registering a struct as a value type the value of 'this' changes by a few bytes if a called member function of that struct is returning another registered struct value type, 'this' does not change when returning nothing (void) or when returning float.
This bug goes away if at least 3 int (for example) member variables are added to the registered value struct that is returned, if so then 'this' does not change.
----------------------
Here is a small example that illustrates the problem.
We have 2 structs (Dummy and Test) and we register them in angelscript. 'Test' has a debug printing of 'this' at every function that gets run from it. We then run these lines in angelscript:
void main() {
Test test;
test.noReturnValue();
test.floatReturnValue();
test.getDummy();
}
This run outputs:
Constructor self address: 0x60e7c4
This print from constructor: 0x60e7c4
This print from noReturnValue function: 0x60e7c4
This print from floatReturnValue function: 0x60e7c4
This print from getDummy function: 0x60e7bc
The application also crashes at the end of the run.
The last lines shows that 'this' changes slightly due to that function call returning a 'Dummy' value.
Here is an example program that is as small as I could make it that still shows the problem (this is c++11 code).
#include <angelscript.h>
#include <add_on/scriptstdstring/scriptstdstring.h>
#include <add_on/scriptbuilder/scriptbuilder.h>
#include <stdio.h>
#include <assert.h>
#include <iostream>
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);
}
void print(const std::string &in) {
std::cout << in << std::endl;
}
struct Dummy {
static void Constructor(Dummy *self) {new(self) Dummy();}
static void Destructor(Dummy *memory) {memory->~Dummy();}
//int a, b, c; // uncomment this line for it to work again
//int a, b; // will not work, requires at least 3 extra vars
};
struct Test {
Test() {
std::cout << "This print from constructor: " << this << std::endl;
}
static void Constructor(Test *self) {std::cout << "Constructor self address: " << self << std::endl;new(self) Test();}
static void Destructor(Test *memory) {memory->~Test();}
void noReturnValue() {
std::cout << "This print from noReturnValue function: " << this << std::endl;
}
float floatReturnValue() {
std::cout << "This print from floatReturnValue function: " << this << std::endl;
return 5.6;
}
Dummy getDummy() {
std::cout << "This print from getDummy function: " << this << std::endl;
return Dummy();
}
};
template<typename T>
void reg(asIScriptEngine *engine, std::string name) {
int r = engine->RegisterObjectType(name.c_str(), sizeof(T), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK ); assert( r >= 0 );
r = engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_CONSTRUCT, std::string("void f()").c_str(), asFUNCTION(T::Constructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_DESTRUCT, "void f()", asFUNCTION(T::Destructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
}
std::string app = R"(void main() {
Test test;
test.noReturnValue();
test.floatReturnValue();
test.getDummy();
})";
int main() {
asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
int r = engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL); assert( r >= 0 );
RegisterStdString(engine);
r = engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(print), asCALL_CDECL); assert( r >= 0 );
reg<Dummy>(engine, "Dummy");
reg<Test>(engine, "Test");
r = engine->RegisterObjectMethod("Test", "Dummy getDummy()", asMETHOD(Test,getDummy), asCALL_THISCALL);assert( r >= 0 );
r = engine->RegisterObjectMethod("Test", "void noReturnValue()", asMETHOD(Test,noReturnValue), asCALL_THISCALL);assert( r >= 0 );
r = engine->RegisterObjectMethod("Test", "float floatReturnValue()", asMETHOD(Test,floatReturnValue), asCALL_THISCALL);assert( r >= 0 );
CScriptBuilder builder;
r = builder.StartNewModule(engine, "MyModule"); if( r < 0 ) {printf("Unrecoverable error while starting a new module.\n");return 0;}
r = builder.AddSectionFromMemory("TestSection", app.c_str());if( r < 0 ) {printf("Please correct the errors in the script and try again.\n");return 0;}
r = builder.BuildModule(); if( r < 0 ) {printf("Please correct the errors in the script and try again.\n");return 0;}
asIScriptModule *mod = engine->GetModule("MyModule");
asIScriptFunction *func = mod->GetFunctionByDecl("void main()"); if( func == 0 ) {printf("The script must have the function 'void main()'. Please add it and try again.\n");return 0;}
asIScriptContext *ctx = engine->CreateContext();
ctx->Prepare(func);
r = ctx->Execute(); if( r != asEXECUTION_FINISHED and r == asEXECUTION_EXCEPTION) {printf("An exception '%s' occurred. Please correct the code and try again.\n", ctx->GetExceptionString());}
}