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:
paulc 2014-08-06 08:08:06 +00:00
parent 78e9e6d942
commit b0e7f8dcab
5 changed files with 244 additions and 33 deletions

View File

@ -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 :=

View File

@ -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

View File

@ -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) {

View File

@ -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

157
modules/test/jsext.cpp Normal file
View File

@ -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: */