Null pointer access exception

Started by
3 comments, last by BrianEmpson 12 years, 6 months ago
Hello,

I had a script class, defined as such:


class Console {
Console()
{
//Initialization...
ID_GUI_CONSOLE_WINDOW = 100;
}
~Console()
{
}
void autocomplete()
{
}
int getWindowId()
{
return ID_GUI_CONSOLE_WINDOW;
}
private int ID_GUI_CONSOLE_WINDOW;
}


The issue is with "s32 getWindowId()", when attempting to call this function, I get:


Engine 0: Exception! Line: 0 Func: int Console::getWindowId()
String: Null pointer access.


When I change the return value to "void" so the function declaration reads "void getWindowId()" I can call the function fine, as well as all of the other functions in the class (all void).

Is there something I'm not doing right? I tried the property accessors, but I still get that same error message.

Thanks,
Brian
Advertisement
This is strange. Can you show a script with how you're actually using this Console class?

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

Alright, here is all of the code involved for the classes: (note that s32 in registered as a regular int)

AngelScript console class:


class Console {
//Other funcs snipped
s32 getWindowId()
{
s.print("Console::getWindowId()!\n");
//s.print(ID_GUI_CONSOLE_WINDOW);
return ID_GUI_CONSOLE_WINDOW;
}
//Properties
//------------------------------------
//Other properties snipped
private s32 ID_GUI_CONSOLE_WINDOW;
}


C++ console class (gutted with relevant functions only):


class Console {
void init(); //This is called once the graphics environment is finished setting up, it creates the script console class.
}

void Console::init() //Attach this class to the script console, once the graphics engine is initialized
{
scriptConsole = graphics->getEngine()->getScriptManager()->getObject("ui","Console","()"); //Create a Console :3
if (scriptConsole == 0)
graphics->stop();
else
scriptConsole->AddRef(); //Store the object
}

//Script Manager

asIScriptObject* ScriptManager::getObject(const std::string& moduleName,const std::string& objectName,const std::string& params)
{
// Get the object type
asIScriptModule *module = engine->GetModule(moduleName.c_str());
if (module == 0)
{
ss.str("");
ss.clear();
ss<<"Attempted to use non-existent module "<<moduleName.c_str()<<".\n";
log->log(ss.str());
return 0;
}
asIObjectType *type = engine->GetObjectTypeById(module->GetTypeIdByDecl(objectName.c_str()));

if (type == 0)
{
ss.str("");
ss.clear();
ss<<"Could not retrieve object type "<<objectName.c_str()<<".\n";
log->log(ss.str());
return 0;
}
// Get the factory function id from the object type
std::string decl = objectName + " @" + objectName + params;
int factoryId = type->GetFactoryIdByDecl(decl.c_str());

// Prepare the context to call the factory function
asIScriptContext* ctx = prepareContext(factoryId);
// Execute the call
ctx->Execute();

// Get the object that was created
//asIScriptObject* obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue();
asIScriptObject* obj = (asIScriptObject*)ctx->GetReturnAddress();
if (obj == 0)
{
ss.str("");
ss.clear();
ss<<"Could not instanciate object "<<objectName.c_str()<<" from script.\n";
log->log(ss.str());
return 0;
}
returnContext(ctx);
return obj; //Let the calling function determine if they want to save it or not :3
}

