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:
parent
f5e76ddb6b
commit
c446eb8d2e
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue