member functions that return an object gets invalid parameter

Started by
10 comments, last by Deyja 18 years, 3 months ago
As before, I am compiling with AS_ALLOW_UNSAFE_REFERENCES. It crashes when calling foo_member_fun_one. free_fun and foo_member_fun_two both work fine. The problem seems to be that when a member function returns an object the first parameter is corrupted. The this pointer is valid. But the string reference is not.

#include "angelscript.h"
#include <string>
#include <iostream>

struct Foo 
{
	Foo() {}
	~Foo() {}
	Foo(const Foo& rhs) {}
	Foo& operator=(const Foo& rhs) { return *this; }
};

//THIS CRASHES.
std::string foo_member_fun_one(const std::string& in, Foo* thisp)
{
	std::cout << "foo_member_fun_one: " << in << "\n";
	return in;
}

void foo_member_fun_two(const std::string& in, Foo* thisp)
{
	std::cout << "foo_member_fun_two: " << in << "\n";
}

std::string free_fun(const std::string& in)
{
	std::cout << "free_fun: " << in << "\n";
	return in;
}

void ConstructFoo(Foo* ptr) { new (ptr) Foo(); }
void CopyConstructFoo(const Foo& rhs, Foo* ptr) { new (ptr) Foo(rhs); }
void DestroyFoo(Foo* ptr) { ptr->~Foo(); } 
Foo& AssignFoo(const Foo& rhs, Foo* ptr) { return (*ptr) = rhs; }

void ConstructString(std::string* ptr) { new (ptr) std::string(); }
void CopyConstructString(const std::string& rhs, std::string* ptr) { new (ptr) std::string(rhs); }
void DestroyString(std::string* ptr) { ptr->~basic_string(); } 
std::string& AssignString(const std::string& rhs, std::string* ptr) { return (*ptr) = rhs; }

std::string StringFactory(unsigned int length, const char *s)
	{
		std::cout << "StringFactory: " << std::string(s,length) << "\n";
		return std::string(s,length);
	}