void* ScriptManager::exec(asIScriptObject* obj,const std::string& function,const char retType,const char* types, ... )
{
va_list vl;
int r;
va_start(vl,types);

asIObjectType* type = obj->GetObjectType();
// Obtain the id of the class method
irr::s32 funcId = type->GetMethodIdByDecl(function.c_str());

// Prepare the context for calling the method
asIScriptContext* ctx = prepareContext(funcId);

//Parse types and set arguments
//types are: c - 8bit,w - 16bit,i - 32bit,q - 64bit,f - float,d - double,v - arg by Address,V - obj by address
for(int i=0;types != '\0';i++)
{
switch(types)
{
case 'c': //8bit
ctx->SetArgByte(i,(char)va_arg(vl,int));
break;
case 'w': //16bit
ctx->SetArgWord(i,(short)va_arg(vl,int));
break;
case 'i': //32bit
ctx->SetArgDWord(i,va_arg(vl,int));
break;
case 'q': //64bit int
ctx->SetArgQWord(i,va_arg(vl,long long));
break;
case 'f': //float
ctx->SetArgFloat(i,(float)va_arg(vl,double));
break;
case 'd': //double
ctx->SetArgDouble(i,va_arg(vl,double));
break;
case 'v': //arg by addr
ctx->SetArgAddress(i,va_arg(vl,void*));
break;
case 'V': //obj by addr
ctx->SetArgObject(i,va_arg(vl,void*));
break;
default:
break;
}
}

va_end(vl);

r = ctx->Execute();
if( r != asEXECUTION_FINISHED)
{
// The execution didn't complete as expected. Determine what happened.
if( r == asEXECUTION_EXCEPTION)
{
// An exception occurred, let the script writer know what happened so it can be corrected.
ss.str("");
ss.clear();
ss<<"Exception! Line: " << ctx->GetExceptionLineNumber() << " Func: " << engine->GetFunctionDescriptorById(ctx->GetExceptionFunction())->GetDeclaration() << "\nString: " << ctx->GetExceptionString() << ".\n";
log->log(ss.str());
return 0;
}
}

//Check return type
void* ret = 0;
switch(retType)
{
case 'o':
{
ret = ctx->GetReturnAddress();
return ret;
break;
}
case 'O':
{
ret = ctx->GetReturnObject();
return ret;
}
case 'v':
{
ret = ctx->GetAddressOfReturnValue();
return ret;
}
}

// Clean up
returnContext(ctx);

return 0;
}



returnContext() and prepareContext are copies from your game example. It seems to work well. I haven't had time to rewrite the portions that use that code yet. Here is the actual call:

int Console::getWindowId()
{
int* windowId;
if(bBufferDumped == false) //This will be true when the console is ready.
{
return -1;
}
else
{
void* test = graphics->getEngine()->getScriptManager()->exec(scriptConsole,"int getWindowId()",'v',"");
(void)0;
int justADebuggingBreakpointPlaceholder = 4;
//windowId = (int*)
}
//return *windowId;
return -1;
}


Before the console gets created, the logger will still try to spit text out to it, which would cause a segfault or other nastyness, so the C++ class stores the text temporarily until the console class gets a signal to dump the text buffer to the script console class after it has been created, which it then does and sets the bBufferDumped flag to true. Then this getWindowId() gets executed whenever the GUI library senses a GUI event, and calls this function. Debugging reveals that my generic pointer, void* test is zero and an exception logged as above.

So, the exec function takes a stored asIScriptObject, grabs the type, and calls the method signature in the second parameter. The third parameter is the return type, which is "return by value address" in this case. The fourth parameter is a string of variable types that the rest of the parameters occupy.

Here is the call to getWindowId():


if (event.EventType == EET_GUI_EVENT)
{
s32 id = event.GUIEvent.Caller->getID();
irr::gui::IGUIEnvironment* env = graphics->device->getGUIEnvironment();
s32 consoleId = console->getWindowId(); //<-------------------------------------------
static std::stringstream ss;
ss.str("");
ss.clear();
ss << "GUI id: "<< id << " Console ID: " << consoleId << "\n";
log->log(ss.str());
if(id == consoleId)
{
console->updateElements();
}
//etc...
}

You're calling a script method, but you never inform the actual object that the method is called on in ScriptManager::exec. You need to call ctx->SetObject(obj); before executing the script function. Unless you left out this by mistake when posting here, this is the cause for null pointer exception.

Also, you may want to cache the method id returned by GetMethodIdByDecl(). This function is quite slow as it has to parse the function declaration, and then search for matching methods in order to find the correct one.

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

Yes, this worked wonderfully.

I will look into caching...sounds like a good idea.

This topic is closed to new replies.

Advertisement