Changed the way native objects' constructors are called.

Added proper cleanup in module.
Added script unload hook onUnload() that just needs be defined.


git-svn-id: http://yate.null.ro/svn/yate/trunk@5128 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2012-06-18 13:52:21 +00:00
parent f5e76ddb6b
commit c446eb8d2e
5 changed files with 185 additions and 35 deletions

View File

@ -140,6 +140,7 @@ public:
}
virtual bool initialize(ScriptContext* context) const;
virtual bool evaluate(ScriptRun& runner, ObjList& results) const;
virtual ScriptRun* createRunner(ScriptContext* context);
bool link();
JsObject* parseArray(const char*& expr, bool constOnly);
JsObject* parseObject(const char*& expr, bool constOnly);
@ -184,7 +185,8 @@ private:
bool callFunction(ObjList& stack, const ExpOperation& oper, GenObject* context,
JsFunction* func, bool constr) const;
bool callFunction(ObjList& stack, const ExpOperation& oper, GenObject* context,
long int retIndex, JsFunction* func, ObjList& args, ExpOperation* thisObj) const;
long int retIndex, JsFunction* func, ObjList& args,
ExpOperation* thisObj, ExpOperation* scopeObj) const;
inline JsFunction* getGlobalFunction(const String& name) const
{ return YOBJECT(JsFunction,m_globals[name]); }
long int m_label;
@ -228,7 +230,7 @@ public:
m_paused(false), m_opcode(0), m_index(0)
{ }
virtual Status reset();
virtual Status call(const String& name, ObjList& args, ExpOperation* thisObj = 0);
virtual Status call(const String& name, ObjList& args, ExpOperation* thisObj = 0, ExpOperation* scopeObj = 0);
virtual bool callable(const String& name);
protected:
virtual Status resume();
@ -2026,13 +2028,16 @@ bool JsCode::callFunction(ObjList& stack, const ExpOperation& oper, GenObject* c
ExpOperation* thisObj = constr ? popOne(stack) : 0;
ObjList args;
JsObject::extractArgs(func,stack,oper,context,args);
return callFunction(stack,oper,context,index,func,args,thisObj);
return callFunction(stack,oper,context,index,func,args,thisObj,0);
}
bool JsCode::callFunction(ObjList& stack, const ExpOperation& oper, GenObject* context,
long int retIndex, JsFunction* func, ObjList& args, ExpOperation* thisObj) const
long int retIndex, JsFunction* func, ObjList& args,
ExpOperation* thisObj, ExpOperation* scopeObj) const
{
pushOne(stack,new ExpOperation(OpcFunc,0,retIndex,true));
if (scopeObj)
pushOne(stack,new ExpWrapper(scopeObj,scopeObj->name()));
JsObject* ctxt = JsObject::buildCallContext(func->mutex(),thisObj);
for (unsigned int idx = 0; ; idx++) {
const String* name = func->formalName(idx);
@ -2049,6 +2054,14 @@ bool JsCode::callFunction(ObjList& stack, const ExpOperation& oper, GenObject* c
return jumpToLabel(func->label(),context);
}
ScriptRun* JsCode::createRunner(ScriptContext* context)
{
if (!context)
return 0;
return new JsRunner(this,context);
}
ScriptRun::Status JsRunner::reset()
{
@ -2073,27 +2086,31 @@ ScriptRun::Status JsRunner::resume()
return m_paused ? Incomplete : Succeeded;
}
ScriptRun::Status JsRunner::call(const String& name, ObjList& args, ExpOperation* thisObj)
ScriptRun::Status JsRunner::call(const String& name, ObjList& args,
ExpOperation* thisObj, ExpOperation* scopeObj)
{
Lock mylock(this);
if (Invalid == state()) {
TelEngine::destruct(thisObj);
TelEngine::destruct(scopeObj);
return Invalid;
}
const JsCode* c = static_cast<const JsCode*>(code());
if (!(c && context())) {
TelEngine::destruct(thisObj);
TelEngine::destruct(scopeObj);
return Invalid;
}
JsFunction* func = c->getGlobalFunction(name);
if (!func) {
TelEngine::destruct(thisObj);
TelEngine::destruct(scopeObj);
return Failed;
}
reset();
// prepare a function call stack
ExpOperation oper(ExpEvaluator::OpcFunc,name,args.count());
if (!c->callFunction(stack(),oper,this,-1,func,args,thisObj))
if (!c->callFunction(stack(),oper,this,-1,func,args,thisObj,scopeObj))
return Failed;
mylock.drop();
// continue normal execution like in run()
@ -2154,8 +2171,10 @@ bool JsFunction::runNative(ObjList& stack, const ExpOperation& oper, GenObject*
if (!oper.number())
return false;
}
else
return JsObject::runNative(stack,oper,context);
else {
JsObject* obj = YOBJECT(JsObject,params().getParam(YSTRING("prototype")));
return obj ? obj->runNative(stack,oper,context) : JsObject::runNative(stack,oper,context);
}
return true;
}
@ -2165,13 +2184,16 @@ bool JsFunction::runDefined(ObjList& stack, const ExpOperation& oper, GenObject*
JsObject* proto = YOBJECT(JsObject,getField(stack,"prototype",context));
if (proto) {
// found prototype, build object
JsObject* obj = proto->clone();
obj->copyFields(stack,*proto,context);
obj->runConstructor(stack,oper,context);
JsObject* obj = proto->runConstructor(stack,oper,context);
if (!obj)
return false;
ExpEvaluator::pushOne(stack,new ExpWrapper(obj,oper.name()));
}
JsCode* code = YOBJECT(JsCode,m_code);
return code && code->callFunction(stack,oper,context,this,(proto != 0));
XDebug(DebugAll,"JsFunction::runDefined code=%p proto=%p [%p]",code,proto,this);
if (code)
return code->callFunction(stack,oper,context,this,(proto != 0));
return proto || runNative(stack,oper,context);
}

View File

@ -226,6 +226,13 @@ JsObject* JsObject::buildCallContext(Mutex* mtx, ExpOperation* thisObj)
return ctxt;
}
JsObject* JsObject::runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
JsObject* obj = clone();
obj->copyFields(stack,*this,context);
return obj;
}
bool JsObject::runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
XDebug(DebugInfo,"JsObject::runFunction() '%s' in '%s' [%p]",
@ -344,6 +351,7 @@ void JsObject::addConstructor(NamedList& params, const char* name, JsObject* obj
{
JsFunction* ctr = new JsFunction(obj->mutex(),name);
ctr->params().addParam(new NamedPointer("prototype",obj,obj->toString()));
obj->initConstructor(ctr);
params.addParam(new NamedPointer(name,ctr,ctr->toString()));
}

View File

@ -277,9 +277,11 @@ ScriptRun::Status ScriptRun::run()
}
// Execute a function or method call
ScriptRun::Status ScriptRun::call(const String& name, ObjList& args, ExpOperation* thisObj)
ScriptRun::Status ScriptRun::call(const String& name, ObjList& args,
ExpOperation* thisObj, ExpOperation* scopeObj)
{
TelEngine::destruct(thisObj);
TelEngine::destruct(scopeObj);
return Failed;
}

View File

@ -1270,6 +1270,14 @@ public:
* @param results List to fill with expression results
*/
virtual bool evaluate(ScriptRun& runner, ObjList& results) const = 0;
/**
* Create a runner adequate for this block of parsed code
* @param context Script context, must not be NULL
* @return A new script runner, NULL if context is NULL or feature is not supported
*/
virtual ScriptRun* createRunner(ScriptContext* context)
{ return 0; }
};
/**
@ -1410,9 +1418,11 @@ public:
* @param name Name of the function to call
* @param args Values to pass as actual function arguments
* @param thisObj Object to pass as "this" if applicable
* @param scopeObj Optional object to be used for scope resolution inside the call
* @return Final status of the runtime after function call
*/
virtual Status call(const String& name, ObjList& args, ExpOperation* thisObj = 0);
virtual Status call(const String& name, ObjList& args,
ExpOperation* thisObj = 0, ExpOperation* scopeObj = 0);
/**
* Check if a script has a certain function or method
@ -1532,12 +1542,15 @@ private:
ScriptCode* m_code;
};
class JsFunction;
/**
* Javascript Object class, base for all JS objects
* @short Javascript Object
*/
class YSCRIPT_API JsObject : public ScriptContext
{
friend class JsFunction;
YCLASS(JsObject,ScriptContext)
public:
/**
@ -1576,13 +1589,20 @@ public:
{ return clone(toString()); }
/**
* Native object constructor
* Native constructor initialization, called by addConstructor on the prototype
* @param construct Function that has this object as prototype
*/
virtual void initConstructor(JsFunction* construct)
{ }
/**
* Native object constructor, it's run on the prototype
* @param stack Evaluation stack in use
* @param oper Constructor function to evaluate
* @param context Pointer to arbitrary object passed from evaluation methods
* @return New created and populated Javascript object
*/
virtual void runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context)
{ }
virtual JsObject* runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context);
/**
* Try to evaluate a single method

View File

@ -51,6 +51,8 @@ private:
JsParser m_assistCode;
};
INIT_PLUGIN(JsModule);
class JsAssist : public ChanAssist
{
public:
@ -139,33 +141,64 @@ public:
inline JsMessage(Mutex* mtx)
: JsObject("Message",mtx,true), m_message(0), m_owned(false)
{
XDebug(DebugAll,"JsMessage::JsMessage() [%p]",this);
params().addParam(new ExpFunction("constructor"));
XDebug(&__plugin,DebugAll,"JsMessage::JsMessage() [%p]",this);
}
inline JsMessage(Message* message, Mutex* mtx, bool owned)
: JsObject("Message",mtx), m_message(message), m_owned(owned)
{
XDebug(DebugAll,"JsMessage::JsMessage(%p) [%p]",message,this);
XDebug(&__plugin,DebugAll,"JsMessage::JsMessage(%p) [%p]",message,this);
params().addParam(new ExpFunction("enqueue"));
params().addParam(new ExpFunction("dispatch"));
params().addParam(new ExpFunction("broadcast"));
}
virtual ~JsMessage()
{
XDebug(DebugAll,"JsMessage::~JsMessage() [%p]",this);
XDebug(&__plugin,DebugAll,"JsMessage::~JsMessage() [%p]",this);
if (m_owned)
TelEngine::destruct(m_message);
}
virtual void runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context);
virtual JsObject* runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context);
virtual void initConstructor(JsFunction* construct)
{
construct->params().addParam(new ExpFunction("install"));
}
inline void clearMsg()
{ m_message = 0; m_owned = false; }
static void initialize(ScriptContext* context);
protected:
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
ObjList m_handlers;
Message* m_message;
bool m_owned;
};
class JsHandler : public MessageHandler
{
YCLASS(JsHandler,MessageHandler)
public:
inline JsHandler(const char* name, unsigned priority, const ExpFunction& func, GenObject* context)
: MessageHandler(name,priority,__plugin.name()),
m_function(func.name(),1)
{
XDebug(&__plugin,DebugAll,"JsHandler::JsHandler('%s',%u,'%s') [%p]",
name,priority,func.name().c_str(),this);
ScriptRun* runner = YOBJECT(ScriptRun,context);
if (runner) {
m_context = runner->context();
m_code = runner->code();
}
}
virtual ~JsHandler()
{
XDebug(&__plugin,DebugAll,"JsHandler::~JsHandler() '%s' [%p]",c_str(),this);
}
virtual bool received(Message& msg);
private:
ExpFunction m_function;
RefPointer<ScriptContext> m_context;
RefPointer<ScriptCode> m_code;
};
class JsFile : public JsObject
{
YCLASS(JsFile,JsObject)
@ -210,8 +243,6 @@ protected:
static String s_basePath;
INIT_PLUGIN(JsModule);
UNLOAD_PLUGIN(unloadNow)
{
if (unloadNow) {
@ -319,6 +350,7 @@ void JsEngine::initialize(ScriptContext* context)
bool JsMessage::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
XDebug(DebugAll,"JsMessage::runNative '%s'(%ld)",oper.name().c_str(),oper.number());
if (oper.name() == YSTRING("broadcast")) {
if (oper.number() != 0)
return false;
@ -350,21 +382,50 @@ bool JsMessage::runNative(ObjList& stack, const ExpOperation& oper, GenObject* c
}
ExpEvaluator::pushOne(stack,new ExpOperation(ok));
}
else if (oper.name() == YSTRING("install")) {
ObjList args;
if (extractArgs(stack,oper,context,args) < 2)
return false;
ExpFunction* func = YOBJECT(ExpFunction,args[0]);
if (!func)
return false;
ExpOperation* name = static_cast<ExpOperation*>(args[1]);
ExpOperation* prio = static_cast<ExpOperation*>(args[2]);
if (!name)
return false;
unsigned int priority = 100;
if (prio) {
if (prio->isInteger() && (prio->number() >= 0))
priority = prio->number();
else
return false;
}
JsHandler* h = new JsHandler(*name,priority,*func,context);
m_handlers.append(h);
Engine::install(h);
}
else
return JsObject::runNative(stack,oper,context);
return true;
}
void JsMessage::runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context)
JsObject* JsMessage::runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
if (oper.number() != 1)
return;
ExpOperation* op = popValue(stack,context);
if (!op)
return;
Message* m = new Message(*op);
ExpEvaluator::pushOne(stack,new ExpWrapper(new JsMessage(m,mutex(),true)));
TelEngine::destruct(op);
XDebug(DebugAll,"JsMessage::runConstructor '%s'(%ld)",oper.name().c_str(),oper.number());
ObjList args;
switch (extractArgs(stack,oper,context,args)) {
case 1:
case 2:
break;
default:
return 0;
}
ExpOperation* name = static_cast<ExpOperation*>(args[0]);
ExpOperation* broad = static_cast<ExpOperation*>(args[1]);
if (!name)
return 0;
Message* m = new Message(*name,0,broad && broad->valBoolean());
return new JsMessage(m,mutex(),true);
}
void JsMessage::initialize(ScriptContext* context)
@ -375,12 +436,33 @@ void JsMessage::initialize(ScriptContext* context)
Lock mylock(mtx);
NamedList& params = context->params();
if (!params.getParam(YSTRING("Message")))
addObject(params,"Message",new JsMessage(mtx));
addConstructor(params,"Message",new JsMessage(mtx));
}
bool JsHandler::received(Message& msg)
{
DDebug(&__plugin,DebugAll,"JsHandler::received '%s'",c_str());
if (!m_code)
return false;
ScriptRun* runner = m_code->createRunner(m_context);
if (!runner)
return false;
JsMessage* jm = new JsMessage(&msg,runner->context()->mutex(),false);
jm->ref();
ObjList args;
args.append(new ExpWrapper(jm,"message"));
ScriptRun::Status rval = runner->call(m_function.name(),args);
jm->clearMsg();
TelEngine::destruct(jm);
TelEngine::destruct(runner);
return (ScriptRun::Succeeded == rval);
}
bool JsFile::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
XDebug(DebugAll,"JsFile::runNative '%s'(%ld)",oper.name().c_str(),oper.number());
if (oper.name() == YSTRING("exists")) {
if (oper.number() != 1)
return false;
@ -479,6 +561,7 @@ void JsFile::initialize(ScriptContext* context)
bool JsChannel::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
XDebug(DebugAll,"JsChannel::runNative '%s'(%ld)",oper.name().c_str(),oper.number());
if (oper.name() == YSTRING("id")) {
if (oper.number())
return false;
@ -536,7 +619,20 @@ void JsChannel::initialize(ScriptContext* context, JsAssist* assist)
JsAssist::~JsAssist()
{
TelEngine::destruct(m_runner);
if (m_runner) {
ScriptContext* context = m_runner->context();
if (m_runner->callable("onUnload")) {
ScriptRun* runner = m_runner->code()->createRunner(context);
if (runner) {
ObjList args;
runner->call("onUnload",args);
TelEngine::destruct(runner);
}
}
if (context)
context->params().clearParams();
TelEngine::destruct(m_runner);
}
}
bool JsAssist::init()
@ -643,6 +739,8 @@ JsGlobal::~JsGlobal()
TelEngine::destruct(runner);
}
}
if (m_context)
m_context->params().clearParams();
}
bool JsGlobal::fileChanged(const char* fileName) const