2.24.0 GetFunctionByDecl returns NULL for functions inside a namespace

Started by
7 comments, last by WitchLord 11 years, 9 months ago
Greetings. I'm doing lots of mad scientist experiments here ph34r.png  and I think I found 2 bugs for the price of one.

I'm trying to allow scripts to get function pointers by passing a string with the CDECL, and found out that, when we use GetFunctionByDecl to try to get the pointer of a function inside a namespace, if that function receives parameters, GetFunctionByDecl returns NULL.

Lemme explain with code:
So, here is the code to register "Get function pointer":

r = engine->RegisterGlobalFunction ("void getFuncPtr (const string &in)", asFUNCTION (getFuncPtr), asCALL_CDECL); assert (r >= 0);


Please notice that currently, the getFuncPtr function is not actually returning anything (as it's return type is void); I'll get into that in the second issue, but for now, let me first explain the problem with namespaces.

the code for getFuncPtr goes like this:


void getFuncPtr (const string &Name)
{
   asIScriptFunction *Func = engine->GetModule(0)->GetFunctionByDecl (Name.c_str ());
   cout << Name << endl;
   cout << Func << endl;
   cout << Func->GetObjectType () << endl;
}



And, in an angelscript we have the following code:


void DoNothing (int NotUsed)
{
}

namespace Freedom
{
   void DoNothing ()
   {
   }
   void DoNothingElse (int NotUsed)
   {
   }
}

void main ()
{
   getFuncPtr ("void DoNothing (int)");
   getFuncPtr ("void Freedom::DoNothing ()");
   getFuncPtr ("void Freedom::DoNothingElse (int)");
}




and that code prints the following:


main.fss (0, 0) : INFO : Script successfully built
void DoNothing (int)
005BA9D0
00000000
void Freedom::DoNothing ()
005BAB28
00000000
void Freedom::DoNothingElse (int)
00000000


Notice that, for the first 2 calls to getFuncPtr, we get a pointer, but for the third, the one that tries to get the address of the function that receives parameters and is inside a namespace, we get NULL. Of course, the application crashes in the third call to getFuncPointer, since we got a NULL pointer.

Another thing that worries me is that "Func->GetObjectType ()" is always returning NULL.

So, that's for the first issue: getting functions with parameters inside namespaces.



======================================================


Of course, the main idea behind all of this weirdness is to allow script objects to get pointers to functions (I know, it's weird...), so, after lots of not-less-weird attempts, I tried with the ref object:


//Register the function to return a ref:
r = engine->RegisterGlobalFunction ("ref @getFuncPtr (const string &in)", asFUNCTION (getFuncPtr), asCALL_CDECL); assert (r >= 0);

//some happy lines of code later...
CScriptHandle *getFuncPtr (const string &Name)
{
   CScriptHandle *Handle = new CScriptHandle;
   asIScriptFunction *Func = engine->GetModule(0)->GetFunctionByDecl (Name.c_str ());
   cout << Name << endl;
   cout << Func << endl;
   cout << Func->GetObjectType () << endl;
   Handle->Set (Func, Func->GetObjectType ());

   return Handle;
}


and the script is pretty the same code as the previous. If I'm lucky, the output goes something like this:

main.fss (0, 0) : INFO : Script successfully built
System function (1, 1) : ERR  : Expected data type

00000000


notice that for "Name" is not printing anything, so, the function is not actually getting the correct reference. Of course, the application crashes happy.png
(On an unlucky run, I would get a matrix screen saver in my console output)

I'ts most likely just me doing something stupid; I gotta admit I don't understand this "reference object" very much biggrin.png .
format c: /q
Advertisement
I'll have to investigate this. It definitely looks like you've detected a bug here.

I hadn't thought about how to set ScriptHandle with a function pointer from the C++ side. Right now there is no easy way of doing that, as the typeId that you'll need is not directly accessible. The best that can be done for now is to use module->GetTypeIdByDecl(), however you need to pass the name of the matching funcdef, not the function declaration.

asIScriptFunction::GetObjectType() will only return something if the function is a class method though, in which case it will return the type of the class.


The CScriptHandle object is to be returned by value even though it is registered as ref@. This is because the CScriptHandle is representing the handle itself.

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

EDIT: Fixed a stupid mistake of mine in the function implementation, now it almost works perfectly!! (look by the end)

Hello. If it helps... as in... at all... I've done the following changes:

Function registering (now it returns value instead of reference):
r = engine->RegisterGlobalFunction ("ref getFuncPtr (const string &in)", asFUNCTION (getFuncPtr), asCALL_CDECL); assert (r >= 0);

Function implementation (ID is now get with module->GetTypeIdByDecl to a fixed funcdef):



CScriptHandle getFuncPtr (const string &Name)
{
CScriptHandle Handle;
asIScriptFunction *Func = engine->GetModule(0)->GetFunctionByDecl (Name.c_str ());
cout << Name << endl;
cout << Func << endl;
cout << engine->GetObjectTypeById (engine->GetModule(0)->GetTypeIdByDecl ("FSMFunc")) << endl;
Handle.Set (Func, engine->GetObjectTypeById (engine->GetModule(0)->GetTypeIdByDecl ("FSMFunc")));

return Handle;
}


and the script code goes like this (Notice that the function is called by the end):


funcdef void FSMFunc(TestClass @);

class TestClass
{
string getMessage ()
{
return "this is a message";
}
}

void PrintMessage (TestClass @Class)
{
print ("We got a message: " + Class.getMessage ());
}


void main ()
{
TestClass Test;
FSMFunc @Func = cast <FSMFunc @> (getFuncPtr ("void PrintMessage (TestClass @)"));

Func (Test);
}



Aaaand, the grand result iiiis~


void PrintMessage (TestClass @)
0056BAA0
0054AB34
We got a message: this is a messageCall



So, that part is now working perfectly.
BUT!
If in the script we try to get a pointer to a function inside a namespace, the result is a null pointer.
Example code:

funcdef void FSMFunc(TestClass @);

class TestClass
{
string getMessage ()
{
return "this is a message";
}
}

namespace test
{
void PrintMessage (TestClass @Class)
{
::print ("We got a message: " + Class.getMessage ());
}
}

void main ()
{
TestClass Test;
FSMFunc @Func = cast <FSMFunc @> (getFuncPtr ("void test::PrintMessage (TestClass @)"));

Func (Test);
}


That script code gives us the following output:


void test::PrintMessage (TestClass @)
00000000
0016AB34


So, it seems like GetFunctionByDecl is not taking namespaces into account... maybe?
format c: /q
Hello again!

I've been checking the source code and I think I have a few hints on what's going on.

First of all: the problem only occurs when the function who's pointer we want to retrive is inside a namespace AND it receives parámeters. If the function has no parameters, it's pointer is retrieved correctly.

The reason for that is that in as_module.cpp, around line 640, we have the following code:

if( globalFunctions[n]->objectType == 0 &&
func.name == globalFunctions[n]->name &&
func.returnType == globalFunctions[n]->returnType &&
func.parameterTypes.GetLength() == globalFunctions[n]->parameterTypes.GetLength() &&
ns == globalFunctions[n]->nameSpace )
{
//this means that the function we are looking for has been found!!


Take a close look at this line:
func.parameterTypes.GetLength() == globalFunctions[n]->parameterTypes.GetLength() &&

Turns out, when we search for a function inside a namespace, func.parameterTypes.GetLength() always returns 0.
And that's because in as_builder.cpp, around line 956, we have this code:

n = node->firstChild->next->next->next->firstChild;
while( n )
{
//Do some parameter counting stuff here...


The problem is: when the function we are searching for is inside a namespace, "node->firstChild->next->next->next->firstChild;" returns null.
Why, you ask?
Damn I wish I knew... tongue.png I'd really love to know what that line of code actually does laugh.png

Could you help me out here, Andreas?
Why do you get the node's first child of the third sibling?

I really hope this is at least closing the distance to solve this weird issue
format c: /q
You're probably on the right track here. Unfortunately I haven't had the time to do my own investigation yet.

If I'm not remembering things completely wrong the parser will create the following nodes as part of the function declaration:


"function declaration"
"return type"
"type modifier"
"name"
"parameter list"
"parameter 1"
"parameter 2"
etc
"statement block"
"statement 1"
"statement 2"
etc



If this is correct, then the builder is obviously forgetting that there might be optional nodes to give the namespace before the function name.

The namespace feature is obviously not a highly used feature yet, otherwise a bug like this would have been detected sooner.

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

Great.

Thanks for the tip. I'll be sure to check it out later today
format c: /q
You had indeed found the cause.

To fix the problem I made a couple of changes in as_builder.cpp in the method ParseFunctionDeclaration().

on line 934:


// Count number of parameters
int paramCount = 0;
{-} n = n->next->firstChild;
{+} asCScriptNode *paramList = n->next;
{+} n = paramList->firstChild;
while( n )


and on line 952:


{-} n = node->firstChild->next->next->next->firstChild;
{+} n = paramList->firstChild;


I'll have this checked in tonight.

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

Yays!
Glad I was able to help a little this time, and not only point my finger to a problem happy.png
format c: /q
Fix checked in to revision 1361.

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

This topic is closed to new replies.

Advertisement