int main(int argc, char* argv[])
{
	asIScriptEngine* engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
	int error_code = 0;
	
	std::cout << "Registering Foo. ";
	error_code = engine->RegisterObjectType("Foo",sizeof(Foo),asOBJ_CLASS_CDA);
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering Foo constructor. ";
	error_code = engine->RegisterObjectBehaviour("Foo",
		asBEHAVE_CONSTRUCT,
		"void constructor()",
		asFUNCTION(ConstructFoo),
		asCALL_CDECL_OBJLAST);	
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering Foo destructor. ";	
	error_code = engine->RegisterObjectBehaviour("Foo",
		asBEHAVE_DESTRUCT,
		"void destructor()",
		asFUNCTION(DestroyFoo),
		asCALL_CDECL_OBJLAST);	
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering Foo operator=. ";
	error_code = engine->RegisterObjectBehaviour("Foo",
		asBEHAVE_ASSIGNMENT,
		"Foo& op_assign(const Foo&)",
		asFUNCTION(AssignFoo),
		asCALL_CDECL_OBJLAST);	
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering Foo copy constructor. ";
	error_code = engine->RegisterObjectBehaviour("Foo",
		asBEHAVE_CONSTRUCT,
		"void constructor(const Foo&)",
		asFUNCTION(CopyConstructFoo),
		asCALL_CDECL_OBJLAST);					
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering string. ";
	error_code = engine->RegisterObjectType("string",sizeof(std::string),asOBJ_CLASS_CDA);
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering string constructor. ";
	error_code = engine->RegisterObjectBehaviour("string",
		asBEHAVE_CONSTRUCT,
		"void constructor()",
		asFUNCTION(ConstructString),
		asCALL_CDECL_OBJLAST);	
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering string destructor. ";	
	error_code = engine->RegisterObjectBehaviour("string",
		asBEHAVE_DESTRUCT,
		"void destructor()",
		asFUNCTION(DestroyString),
		asCALL_CDECL_OBJLAST);	
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering string operator=. ";
	error_code = engine->RegisterObjectBehaviour("string",
		asBEHAVE_ASSIGNMENT,
		"string& op_assign(const string&)",
		asFUNCTION(AssignString),
		asCALL_CDECL_OBJLAST);	
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering string copy constructor. ";
	error_code = engine->RegisterObjectBehaviour("string",
		asBEHAVE_CONSTRUCT,
		"void constructor(const string&)",
		asFUNCTION(CopyConstructString),
		asCALL_CDECL_OBJLAST);					
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering string factory. ";
	error_code = engine->RegisterStringFactory("string",
		asFUNCTION(StringFactory),
		asCALL_CDECL);
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering Foo member fun one. ";
	error_code = engine->RegisterObjectMethod("Foo",
		"string member_one(const string&)",
		asFUNCTION(foo_member_fun_one),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering Foo member fun two. ";
	error_code = engine->RegisterObjectMethod("Foo",
		"void member_two(const string&)",
		asFUNCTION(foo_member_fun_two),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nRegistering free fun. ";
	error_code = engine->RegisterGlobalFunction(
		"string free_fun(const string&)",
		asFUNCTION(free_fun),
		asCALL_CDECL);
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::string script = "void test(Foo f) { free_fun(\"foo\"); f.member_two(\"foo\"); f.member_one(\"foo\"); }";
	engine->AddScriptSection(0,"test",script.c_str(),script.length());
	
	std::cout << "\nBuilding test script. ";
	error_code = engine->Build(0);
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nGetting function id. ";
	int func_id = engine->GetFunctionIDByName(0,"test");
	if (func_id < 0) std::cout << error_code;

	asIScriptContext* ctx = engine->CreateContext();
	std::cout << "\nPreparing context. ";
	error_code = ctx->Prepare(func_id);
	if (error_code < 0) std::cout << "Error: " << error_code;

	Foo f;

	std::cout << "\nSetting parameter. ";
	error_code = ctx->SetArgObject(0,&f);
	if (error_code < 0) std::cout << "Error: " << error_code;

	std::cout << "\nCalling script.\n";
	error_code = ctx->Execute();
	if (error_code != asEXECUTION_FINISHED) std::cout << "Error: " << error_code;

	std::cout << "\n\nFin.";	

	return 0;
}

Advertisement
I'll look into it. Thanks for providing the example for me.

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

I've ran into this issue also. Although I thought it was due to the way my environment was set up, the objects were being created in a dll, and being manipulated by angelscript which resides in the .exe

I traced the problem down to when angelscript calls the destructor on the object during a temporary copy of some sort, which I attributed to cross module boundary object lifetime issues.

My issue may however be unrelated.
You may want to register the memory management functions with AngelScript if this is your problem. This can be done on both a global level and individually for each registered object type.

Doing this will make sure AngelScript uses the same heap for memory allocation as the application.

See the following functions for more info:

engine::SetCommonObjectMemoryFunctions()
engine::RegisterObjectBehaviour() with behaviours asBEHAVE_ALLOC and asBEHAVE_FREE

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

WL; was that referring to me or raindog? My example doesn't crash in the destructor, and the parts that call it work fine.
In that case I was referring to Rain Dog. [wink]

Actually in your other thread you mentioned something that could be related to the memory management, so I intended the response for both of you. Mostly as something to think about.

I still pretend to test the problem you reported in this thread, but I haven't had the time to do so yet.

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

I do hope you meant 'intend'. O.o
In this context 'pretend to' is the same as 'intend to'. At least that is how I understand it. [wink]

Anyway, I found the bug. It's an old bug so I'm a bit surprised that it hasn't shown up before. On line 260 in as_callfunc_x86.cpp the function pointer is initialized incorrectly. It should be:

const t_CallCDeclObjRetByRef CallCDeclFunctionRetByRefObjLast = (t_CallCDeclObjRetByRef)CallCDeclFunctionRetByRefObjLast_impl;


Regards,
Andreas

[edit]Updated file name[/edit]

[Edited by - WitchLord on January 13, 2006 7:12:39 AM]

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

Hurray!
Incidentally, you meant as_callfunc_x86.cpp. There is no as_context_x86.cpp.

This topic is closed to new replies.

Advertisement