AngelScript 2.6.0 released
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
Point AddPoints(Point p1, Point p2)
{
Point p3 = p1;
p3.x += p2.x;
p3.y += p2.y;
return p3;
}
The crash occurs somewhere within the execution of the function, specifically, when the last reference of the parameters passed to the function are released with a count of 1, resulting in an attempted deletion. However I am passing the objects by value so I am confused why the script engine is attempting to release references. It is acting as if I passed the object by reference and not by value.
If it is of any help, I have not changed my argument setting methods and still use those from 2.5.0 backwards i.e. SetArgObjects for native types, objects and references. Could this be related to the problem? Actually I'm not exaclty sure what is the difference between SetArgObject and the new SetArgAddress.
Well, since it was declared to take the arguments by value you should use the method SetArgObject() to pass in the values. This method will then create a copy of your object and put their addresses on the script stack. When the script function ends, it will release these created objects.
SetArgAddress() should be used for parameters declared to take a reference, in which case the method will simply put the pointer on the script stack.
Would it be possible for you to send me your "test rig" so that I can test it? Or if it contains too much 'secret' stuff, perhaps you could send me a stripped down version?
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
No secrets really, but all the AS calls are wrapped in my own API so it would take me a while to get a standalone working version. I will try to explain how everything is set up however, including the calls to the AS API:
The following is the Point class in C++:
class Point
{
public:
int iRefCount;
int x;
int y;
Point(void) : iRefCount(1), x(0), y(0) {};
~Point(void) {};
inline void Add(Point p) { this->x += p.x; this->y += p.y; };
inline Point &operator=(const Point &pointOther)
{
// leave ref count untouched
this->x = pointOther.x;
this->y = pointOther.y;
return *this;
}
inline void AddRef(void) { ++iRefCount; } ;
inline void Release(void)
{
if( --iRefCount == 0 )
delete this;
};
};
Due to the way I coded my API, I need to use wrapper functions with the OBJFIRST convention for all the class methods, constructors, destructors and operators:
void Point_Construct(Point *pPoint)
{
new(pPoint)Point();
}
void Point_Destruct(Point &point)
{
point.~Point();
}
Point &Point_Assign(Point &point, Point &pointOther)
{
point = pointOther;
return point;
}
void Point_Add(Point &point, Point p)
{
point.Add(p);
}
int *Point_Index(Point &point, int iIndex)
{
if ((iIndex < 0) || (iIndex > 1))
GetNGEngine()->GetScriptManager()->RaiseScriptException("Point index out of range.");
if (iIndex == 0)
return &point.x
else
return &point.y
}
void Point_AddRef(Point &point)
{
point.AddRef();
}
void Point_Release(Point &point)
{
point.Release();
}
And here are the registration calls for Point as they appear in my API:
ScriptManager *pScriptManager = pNGEngine->GetScriptManager();
pScriptManager->RegisterHostType("Point", sizeof(Point));
pScriptManager->RegisterHostTypeProperty("Point", "int x", offsetof(Point, x));
pScriptManager->RegisterHostTypeProperty("Point", "int y", offsetof(Point, y));
pScriptManager->RegisterHostTypeMethod("Point", "void Add(Point)", Point_Add);
pScriptManager->RegisterHostTypeBehaviour("Point", STB_Construct, "void f()", Point_Construct);
pScriptManager->RegisterHostTypeBehaviour("Point", STB_Destruct, "void f()", Point_Destruct);
pScriptManager->RegisterHostTypeBehaviour("Point", STB_Assign, "Point &f(Point ∈)", Point_Assign);
pScriptManager->RegisterHostTypeBehaviour("Point", STB_Index, "int &f(int)", Point_Index);
pScriptManager->RegisterHostTypeBehaviour("Point", STB_AddRef, "void f()", Point_AddRef);
pScriptManager->RegisterHostTypeBehaviour("Point", STB_Release, "void f()", Point_Release);
Finally here is the implementation of my own API functions:
void ScriptManagerImpl::RegisterHostVariable(string strSignature, void* pVariable)
{
if (m_pasIScriptEngine->RegisterGlobalProperty(
strSignature.c_str(), pVariable) < 0)
throw Exception(
"Failed registering host function '" + strSignature + "'.");
}
void ScriptManagerImpl::RegisterHostFunction(string strSignature, void* pFunction)
{
if (m_pasIScriptEngine->RegisterGlobalFunction(
strSignature.c_str(), asFUNCTION(pFunction), asCALL_CDECL) < 0)
throw Exception(
"Failed registering host function '" + strSignature + "'.");
}
void ScriptManagerImpl::RegisterHostOperator(
ScriptOperator scriptOperator,
string strBehaviourSignature, void *pBehaviourFunction)
{
asDWORD dwBehaviour = this->ConvertToASBehaviour(scriptOperator);
if (m_pasIScriptEngine->RegisterGlobalBehaviour(
dwBehaviour, strBehaviourSignature.c_str(),
asFUNCTION(pBehaviourFunction), asCALL_CDECL) < 0)
throw Exception(
"Failed registering host operator behaviour '"
+ strBehaviourSignature + "'.", __FILE__, __LINE__);
}
void ScriptManagerImpl::RegisterHostType(string strType, int iTypeSize)
{
if (m_pasIScriptEngine->RegisterObjectType(
strType.c_str(), iTypeSize, asOBJ_CLASS_CDA) < 0)
throw Exception("Failed registering host type '" + strType + "'.",
__FILE__, __LINE__);
}
void ScriptManagerImpl::RegisterHostTypeProperty(
string strType, string strPropertySignature, size_t iPropertyOffset)
{
if (m_pasIScriptEngine->RegisterObjectProperty(
strType.c_str(), strPropertySignature.c_str(), (int) iPropertyOffset) < 0)
throw Exception(
"Failed registering host type property '" + strPropertySignature
+ "' for host type '" + strType + "'.",
__FILE__, __LINE__);
}
void ScriptManagerImpl::RegisterHostTypeMethod(
string strType, string strMethodSignature, void *pMethodFunction)
{
// for debug watch purposes
const char *pStr = strMethodSignature.c_str();
if (m_pasIScriptEngine->RegisterObjectMethod(
strType.c_str(), strMethodSignature.c_str(),
asFUNCTION(pMethodFunction), asCALL_CDECL_OBJFIRST) < 0)
throw Exception(
"Failed registering host type method '" + strMethodSignature
+ "' for host type '" + strType + "'.",
__FILE__, __LINE__);
}
void ScriptManagerImpl::RegisterHostTypeBehaviour(
string strType, ScriptTypeBehaviour scriptTypeBehaviour,
string strBehaviourSignature, void *pBehaviourFunction)
{
asDWORD dwBehaviour = this->ConvertToASBehaviour(scriptTypeBehaviour);
if (m_pasIScriptEngine->RegisterObjectBehaviour(
strType.c_str(), dwBehaviour, strBehaviourSignature.c_str(),
asFUNCTION(pBehaviourFunction), asCALL_CDECL_OBJFIRST) < 0)
throw Exception(
"Failed registering host type behaviour '"
+ strBehaviourSignature + "' for type '" + strType + "'.",
__FILE__, __LINE__);
}
I checked the AS behaviour code conversion in case I was doing something stupid.. but it seems ok. Anyway.. here it is:
asDWORD ScriptManagerImpl::ConvertToASBehaviour(
ScriptTypeBehaviour scriptTypeBehaviour)
{
switch(scriptTypeBehaviour)
{
case STB_Construct: return asBEHAVE_CONSTRUCT;
case STB_Destruct: return asBEHAVE_DESTRUCT;
case STB_Assign: return asBEHAVE_ASSIGNMENT;
case STB_AddAssign: return asBEHAVE_ADD_ASSIGN;
case STB_SubAssign: return asBEHAVE_SUB_ASSIGN;
case STB_MulAssign: return asBEHAVE_MUL_ASSIGN;
case STB_DivAssign: return asBEHAVE_DIV_ASSIGN;
case STB_ModAssign: return asBEHAVE_MOD_ASSIGN;
case STB_OrAssign: return asBEHAVE_OR_ASSIGN;
case STB_AndAssign: return asBEHAVE_AND_ASSIGN;
case STB_XorAssign: return asBEHAVE_XOR_ASSIGN;
case STB_SllAssign: return asBEHAVE_SLL_ASSIGN;
case STB_SrlAssign: return asBEHAVE_SRL_ASSIGN;
case STB_SraAssign: return asBEHAVE_SRA_ASSIGN;
case STB_Index: return asBEHAVE_INDEX;
case STB_Negate: return asBEHAVE_NEGATE;
case STB_AddRef: return asBEHAVE_ADDREF;
case STB_Release: return asBEHAVE_RELEASE;
}
throw Exception("Unsupported type behaviour.", __FILE__, __LINE__);
}
[Edited by - SharkBait on April 17, 2006 12:32:22 PM]
Sorry for bumping again... but since the thread has gone quiet...
I was wondering if you had a chance to look into the problem above. So far it appears to be a problem in version 2.6.0 of AS as everything has worked fine for several versions until now. Perhaps it is due to the introduction of SetArgAddress? Anyway, I am still using SetArgObject and that appears to be the correct method to use according to my script function definition.
Thanks,
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
#include "utils.h"namespace TestShark{#define TESTNAME "TestShark"class Point{public: int iRefCount; int x; int y; Point(void) : iRefCount(1), x(0), y(0) {}; ~Point(void) {}; inline void Add(Point p) { this->x += p.x; this->y += p.y; }; inline Point &operator=(const Point &pointOther) { // leave ref count untouched this->x = pointOther.x; this->y = pointOther.y; return *this; } inline void AddRef(void) { ++iRefCount; } ; inline void Release(void) { if( --iRefCount == 0 ) delete this; };};void Point_Construct(Point *pPoint){ new(pPoint)Point();}void Point_Destruct(Point &point){ point.~Point();}Point &Point_Assign(Point &point, Point &pointOther){ point = pointOther; return point;}void Point_Add(Point &point, Point p){ point.Add(p);}int *Point_Index(Point &point, int iIndex){ if ((iIndex < 0) || (iIndex > 1)) asGetActiveContext()->SetException("Point index out of range."); if (iIndex == 0) return &point.x; else return &point.y;}void Point_AddRef(Point &point){ point.AddRef();}void Point_Release(Point &point){ point.Release();}static char *script ="Point AddPoints(Point p1, Point p2) \n""{ \n""Point p3 = p1; \n""p3.x += p2.x; \n""p3.y += p2.y; \n""return p3; \n""} \n";bool Test(){ bool fail = false; int r; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); r = engine->RegisterObjectType("Point", sizeof(Point), asOBJ_CLASS_CDA); assert( r >= 0 ); r = engine->RegisterObjectProperty("Point", "int x", offsetof(Point, x)); assert( r >= 0 ); r = engine->RegisterObjectProperty("Point", "int y", offsetof(Point, y)); assert( r >= 0 ); r = engine->RegisterObjectMethod("Point", "void Add(Point)", asFUNCTION(Point_Add), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("Point", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Point_Construct), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("Point", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Point_Destruct), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("Point", asBEHAVE_ASSIGNMENT, "Point &f(Point &in)", asFUNCTION(Point_Assign), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("Point", asBEHAVE_INDEX, "int &f(int)", asFUNCTION(Point_Index), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("Point", asBEHAVE_ADDREF, "void f()", asFUNCTION(Point_AddRef), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("Point", asBEHAVE_RELEASE, "void f()", asFUNCTION(Point_Release), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); COutStream out; engine->AddScriptSection(0, TESTNAME, script, strlen(script)); engine->SetCommonMessageStream(&out); r = engine->Build(0); if( r < 0 ) { printf("%s: Failed to build\n", TESTNAME); fail = true; } else { // Internal return int funcId = engine->GetFunctionIDByName(0, "AddPoints"); asIScriptContext *ctx = engine->CreateContext(); ctx->Prepare(funcId); Point a, b, c; a.x = 1; a.y = 1; b.x = 2; b.y = 2; ctx->SetArgObject(0, &a); ctx->SetArgObject(1, &b); r = ctx->Execute(); if( r != asEXECUTION_FINISHED ) fail = true; Point *ret = (Point*)ctx->GetReturnObject(); c = *ret; ctx->Release(); } engine->Release(); return fail;}} // End namespace
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
The error apparently occurs when some intermediate variable of type Point runs out of references and gets deleted. The deletion triggers a breakpoint when the pointer fails to pass a heap integrity test (_CrtIsValidHeapPointer). Perhaps I'm doing something wrong with references? Should the assignment operator, for example, do anything to the references?
Thinking of what else could be different from your test rig.. I'm compiling on VS 2005, which, altough it has worked with AS 2.5.0, could be the root of the problem, at least as far as AS 2.6.0 is concerned.
I am also beginning to wonder whether the 'inline' specifiers of some of Point's methods could have an effect.. but I suppose they simply act as a hints that replace hard-coded calls with the functions' content. In the case of AS, function calls are dynamic through the use of registered function pointer so that should not be an issue.. or could it? Just thinking out loud...
From your Point class you seem to be doing everything correctly in regards to the reference counting.
Are you using AngelScript from a dll? If you do then you probably need to register the memory behaviours as well, otherwise AngelScript will allocate memory on the dll heap and the application will free it from the application heap.
Another possibility for the problem may be the optimization mode you're compiling with. I've found that when compiling AngelScript with maximum optimizations turned on, some of the tests fails. Though not the way your test fails, so this is probably not the cause.
Perhaps you could add a LineCallback to AngelScript and log every line visited when executing the script function? You can look at the test_debug.cpp test for an idea on how that can be done. This may shed some light on the problem, especially if you also log each of the calls to the AddRef and Release calls so that you can see how they are manipulated.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game