Application function returning a funcdef handle crashes when called in AS

Started by
16 comments, last by Violet CLM 10 years, 9 months ago
Using the latest AS release. Relevant application code:
typedef void (*AiProcPtr) (int);

//...

AiProcPtr getBehaviorPointer(Omonster* obj) {
	return AiProcPtr((*(unsigned int*)obj) & 0x7FFFFFFF); //the property is at offset zero within the Omonster class
}
void setBehaviorPointerToASFunc(unsigned int func, Omonster* obj) {
	obj->procPtr = (AiProcPtr)(func | 0x80000000); //the top bit is used to identify the pointer as pointing to an AngelScript function, not a native application function. it's irrelevant here.
}

//...

ASengine->RegisterObjectType("jjOBJ", sizeof(Omonster), asOBJ_REF | asOBJ_NOCOUNT  );
ASengine->RegisterFuncdef("void jjBEHAVIOR(jjOBJ@)");
ASengine->RegisterObjectMethod("jjOBJ", "jjBEHAVIOR@ getBehavior()", asFUNCTION(getBehaviorPointer), asCALL_CDECL_OBJLAST);
ASengine->RegisterObjectMethod("jjOBJ", "void setBehavior(jjBEHAVIOR@)", asFUNCTIONPR(setBehaviorPointerToASFunc, (unsigned int, Omonster*), void), asCALL_CDECL_OBJLAST);

Relevant AngelScript code:
void TriggerableBlock(jjOBJ@ obj) {
}

//...

void checkBehavior() {
	jjOBJ@ foo = jjObjects[1]; //indexed property accessor that returns a jjOBJ/Omonster
	if (foo.getBehavior() is null) foo.setBehavior(TriggerableBlock);
}
The seventh time or so that checkBehavior() is called from within the application, the application totally crashes, having tried to read memory from an address either much too small or much too large. Depending on the context getBehavior() is called in -- a global asIScriptContext*, one created just for calling that one AngelScript function that one time, whatever -- sometimes it'll only take four calls to getBehavior() or even just one, but it's seven or eight in this particular context. The crashing does not occur if getBehavior() returns 0, but it does for all other numbers. I get similar results with a simple application function to return a constant number, e.g. 7, treated by AngelScript as returning a funcdef. getBehavior() does work, though, prior to the crash... after foo.setBehavior(TriggerableBlock); is used, (foo.getBehavior() is TriggerableBlock) evaluates to true.

So what am I doing wrong? How do I get a function to return a funcdef, without everything crashing? As far as I can tell from debug information, the crash sometimes takes place somewhere in asCContext::Prepare(asIScriptFunction *func), somewhere around m_initialFunction->AddRef();. Other times it seems to happen while calling getBehavior(). I really don't know what's going on.



Sidenote: If I name the function set_behavior instead of setBehavior, I can have overloaded set_behavior functions written as functions, but AngelScript refuses to let me use them as property accessors? foo.set_behavior(TriggerableBlock); is fine but foo.behavior = TriggerableBlock; gives a compilation error. Accessors can't be overloaded? sad.png
Advertisement

The returned funcdef handle must be an asIScriptFunction pointer. AngelScript will call methods on the asIScriptFunction (or really the underlying asCScriptFunction object), so if you return a pointer anything else you will have unexpected behaviours and even application crashes.

On the side note: Correct, accessors cannot be overloaded. Only one set and one get accessor is currently accepted for each virtual property.

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

It is a asIScriptFunction*, though, as I said... (foo.getBehavior() is TriggerableBlock) evaluates to true.

I think then that the problem is that you're not updating the refCounter of the asIScriptFunction object properly.

setBehaviorPointerToASFunc() needs to release the handle of the old object stored in obj->procPtr since it is being overwritten.

getBehaviorPointer() needs to increase the refCounter of the handle that is returned, since AngelScript will release it later.

Also, your setBehaviourPointerToASFunc stores the pointer in a member, but your getBehaviourPointer doesn't use that member. This looks strange, but if you garantee that the procPtr is the first member of the Omonster class, and the Omonster class doesn't have any virtual functions then it should be ok.

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

Hmm... I guess that makes sense. Functions having reference counters seems strange, but when funcdefs get involved, okay. I'll give it a try.

Is it possible to write casts involving funcdefs? I haven't been able to get any to work.

It may not make sense for C++ functions that don't change, but the script functions are actually objects that are created and destroyed, so here it is necessary to keep track of the reference count so that the objects aren't destroyed prematurely.

Casts involving funcdefs work just like other casts. What exactly is it that you've tried but didn't work.

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

Casts involving funcdefs work just like other casts. What exactly is it that you've tried but didn't work.

