1
0
Fork 0

On-demand loading of Nasal modules.

Load a module whenever its /nasal/foo/enabled flag is set,
even if it was disabled at start-up. Also expose a property if it was
successfully loaded.
This commit is contained in:
ThorstenB 2011-04-03 15:30:25 +02:00
parent 731e803223
commit 93180eb004
2 changed files with 99 additions and 53 deletions

View file

@ -38,6 +38,31 @@
static FGNasalSys* nasalSys = 0;
// Listener class for loading Nasal modules on demand
class FGNasalModuleListener : public SGPropertyChangeListener
{
public:
FGNasalModuleListener(SGPropertyNode* node);
virtual void valueChanged(SGPropertyNode* node);
private:
SGPropertyNode_ptr _node;
};
FGNasalModuleListener::FGNasalModuleListener(SGPropertyNode* node) : _node(node)
{
}
void FGNasalModuleListener::valueChanged(SGPropertyNode*)
{
if (_node->getBoolValue("enabled",false)&&
!_node->getBoolValue("loaded",true))
{
nasalSys->loadPropertyScripts(_node);
}
}
// Read and return file contents in a single buffer. Note use of
// stat() to get the file size. This is a win32 function, believe it
@ -817,56 +842,73 @@ void FGNasalSys::loadPropertyScripts()
SGPropertyNode* nasal = globals->get_props()->getNode("nasal");
if(!nasal) return;
for(int i=0; i<nasal->nChildren(); i++) {
for(int i=0; i<nasal->nChildren(); i++)
{
SGPropertyNode* n = nasal->getChild(i);
bool is_loaded = false;
const char* module = n->getName();
if(n->hasChild("module"))
module = n->getStringValue("module");
if (n->getBoolValue("enabled",true))
{
// allow multiple files to be specified within a single
// Nasal module tag
int j = 0;
SGPropertyNode *fn;
bool file_specified = false;
while((fn = n->getChild("file", j)) != NULL) {
file_specified = true;
const char* file = fn->getStringValue();
SGPath p(file);
if (!p.isAbsolute() || !p.exists())
{
p = globals->resolve_maybe_aircraft_path(file);
}
loadModule(p, module);
j++;
}
const char* src = n->getStringValue("script");
if(!n->hasChild("script")) src = 0; // Hrm...
if(src)
createModule(module, n->getPath().c_str(), src, strlen(src));
if(!file_specified && !src)
{
// module no longer exists - clear the archived "enable" flag
n->setAttribute(SGPropertyNode::USERARCHIVE,false);
SGPropertyNode* node = n->getChild("enabled",0,false);
if (node)
node->setAttribute(SGPropertyNode::USERARCHIVE,false);
SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
"no <file> or <script> defined in " <<
"/nasal/" << module);
}
else
is_loaded = true;
}
n->setBoolValue("loaded",is_loaded);
loadPropertyScripts(n);
}
}
// Loads the scripts found under /nasal in the global tree
void FGNasalSys::loadPropertyScripts(SGPropertyNode* n)
{
bool is_loaded = false;
const char* module = n->getName();
if(n->hasChild("module"))
module = n->getStringValue("module");
if (n->getBoolValue("enabled",true))
{
// allow multiple files to be specified within a single
// Nasal module tag
int j = 0;
SGPropertyNode *fn;
bool file_specified = false;
bool ok=true;
while((fn = n->getChild("file", j)) != NULL) {
file_specified = true;
const char* file = fn->getStringValue();
SGPath p(file);
if (!p.isAbsolute() || !p.exists())
{
p = globals->resolve_maybe_aircraft_path(file);
}
ok &= loadModule(p, module);
j++;
}
const char* src = n->getStringValue("script");
if(!n->hasChild("script")) src = 0; // Hrm...
if(src)
createModule(module, n->getPath().c_str(), src, strlen(src));
if(!file_specified && !src)
{
// module no longer exists - clear the archived "enable" flag
n->setAttribute(SGPropertyNode::USERARCHIVE,false);
SGPropertyNode* node = n->getChild("enabled",0,false);
if (node)
node->setAttribute(SGPropertyNode::USERARCHIVE,false);
SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
"no <file> or <script> defined in " <<
"/nasal/" << module);
}
else
is_loaded = ok;
}
else
{
SGPropertyNode* enable = n->getChild("enabled");
if (enable)
{
FGNasalModuleListener* listener = new FGNasalModuleListener(n);
enable->addChangeListener(listener, false);
}
}
n->setBoolValue("loaded",is_loaded);
}
// Logs a runtime error, with stack trace, to the FlightGear log stream
void FGNasalSys::logError(naContext context)
{
@ -884,7 +926,7 @@ void FGNasalSys::logError(naContext context)
// Reads a script file, executes it, and places the resulting
// namespace into the global namespace under the specified module
// name.
void FGNasalSys::loadModule(SGPath file, const char* module)
bool FGNasalSys::loadModule(SGPath file, const char* module)
{
int len = 0;
char* buf = readfile(file.c_str(), &len);
@ -892,25 +934,26 @@ void FGNasalSys::loadModule(SGPath file, const char* module)
SG_LOG(SG_NASAL, SG_ALERT,
"Nasal error: could not read script file " << file.c_str()
<< " into module " << module);
return;
return false;
}
createModule(module, file.c_str(), buf, len);
bool ok = createModule(module, file.c_str(), buf, len);
delete[] buf;
return ok;
}
// Parse and run. Save the local variables namespace, as it will
// become a sub-object of globals. The optional "arg" argument can be
// used to pass an associated property node to the module, which can then
// be accessed via cmdarg(). (This is, for example, used by XML dialogs.)
void FGNasalSys::createModule(const char* moduleName, const char* fileName,
bool FGNasalSys::createModule(const char* moduleName, const char* fileName,
const char* src, int len,
const SGPropertyNode* cmdarg,
int argc, naRef* args)
{
naRef code = parse(fileName, src, len);
if(naIsNil(code))
return;
return false;
// See if we already have a module hash to use. This allows the
// user to, for example, add functions to the built-in math
@ -925,6 +968,7 @@ void FGNasalSys::createModule(const char* moduleName, const char* fileName,
call(code, argc, args, locals);
hashset(_globals, moduleName, locals);
return true;
}
void FGNasalSys::deleteModule(const char* moduleName)

View file

@ -25,7 +25,7 @@ public:
// Loads a nasal script from an external file and inserts it as a
// global module of the specified name.
void loadModule(SGPath file, const char* moduleName);
bool loadModule(SGPath file, const char* moduleName);
// Simple hook to run arbitrary source code. Returns a bool to
// indicate successful execution. Does *not* return any Nasal
@ -54,7 +54,7 @@ public:
// Callbacks for command and timer bindings
virtual bool handleCommand(const SGPropertyNode* arg);
void createModule(const char* moduleName, const char* fileName,
bool createModule(const char* moduleName, const char* fileName,
const char* src, int len, const SGPropertyNode* cmdarg=0,
int argc=0, naRef*args=0);
@ -66,6 +66,7 @@ public:
private:
friend class FGNasalScript;
friend class FGNasalListener;
friend class FGNasalModuleListener;
//
// FGTimer subclass for handling Nasal timer callbacks.
@ -86,6 +87,7 @@ private:
static int _listenerId;
void loadPropertyScripts();
void loadPropertyScripts(SGPropertyNode* n);
void loadScriptDirectory(simgear::Dir nasalDir);
void addModule(string moduleName, simgear::PathList scripts);
void hashset(naRef hash, const char* key, naRef val);