Added support for creating scripting extensions in other modules.
git-svn-id: http://voip.null.ro/svn/yate@5902 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
78e9e6d942
commit
b0e7f8dcab
|
@ -30,7 +30,7 @@ SLIBS:= $(YLIB) libyate.so \
|
|||
libyategsm.so.@PACKAGE_VERSION@ libyategsm.so \
|
||||
libyatemgcp.so.@PACKAGE_VERSION@ libyatemgcp.so \
|
||||
libyatejabber.so.@PACKAGE_VERSION@ libyatejabber.so
|
||||
ILIBS:= ygsm
|
||||
ILIBS:= yscript ygsm
|
||||
INCS := yateclass.h yatemime.h yatengine.h yatephone.h yatecbase.h yatexml.h
|
||||
GENS := yateversn.h
|
||||
LIBS :=
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
; allow_link: boolean: Allow linking of Javascript code (jump resolving)
|
||||
;allow_link=yes
|
||||
|
||||
; auto_extensions: boolean: Automatically load scripting extensions in new scripts
|
||||
; This does not prevent script code from explicitly loading extensions
|
||||
;auto_extensions=yes
|
||||
|
||||
|
||||
[scripts]
|
||||
; Add one entry in this section for each script that is to be loaded on Yate startup
|
||||
|
|
|
@ -242,6 +242,8 @@ public:
|
|||
params().addParam(new ExpFunction("clearInterval"));
|
||||
params().addParam(new ExpFunction("setTimeout"));
|
||||
params().addParam(new ExpFunction("clearTimeout"));
|
||||
params().addParam(new ExpFunction("loadLibrary"));
|
||||
params().addParam(new ExpFunction("loadObject"));
|
||||
params().addParam(new ExpFunction("atob"));
|
||||
params().addParam(new ExpFunction("btoa"));
|
||||
params().addParam(new ExpFunction("atoh"));
|
||||
|
@ -706,6 +708,7 @@ static bool s_engineStop = false;
|
|||
static bool s_allowAbort = false;
|
||||
static bool s_allowTrace = false;
|
||||
static bool s_allowLink = true;
|
||||
static bool s_autoExt = true;
|
||||
|
||||
UNLOAD_PLUGIN(unloadNow)
|
||||
{
|
||||
|
@ -717,6 +720,55 @@ UNLOAD_PLUGIN(unloadNow)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Load extensions in a script context
|
||||
static bool contextLoad(ScriptContext* ctx, const char* name, const char* libs = 0, const char* objs = 0)
|
||||
{
|
||||
if (!ctx)
|
||||
return false;
|
||||
bool start = !(libs || objs);
|
||||
Message msg("script.init",0,start);
|
||||
msg.userData(ctx);
|
||||
msg.addParam("module",__plugin.name());
|
||||
msg.addParam("language","javascript");
|
||||
msg.addParam("startup",String::boolText(start));
|
||||
if (name)
|
||||
msg.addParam("instance",name);
|
||||
if (libs)
|
||||
msg.addParam("libraries",libs);
|
||||
if (objs)
|
||||
msg.addParam("objects",objs);
|
||||
return Engine::dispatch(msg);
|
||||
}
|
||||
|
||||
// Load extensions in a script runner context
|
||||
static bool contextLoad(ScriptRun* runner, const char* name, const char* libs = 0, const char* objs = 0)
|
||||
{
|
||||
return runner && contextLoad(runner->context(),name,libs,objs);
|
||||
}
|
||||
|
||||
// Initialize a script context, populate global objects
|
||||
static void contextInit(ScriptRun* runner, const char* name = 0, JsAssist* assist = 0)
|
||||
{
|
||||
if (!runner)
|
||||
return;
|
||||
ScriptContext* ctx = runner->context();
|
||||
if (!ctx)
|
||||
return;
|
||||
JsObject::initialize(ctx);
|
||||
JsEngine::initialize(ctx);
|
||||
if (assist)
|
||||
JsChannel::initialize(ctx,assist);
|
||||
JsMessage::initialize(ctx);
|
||||
JsFile::initialize(ctx);
|
||||
JsConfigFile::initialize(ctx);
|
||||
JsXML::initialize(ctx);
|
||||
JsHasher::initialize(ctx);
|
||||
JsJSON::initialize(ctx);
|
||||
JsDNS::initialize(ctx);
|
||||
if (s_autoExt)
|
||||
contextLoad(ctx,name);
|
||||
}
|
||||
|
||||
// Extract arguments from stack
|
||||
// Maximum allowed number of arguments is given by arguments to extract
|
||||
// Return false if the number of arguments is not the expected one
|
||||
|
@ -1075,6 +1127,26 @@ bool JsEngine::runNative(ObjList& stack, const ExpOperation& oper, GenObject* co
|
|||
bool ret = m_worker->removeEvent((unsigned int)id->valInteger(),oper.name() == YSTRING("clearInterval"));
|
||||
ExpEvaluator::pushOne(stack,new ExpOperation(ret));
|
||||
}
|
||||
else if (oper.name() == YSTRING("loadLibrary") || oper.name() == YSTRING("loadObject")) {
|
||||
bool obj = oper.name() == YSTRING("loadObject");
|
||||
bool ok = false;
|
||||
ObjList args;
|
||||
ScriptRun* runner = YOBJECT(ScriptRun,context);
|
||||
int argc = extractArgs(stack,oper,context,args);
|
||||
if (runner && argc) {
|
||||
ok = true;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
ExpOperation* op = static_cast<ExpOperation*>(args[i]);
|
||||
if (!op || op->isBoolean() || op->isNumber() || YOBJECT(ExpWrapper,op))
|
||||
ok = false;
|
||||
else if (obj)
|
||||
ok = contextLoad(runner,0,0,*op) && ok;
|
||||
else
|
||||
ok = contextLoad(runner,0,*op) && ok;
|
||||
}
|
||||
}
|
||||
ExpEvaluator::pushOne(stack,new ExpOperation(ok));
|
||||
}
|
||||
else if (oper.name() == YSTRING("atob")) {
|
||||
// str = Engine.atob(b64_str)
|
||||
ObjList args;
|
||||
|
@ -3349,19 +3421,10 @@ bool JsAssist::init()
|
|||
{
|
||||
if (!m_runner)
|
||||
return false;
|
||||
ScriptContext* ctx = m_runner->context();
|
||||
JsObject::initialize(ctx);
|
||||
JsEngine::initialize(ctx);
|
||||
JsChannel::initialize(ctx,this);
|
||||
JsMessage::initialize(ctx);
|
||||
JsFile::initialize(ctx);
|
||||
JsConfigFile::initialize(ctx);
|
||||
JsXML::initialize(ctx);
|
||||
JsHasher::initialize(ctx);
|
||||
JsJSON::initialize(ctx);
|
||||
JsDNS::initialize(ctx);
|
||||
contextInit(m_runner,id(),this);
|
||||
if (ScriptRun::Invalid == m_runner->reset(true))
|
||||
return false;
|
||||
ScriptContext* ctx = m_runner->context();
|
||||
ScriptContext* chan = YOBJECT(ScriptContext,ctx->getField(m_runner->stack(),YSTRING("Channel"),m_runner));
|
||||
if (chan) {
|
||||
JsMessage* jsm = YOBJECT(JsMessage,chan->getField(m_runner->stack(),YSTRING("message"),m_runner));
|
||||
|
@ -3685,15 +3748,7 @@ bool JsGlobal::runMain()
|
|||
return false;
|
||||
if (!m_context)
|
||||
m_context = runner->context();
|
||||
JsObject::initialize(runner->context());
|
||||
JsEngine::initialize(runner->context());
|
||||
JsMessage::initialize(runner->context());
|
||||
JsFile::initialize(runner->context());
|
||||
JsConfigFile::initialize(runner->context());
|
||||
JsXML::initialize(runner->context());
|
||||
JsHasher::initialize(runner->context());
|
||||
JsJSON::initialize(runner->context());
|
||||
JsDNS::initialize(runner->context());
|
||||
contextInit(runner,name());
|
||||
ScriptRun::Status st = runner->run();
|
||||
TelEngine::destruct(runner);
|
||||
return (ScriptRun::Succeeded == st);
|
||||
|
@ -3790,17 +3845,8 @@ bool JsModule::evalContext(String& retVal, const String& cmd, ScriptContext* con
|
|||
return true;
|
||||
}
|
||||
ScriptRun* runner = parser.createRunner(context,"[command line]");
|
||||
if (!context) {
|
||||
JsObject::initialize(runner->context());
|
||||
JsEngine::initialize(runner->context());
|
||||
JsMessage::initialize(runner->context());
|
||||
JsFile::initialize(runner->context());
|
||||
JsConfigFile::initialize(runner->context());
|
||||
JsXML::initialize(runner->context());
|
||||
JsHasher::initialize(runner->context());
|
||||
JsJSON::initialize(runner->context());
|
||||
JsDNS::initialize(runner->context());
|
||||
}
|
||||
if (!context)
|
||||
contextInit(runner);
|
||||
ScriptRun::Status st = runner->run();
|
||||
if (st == ScriptRun::Succeeded) {
|
||||
while (ExpOperation* op = ExpEvaluator::popOne(runner->stack())) {
|
||||
|
@ -3976,6 +4022,7 @@ void JsModule::initialize()
|
|||
if (tmp && !tmp.endsWith(Engine::pathSeparator()))
|
||||
tmp += Engine::pathSeparator();
|
||||
s_libsPath = tmp;
|
||||
s_autoExt = cfg.getBoolValue("general","auto_extensions",true);
|
||||
s_allowAbort = cfg.getBoolValue("general","allow_abort");
|
||||
bool changed = false;
|
||||
if (cfg.getBoolValue("general","allow_trace") != s_allowTrace) {
|
||||
|
|
|
@ -15,7 +15,7 @@ MODFLAGS:= @MODULE_LDFLAGS@
|
|||
MODSTRIP:= @MODULE_SYMBOLS@
|
||||
|
||||
MKDEPS := ../../config.status
|
||||
PROGS = randcall.yate msgdelay.yate
|
||||
PROGS = randcall.yate msgdelay.yate jsext.yate
|
||||
LIBS =
|
||||
OBJS =
|
||||
|
||||
|
@ -63,3 +63,6 @@ lib%.so: %.o
|
|||
|
||||
%.yate: @srcdir@/%.cpp $(MKDEPS) $(INCFILES)
|
||||
$(MODCOMP) -o $@ $(LOCALFLAGS) $< $(LOCALLIBS) $(YATELIBS)
|
||||
|
||||
jsext.yate: LOCALFLAGS = -I../../libs/yscript
|
||||
jsext.yate: LOCALLIBS = -lyatescript
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
/**
|
||||
* jsext.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Javascript extensions test
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribution.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#include <yatengine.h>
|
||||
#include <yatescript.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
class JsExtObj : public JsObject
|
||||
{
|
||||
YCLASS(JsExtObj,JsObject)
|
||||
public:
|
||||
inline JsExtObj(Mutex* mtx)
|
||||
: JsObject("ExtObj",mtx,true)
|
||||
{
|
||||
Debug(DebugAll,"JsExtObj::JsExtObj(%p) [%p]",mtx,this);
|
||||
}
|
||||
inline JsExtObj(Mutex* mtx, const char* val)
|
||||
: JsObject("ExtObj",mtx,true),
|
||||
m_val(val)
|
||||
{
|
||||
Debug(DebugAll,"JsExtObj::JsExtObj(%p,'%s') [%p]",mtx,val,this);
|
||||
params().addParam(new ExpFunction("test"));
|
||||
}
|
||||
virtual ~JsExtObj()
|
||||
{
|
||||
Debug(DebugAll,"JsExtObj::~JsExtObj() [%p]",this);
|
||||
}
|
||||
virtual JsObject* runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context);
|
||||
static void initialize(ScriptContext* context);
|
||||
protected:
|
||||
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
|
||||
private:
|
||||
String m_val;
|
||||
};
|
||||
|
||||
|
||||
class JsExtHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
JsExtHandler()
|
||||
: MessageHandler("script.init",90,"jsext")
|
||||
{ }
|
||||
virtual bool received(Message& msg);
|
||||
};
|
||||
|
||||
class JsExtPlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
JsExtPlugin();
|
||||
virtual void initialize();
|
||||
private:
|
||||
JsExtHandler* m_handler;
|
||||
};
|
||||
|
||||
|
||||
JsObject* JsExtObj::runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
||||
{
|
||||
Debug(DebugAll,"JsExtObj::runConstructor '%s'("FMT64") [%p]",oper.name().c_str(),oper.number(),this);
|
||||
const char* val = 0;
|
||||
ObjList args;
|
||||
switch (extractArgs(stack,oper,context,args)) {
|
||||
case 1:
|
||||
val = static_cast<ExpOperation*>(args[0])->c_str();
|
||||
// fall through
|
||||
case 0:
|
||||
return new JsExtObj(mutex(),val);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void JsExtObj::initialize(ScriptContext* context)
|
||||
{
|
||||
if (!context)
|
||||
return;
|
||||
Mutex* mtx = context->mutex();
|
||||
Lock mylock(mtx);
|
||||
NamedList& params = context->params();
|
||||
if (!params.getParam(YSTRING("ExtObj")))
|
||||
addConstructor(params,"ExtObj",new JsExtObj(mtx));
|
||||
else
|
||||
Debug(DebugInfo,"An ExtObj already exists, nothing to do");
|
||||
}
|
||||
|
||||
bool JsExtObj::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
||||
{
|
||||
if (oper.name() == YSTRING("test")) {
|
||||
ObjList args;
|
||||
int argc = extractArgs(stack,oper,context,args);
|
||||
String tmp;
|
||||
tmp << "ExtObj: '" << m_val << "' argc=" << argc;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
ExpOperation* op = static_cast<ExpOperation*>(args[i]);
|
||||
tmp << " '" << *op << "'";
|
||||
}
|
||||
ExpEvaluator::pushOne(stack,new ExpOperation(tmp));
|
||||
}
|
||||
else
|
||||
return JsObject::runNative(stack,oper,context);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static const Regexp s_libs("\\(^\\|,\\)jsext\\($\\|,\\)");
|
||||
static const Regexp s_objs("\\(^\\|,\\)ExtObj\\($\\|,\\)");
|
||||
|
||||
bool JsExtHandler::received(Message& msg)
|
||||
{
|
||||
ScriptContext* ctx = YOBJECT(ScriptContext,msg.userData());
|
||||
const String& lang = msg[YSTRING("language")];
|
||||
Debug(DebugInfo,"Received script.init, language: %s, context: %p",lang.c_str(),ctx);
|
||||
if ((lang && (lang != YSTRING("javascript"))) || !ctx)
|
||||
return false;
|
||||
bool ok = msg.getBoolValue(YSTRING("startup"))
|
||||
|| s_libs.matches(msg.getValue(YSTRING("libraries")))
|
||||
|| s_objs.matches(msg.getValue(YSTRING("objects")));
|
||||
if (ok)
|
||||
JsExtObj::initialize(ctx);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
JsExtPlugin::JsExtPlugin()
|
||||
: Plugin("jsext",true), m_handler(0)
|
||||
{
|
||||
Output("Hello, I am module JsExtPlugin");
|
||||
}
|
||||
|
||||
void JsExtPlugin::initialize()
|
||||
{
|
||||
Output("Initializing module JsExtPlugin");
|
||||
if (!m_handler)
|
||||
Engine::install((m_handler = new JsExtHandler));
|
||||
}
|
||||
|
||||
INIT_PLUGIN(JsExtPlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
Loading…
Reference in New Issue