C++:
	ASengine->RegisterFuncdef("void jjBEHAVIOR(jjOBJ@)");

	ASengine->RegisterFuncdef("void DifferentFunctionPointer()");
	ASengine->RegisterObjectBehaviour("jjBEHAVIOR", asBEHAVE_IMPLICIT_REF_CAST, "DifferentFunctionPointer@ a()", asFUNCTION(someConversionFunction), asCALL_CDECL_OBJLAST);
	ASengine->RegisterGlobalFunction("void someFunctionTakingAFunctionPointer(DifferentFunctionPointer@)", asFUNCTION(someFunction), asCALL_CDECL);

	ASengine->RegisterObjectType("MiscRefObject", 0, asOBJ_REF | asOBJ_NOCOUNT  );
	ASengine->RegisterObjectBehaviour("jjBEHAVIOR", asBEHAVE_IMPLICIT_REF_CAST, "MiscRefObject@ b()", asFUNCTION(someConversionFunction), asCALL_CDECL_OBJLAST);
	ASengine->RegisterGlobalFunction("void someFunctionTakingAnObject(MiscRefObject@)", asFUNCTION(someFunction), asCALL_CDECL);

	ASengine->RegisterObjectBehaviour("jjBEHAVIOR", asBEHAVE_IMPLICIT_VALUE_CAST, "uint d()", asFUNCTION(someConversionFunction), asCALL_CDECL_OBJLAST);
	ASengine->RegisterGlobalFunction("void someFunctionTakingAUint(uint)", asFUNCTION(someFunction), asCALL_CDECL);
AngelScript:
void TriggerableBlock(jjOBJ@ obj) {
	//do stuff
}

//...

	someFunctionTakingAFunctionPointer(TriggerableBlock);
	someFunctionTakingAnObject(TriggerableBlock);
	someFunctionTakingAUint(TriggerableBlock);
Each gives an error akin to No matching signatures to 'someFunctionTakingAFunctionPointer(::TriggerableBlock)'. Typing "@TriggerableBlock" instead of "TriggerableBlock" gives the same results.

If I don't use _IMPLICIT_ casts:
	someFunctionTakingAFunctionPointer(cast<DifferentFunctionPointer>(TriggerableBlock));
	someFunctionTakingAnObject(cast<MiscRefObject>(TriggerableBlock));
	someFunctionTakingAUint(cast<uint>(TriggerableBlock));
The first two give No matching signatures to 'someFunctionTakingAFunctionPointer(<unrecognized token>)' and No matching signatures to 'someFunctionTakingAnObject(<unrecognized token>)'. The last gives Illegal target type for reference cast.

Also,
ASengine->RegisterGlobalProperty("DifferentFunctionPointer f", 0);
just gives me a registration error.

It's illegal to register additional behaviours or methods on funcdefs. The funcdefs are built-in types and cannot be modified. It would appear that I need to add verifications for this so that AngelScript doesn't return success in this is attempted.

I need to look into the reason behind the error 'No matching signatures to 'someFunctionTakingAFunctionPointer(<unrecognized token>)'. It might be a bug in the code.

The error message Illegal target type for the uint is because a primitives and other value types are not reference types, thus are not acceptable for reference casts.

The RegisterGlobalProeprty() fails because you're not informing the address of the property that you're registering. If you're just registering a dummy, then at least give a false address different from 0, e.g.:

ASengine->RegisterGlobalProperty("DifferentFunctionPointer f", (void*)1);

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

It's illegal to register additional behaviours or methods on funcdefs. The funcdefs are built-in types and cannot be modified. It would appear that I need to add verifications for this so that AngelScript doesn't return success in this is attempted.

But isn't RegisterObjectBehavior the function used to allow casting of a reference type, as shown here? How do I cast a funcdef, if I can't use asBEHAVE_IMPLICIT_REF_CAST, if I can't register a behavior?

The RegisterGlobalProeprty() fails because you're not informing the address of the property that you're registering. If you're just registering a dummy, then at least give a false address different from 0

Nope,
	ASengine->RegisterFuncdef("void DifferentFunctionPointer()");
    ASengine->RegisterGlobalProperty("DifferentFunctionPointer f", (void*)1);
still gives a registration error. EDIT: Oh, wait, I needed to add an @. Let's see if I can make this work... EDIT2: Nope, no help. I can register arbitrary numbers for funcdefs to point to, but if I try to pass one of those arbitrary fundefs as an argument to a function, the program crashes. Apparently passing a pointer in AngelScript involves trying to read the data being pointed to.

Yes, RegisterObjectBehaviour() is used for that. But only for application types. Funcdefs are not application types, they are built-in types and cannot be modified by the application.

I'll look into the reason behind RegisterGlobalProperty failing when attempting to register a funcdef property.

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

This topic is closed to new replies.

Advertisement