Advertisement

script object ctors

Started by September 03, 2010 01:21 AM
7 comments, last by arpeggiodragon 14 years, 2 months ago
Hello. I've stumbled across a slight dilemma which I don't know how to solve and was hoping someone could help me get this working properly.

The problem is simple: A script object calls create_object(pos,...); which pushes a new object cpp side which in turn calls the following in the constructor:

//context init stuff.cPtr->object = Engine->CreateScriptObject( type->GetTypeId() );asIScriptObject* _thisObj = (asIScriptObject*)cPtr->object;this->pos = (Vector2*)_thisObj->GetAddressOfProperty(0); //*this->pos = .. //*set to what the script wants it to be*;


However, in the script object constructor there is no way to get the correct pos value set by create_object();. ...How can I achieve this?

Thanks.
What you need is to use a non-default constructor where the position is passed as a parameter.

An object is created with a non-default constructor by calling the factory function, just like a global function.

int funcId = type->GetFactoryIdByDecl("MyObj @MyObj(const Vector2 ∈)");ctx->Prepare(funcId);Vector2 pos;*(Vector2**)ctx->GetAddressOfArg(1) = &posctx->Execute();asIScriptObject *obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue();


Instead of passing the position into the constructor, you might pass something else, perhaps a pointer to your cPtr.

Another option is to not rely on the constructor to do the initialization. Do the initialization in a script class method called by the application after the object has been created.

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

Advertisement
Hmm.. The problem is that the cpp-side script instances simply contain pointers to asIScriptObject members (likely a base class), so they are not valid until the asIScriptObject has been created, which they are then linked by this->ptrData = ()->GetAddressOfProperty(n); so that any changes in those values are reflected immediately no matter what changed them. Until this happens, if any script context were to call a function that accesses that class instance data then the program will die horribly.
What we are doing is letting users do pretty much anything they want in any scripts object constructor. I suppose handling the initialization in a separate script class method would work fine, but it would be sort of weird to tell people "Don't use the constructors!" ;)

I suppose the ideal solution would be something like:

object = (void*)engine->CreateScriptObjectShell(...); //create the object but doesn't call ctor//link class pointers to script data here, but we need the object handle first//...funcId = type->GetFactoryIdByDecl("Class @Class()");ctx->Prepare(funcId);ctx->Execute();


Any ideas?
What's stopping the constructor from calling a function that accesses the pointers of the C++ class from the application side?


I suppose you could do something like this:

int funcId = type->GetFactoryIdByDecl("MyObj @MyObj()");ctx->SetLineCallback(asFUNCTION(LinkObj), this, asCALL_CDECL);ctx->Prepare(funcId);ctx->Execute();asIScriptObject *obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue();...void LinkObj(asIScriptContext *ctx, void *obj){   asIScriptObject *scriptObj = ctx->GetThisPointer();   if( scriptObj )   {      MyObj *myObj = (MyObj*)obj;      myObj->pos = (Vector2*)ctx->GetAddressOfProperty(0);      ...      // Remove the callback since we've already linked the objects      ctx->ClearLineCallback();   }}


The line callback should be called by the engine right after the memory has been allocated and before the actual code of the constructor is executed.

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

Thanks again, and sorry for the late reply but I finally just got around to testing all the code. Everything seems to be working except for the method in your first post. The following causes a crash in line 1054 os as_call_funcs_x86 in 2.19.2:

_ctx->context->Prepare( funcId );*(Vector2**)_ctx->context->GetAddressOfArg(0) = &temp_ctx->context->Execute();_ctx->object = (asIScriptObject*)_ctx->context->GetAddressOfReturnValue();


Commenting out the second line above prevents this from occurring.
I wrote the following test to validate what I had said before:

		engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);				RegisterScriptMath3D(engine);		const char *script = 			"class Obj \n"			"{  \n"			"   Obj(const vector3 &in v) \n"			"   { \n"			"     pos = v; \n"			"   } \n"			"   vector3 pos; \n"			"} \n";		asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE);		mod->AddScriptSection("script", script);		r = mod->Build();		if( r < 0 )			fail = true;		int typeId = mod->GetTypeIdByDecl("Obj");		asIObjectType *type = engine->GetObjectTypeById(typeId);		int funcId = type->GetFactoryIdByDecl("Obj @Obj(const vector3 &in)");		if( funcId < 0 )			fail = true;		asIScriptContext *ctx = engine->CreateContext();		ctx->Prepare(funcId);		Vector3 pos(1,2,3);		*(Vector3**)ctx->GetAddressOfArg(0) = &pos;		r = ctx->Execute();		if( r != asEXECUTION_FINISHED )			fail = true;		asIScriptObject *obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue();		pos = *(Vector3*)obj->GetAddressOfProperty(0);		if( pos != Vector3(1,2,3) )			fail = true;		ctx->Release();		engine->Release();


The test above works perfectly.

The error I did before was passing the wrong index for the argument when calling GetAddressOfArg. But in your post you have the correct index, so I wonder if you don't have something else wrong instead.

Your typecast with the call to GetAddressOfReturnValue is wrong, but since you say it works if you remove the GetAddressOfArg I suspect that is just a typo.

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

Advertisement
Hmm... yes, I'm really stumped by this; your code looks good. I've got it to init and execute up to the main update method but still get a crash here:

Quote:
Engine.exe!asCScriptObject::`vcall'{0}'() + 0x2 bytes C++
Engine.exe!CallThisCallFunction(const void * obj=0x09593018, const unsigned long * args=0x095cfbe0, int paramSize=0, unsigned int func=5018058) Line 1054 C++
Engine.exe!CallSystemFunction(int id=18, asCContext * context=0x09592f00, void * objectPointer=0x00000000) Line 212 + 0x1b bytes C++
> Engine.exe!asCContext::ExecuteNext() Line 1960 + 0x12 bytes C++
Engine.exe!asCContext::Execute() Line 1004 + 0x8 bytes C++


Here is my AS object code:
		_ctx = contextPool->AquireContext();		if(!_ctx)			throw ("Some wacky scripting error occured...");		asIScriptEngine *Engine = contextPool->GetEngine();		int typeID			= Engine->GetModule(0)->GetTypeIdByDecl( class_decl );		if( typeID < 0 )		{			_ctx->destroyed = true;		}		asIObjectType* type = Engine->GetObjectTypeById( typeID );		_ctx->objectType	= type;		_ctx->function_id	= type->GetMethodIdByDecl( method_decl );		if( _ctx->function_id < 0 )		{			_ctx->script_status = asEXECUTION_SUSPENDED;			_ctx->suspend = INFINITE_SUSPEND;		}		_ctx->SetClassName( class_decl );//construct object		// create object at position		int funcId = _ctx->objectType->GetFactoryIdByDecl			( contextPool->ClassDeclToCtor( _ctx->GetClassName().c_str(), "(const Vector2 &in)" ) );		if( funcId < 0 )		{			// use default constructor			CreateObject();		}		else		{			contextPool->PushInstance( this );							_ctx->context->Prepare( funcId );				Vector2 test(222,333);				*(Vector2**)_ctx->context->GetAddressOfArg(0) = &test;				//int r = _ctx->context->SetArgObject(0, &test);				_ctx->context->Execute();				_ctx->object = _ctx->context->GetAddressOfReturnValue();				//_ctx->context->GetReturnObject();			contextPool->PopInstance();		}		return (asIScriptObject*)_ctx->object;//update object		_ctx->context->Prepare( _ctx->function_id );		contextPool->PushInstance( this );			_ctx->context->SetObject( _ctx->object );			_ctx->script_status = _ctx->context->Execute(); //<----- here		contextPool->PopInstance();		return _ctx->script_status;


Stepping through the code shows everything seems to work properly 'till the update method.. Also, forcing the default object ctor via CreateObject() ( which is just this: )

_ctx->object = contextPool->GetEngine()->CreateScriptObject( _ctx->objectType->GetTypeId() );
return (asIScriptObject*)_ctx->object;

Works fine. ?


Also, yes the above was a typo. ;) However Context::object is a void* and I still end up casting it the same way. Is this wrong? -Also just a note, "return (asIScriptObject*)_ctx->object;" is never actually used yet.

[Edited by - arpeggiodragon on September 19, 2010 10:54:21 PM]
This is your problem

_ctx->object = _ctx->context->GetAddressOfReturnValue();


it should be

_ctx->object = *(void**)_ctx->context->GetAddressOfReturnValue();


The factory function returns a handle to the object. GetAddressOfReturnValue returns the address to the location where this handle is stored, so you must dereference that to get the actual handle.

You must also increase the reference count of the newly created object. Otherwise it will be destroyed when the context is reused or freed.

if( _ctx->object )  ((asIScriptObject*)_ctx->object)->AddRef();



While you don't explicitly use the _ctx->object, AngelScript is still using it in the method call, as it tries to increase the reference to hold on to the object while the method is executing. Since the pointer was incorrect, this is why it crashes.

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

Quote: The factory function returns a handle to the object. GetAddressOfReturnValue returns the address to the location where this handle is stored, so you must dereference that to get the actual handle.


Yep, this was indeed the problem; coupled with a bug where contexts would get reused and still be in suspended state was causing some major mischief for me. Everything's finally working now. Thank you!

This topic is closed to new replies.

Advertisement